別人的相關測試數據: http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking html
import "InnerMessage.proto"; package demo; option java_package = "com.agapple.protobuf.data"; option java_outer_classname = "MessageProtos"; option optimize_for = SPEED ; //CODE_SIZE,LITE_RUNTIME option java_generic_services = false; message Message { required string strObj = 1 [default="hello"]; optional int32 int32Obj = 2; optional int64 int64Obj = 3; optional uint32 uint32Obj = 4; optional uint64 uint64Obj = 5; optional sint32 sint32Obj = 6; optional sint64 sint64Obj = 7; optional fixed32 fixed32Obj = 8; optional fixed64 fixed64Obj = 9; optional sfixed32 sfixed32Obj = 10; optional sfixed64 sfixed64Obj = 11; optional bool boolObj = 12; optional bytes bytesObj = 13; optional float folatObj = 14 [deprecated=true]; repeated double doubleObj = 15 [packed=true]; // optional InnerMessage innerMessage = 16; } Innermessage.proto代碼 import "EnumType.proto"; package demo; option java_package = "com.agapple.protobuf.data"; option java_outer_classname = "InnerMessageProtos"; message InnerMessage { optional string name = 1 [default = "name"]; optional int32 id = 2; optional EnumType type = 3 [default = UNIVERSAL]; }
package demo; option java_package = "com.agapple.protobuf.data"; option java_outer_classname = "EnumTypeProtos"; enum EnumType { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; }
基本上把protobuf支持的類型都囊括了,包括嵌套類型,枚舉類型,以及各類int,uint,bool,bytes。 java
依賴關係是Message.proto依賴了InnerMessage對象,而InnerMessage對象裏包含了一個自定義枚舉類型EnumType。 json
關於類型的使用可參見:
http://code.google.com/intl/zh/apis/protocolbuffers/docs/reference/java-generated.html
http://code.google.com/intl/zh/apis/protocolbuffers/docs/proto.html api
cd /home/ljh/work/code/src/main/java /home/ljh/work/protobuf/bin/protoc --proto_path=com/agapple/protobuf/ --java_out=. com/agapple/protobuf/EnumType.proto /home/ljh/work/protobuf/bin/protoc --proto_path=com/agapple/protobuf/ --java_out=. com/agapple/protobuf/InnerMessage.proto /home/ljh/work/protobuf/bin/protoc --proto_path=com/agapple/protobuf/ --java_out=. com/agapple/protobuf/Message.proto
經過protobuf自帶的protoc進行編譯,指定了protobuf文件的路徑, 具體的文檔: http://code.google.com/intl/zh/apis/protocolbuffers/docs/proto.html#generating app
運行腳本後就會生成對應的3個javabean文件: MessageProtos , InnerMessageProtos , EnumTypeProtos。 ide
private static MessageProtos.Message getProtobufBean() { com.agapple.protobuf.data.MessageProtos.Message.Builder messageBuilder = MessageProtos.Message.newBuilder(); messageBuilder.setStrObj("message"); messageBuilder.setFolatObj(1f); messageBuilder.addDoubleObj(1d); messageBuilder.addDoubleObj(2d); messageBuilder.setBoolObj(true); messageBuilder.setBytesObj(ByteString.copyFrom(new byte[] { 1, 2, 3 })); messageBuilder.setInt32Obj(32); messageBuilder.setInt64Obj(64l); messageBuilder.setSint32Obj(232); messageBuilder.setSint64Obj(264); messageBuilder.setFixed32Obj(532); messageBuilder.setFixed64Obj(564); messageBuilder.setSfixed32Obj(2532); messageBuilder.setSfixed64Obj(2564); messageBuilder.setUint32Obj(632); messageBuilder.setUint64Obj(664); com.agapple.protobuf.data.InnerMessageProtos.InnerMessage.Builder innerMessageBuilder = InnerMessageProtos.InnerMessage.newBuilder(); innerMessageBuilder.setId(1); innerMessageBuilder.setName("inner"); innerMessageBuilder.setType(EnumType.PRODUCTS); messageBuilder.setInnerMessage(innerMessageBuilder); return messageBuilder.build(); }
屬性和proto的bean保持一致。 oop
Java代碼 private static MessagePojo getPojoBean() { MessagePojo bean = new MessagePojo(); bean.setStrObj("message"); bean.setFolatObj(1f); List<Double> doubleObj = new ArrayList<Double>(); doubleObj.add(1d); doubleObj.add(2d); bean.setDoubleObj(doubleObj); bean.setBoolObj(true); bean.setBytesObj(new byte[] { 1, 2, 3 }); bean.setInt32Obj(32); bean.setInt64Obj(64l); bean.setSint32Obj(232); bean.setSint64Obj(264); bean.setFixed32Obj(532); bean.setFixed64Obj(564); bean.setSfixed32Obj(2532); bean.setSfixed64Obj(2564); bean.setUint32Obj(632); bean.setUint64Obj(664); InnerMessagePojo innerMessagePojo = new InnerMessagePojo(); innerMessagePojo.setId(1); innerMessagePojo.setName("inner"); innerMessagePojo.setType(EnumTypePojo.PRODUCTS); bean.setInnerMessage(innerMessagePojo); return bean; }
interface TestCallback { String getName(); byte[] writeObject(Object source); Object readObject(byte[] bytes); }
Java代碼 private static void testTemplate(TestCallback callback, Object source, int count) { int warmup = 10; // 先進行預熱,加載一些類,避免影響測試 for (int i = 0; i < warmup; i++) { byte[] bytes = callback.writeObject(source); callback.readObject(bytes); } restoreJvm(); // 進行GC回收 // 進行測試 long start = System.nanoTime(); long size = 0l; for (int i = 0; i < count; i++) { byte[] bytes = callback.writeObject(source); size = size + bytes.length; callback.readObject(bytes); // System.out.println(callback.readObject(bytes)); bytes = null; } long nscost = (System.nanoTime() - start); System.out.println(callback.getName() + " total cost=" + integerFormat.format(nscost) + "ns , each cost=" + integerFormat.format(nscost / count) + "ns , and byte sizes = " + size / count); restoreJvm();// 進行GC回收 }
在測試模板方法中,使用了warmup預熱的概念,就是預先執行目標方法必定的次數,用於避免由於jit的優化影響系統測試。 同時包含了每次測試模板調用完成後system.gc保證下一輪的功能測試 性能
相應的restoreJvm方法: 測試
Java代碼 private static void restoreJvm() { int maxRestoreJvmLoops = 10; long memUsedPrev = memoryUsed(); for (int i = 0; i < maxRestoreJvmLoops; i++) { System.runFinalization(); System.gc(); long memUsedNow = memoryUsed(); // break early if have no more finalization and get constant mem used if ((ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount() == 0) && (memUsedNow >= memUsedPrev)) { break; } else { memUsedPrev = memUsedNow; } } } private static long memoryUsed() { Runtime rt = Runtime.getRuntime(); return rt.totalMemory() - rt.freeMemory(); }
Java代碼 final int testCount = 1000 * 500; final MessageProtos.Message protoObj = getProtobufBean(); final MessagePojo pojoOBj = getPojoBean(); // Serializable測試 testTemplate(new TestCallback() { public String getName() { return "Serializable Test"; } @Override public byte[] writeObject(Object source) { try { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream output = new ObjectOutputStream(bout); output.writeObject(source); return bout.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override public Object readObject(byte[] bytes) { try { ByteArrayInputStream bin = new ByteArrayInputStream(bytes); ObjectInputStream input = new ObjectInputStream(bin); return input.readObject(); } catch (Exception e) { e.printStackTrace(); } return null; } }, pojoOBj, testCount); // protobuf測試 testTemplate(new TestCallback() { public String getName() { return "protobuf test"; } @Override public byte[] writeObject(Object source) { if (source instanceof MessageProtos.Message) { MessageProtos.Message message = (MessageProtos.Message) source; return message.toByteArray(); } return null; } @Override public Object readObject(byte[] bytes) { try { return MessageProtos.Message.parseFrom(bytes); } catch (InvalidProtocolBufferException e) { e.printStackTrace(); } return null; } }, protoObj, testCount); // json測試 final ObjectMapper objectMapper = new ObjectMapper(); final JavaType javaType = TypeFactory.type(pojoOBj.getClass()); // JSON configuration not to serialize null field objectMapper.getSerializationConfig().setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL); // JSON configuration not to throw exception on empty bean class objectMapper.getSerializationConfig().disable(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS); // JSON configuration for compatibility objectMapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); testTemplate(new TestCallback() { public String getName() { return "Jackson Test"; } @Override public byte[] writeObject(Object source) { try { return objectMapper.writeValueAsBytes(source); } catch (JsonGenerationException e) { e.printStackTrace(); } catch (JsonMappingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override public Object readObject(byte[] bytes) { try { return objectMapper.readValue(bytes, 0, bytes.length, javaType); } catch (JsonParseException e) { e.printStackTrace(); } catch (JsonMappingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } }, pojoOBj, testCount); // Xstream測試 final XStream xstream = new XStream(); testTemplate(new TestCallback() { public String getName() { return "Xstream test"; } @Override public byte[] writeObject(Object source) { return xstream.toXML(source).getBytes(); } @Override public Object readObject(byte[] bytes) { return xstream.fromXML(new ByteArrayInputStream(bytes)); } }, pojoOBj, testCount);
hessian 3.1.5版本基於二進制序列化的測試 優化
Xml代碼 <dependency> <groupId>com.caucho</groupId> <artifactId>hessian</artifactId> <version>3.1.5</version> </dependency>
測試了3種狀況:
測試代碼:
Java代碼 // hessian 2 with no deflat testTemplate(new TestCallback() { public String getName() { return "hessian 2 with no deflat"; } @Override public byte[] writeObject(Object source) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); Hessian2Output out = new Hessian2Output(bos); // out.startMessage(); out.writeObject(source); // out.completeMessage(); out.flush(); return bos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override public Object readObject(byte[] bytes) { try { ByteArrayInputStream bin = new ByteArrayInputStream(bytes); Hessian2Input in = new Hessian2Input(bin); // in.startMessage(); Object obj = in.readObject(); // in.completeMessage(); return obj; } catch (IOException e) { e.printStackTrace(); } return null; } }, pojoOBj, testCount); // hessian 2 with deflat final Deflation envelope = new Deflation(); testTemplate(new TestCallback() { public String getName() { return "hessian 2 with deflat"; } @Override public byte[] writeObject(Object source) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); Hessian2Output out = new Hessian2Output(bos); out = envelope.wrap(out); out.writeObject(source); out.flush(); out.close(); // 記得關閉 return bos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return null; } @Override public Object readObject(byte[] bytes) { try { ByteArrayInputStream bin = new ByteArrayInputStream(bytes); Hessian2Input in = new Hessian2Input(bin); in = envelope.unwrap(in); Object obj = in.readObject(); in.close(); return obj; } catch (IOException e) { e.printStackTrace(); } return null; } }, pojoOBj, testCount); // hessian 1 with no deflat testTemplate(new TestCallback() { public String getName() { return "hessian 1 with no deflat"; } @Override public byte[] writeObject(Object source) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); HessianOutput out = new HessianOutput(bos); out.writeObject(source); out.flush(); return bos.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return null; } @Override public Object readObject(byte[] bytes) { try { ByteArrayInputStream bin = new ByteArrayInputStream(bytes); HessianInput in = new HessianInput(bin); Object obj = in.readObject(); in.close(); return obj; } catch (IOException e) { e.printStackTrace(); } return null; } }, pojoOBj, testCount);
protobuf | jackson | xstream | Serializable | hessian2 | hessian2壓縮 | hessian1 | |
序列化(單位ns) | 1154 | 5421 | 92406 | 10189 | 26794 | 100766 | 29027 |
反序列化(單位ns) | 1334 | 8743 | 117329 | 64027 | 37871 | 188432 | 37596 |
bytes | 97 | 311 | 664 | 824 | 374 | 283 | 495 |
之後在內部系統,數據cache存儲上能夠考慮使用protobuf。跟外部系統交互上能夠考慮使用json。
有興趣的同窗,能夠研究一下google protobuf的marshall的方式:http://code.google.com/intl/zh/apis/protocolbuffers/docs/encoding.html