在Windows上快速使用Google协议缓冲区,在Ubuntu上速度很慢

时间:2017-11-21 01:32:48

标签: java ubuntu protocol-buffers

我创建了一个非常简单的.proto文件:

syntax = "proto2";


package Metadata; 


option java_package = "metadata";
option java_outer_classname = "Metadata";
option optimize_for=SPEED;
message PutMessage {
 optional string key =1;
 optional int64 dt = 2; 
}

我创建了一个简单的客户端 - 服务器程序,服务器回应客户端消息。所有消息都是PutMessage:

服务器:

public class Server {
 public static void main(String args[]) throws IOException, InterruptedException {
 ServerSocket ss = new ServerSocket(8080);
 System.out.println("Listening for client");
 Socket socket = ss.accept();
 System.out.println("Server Results: ");
 while (true) {
 Long parseStart = System.nanoTime();
 PutMessage sm = PutMessage.parseDelimitedFrom(socket.getInputStream());
 Long parseEnd = System.nanoTime();

 Long writeStart = System.nanoTime();
 sm.writeDelimitedTo(socket.getOutputStream());
 Long writeEnd = System.nanoTime();

 System.out.println("Parse time: " + (parseEnd - parseStart));
 System.out.println("Write time: " + (writeEnd - writeStart));

 }


 }
}

客户端:

public class Client {
 public static void main (String args[]) throws IOException, InterruptedException{
 Socket socket = new Socket("127.0.0.1", 8080);
 int A = new Integer(args[0]);
 PutMessage cm = PutMessage.newBuilder().setDt(3434).setKey("sdfwsdf").build();
 System.out.println("Client Results7: ");
 for (int i=0;i < A; i++){
 Long writeStart = System.nanoTime();
 cm.writeDelimitedTo(socket.getOutputStream());
 Long writeEnd = System.nanoTime();

 Long parseStart = System.nanoTime();
 cm.parseDelimitedFrom(socket.getInputStream());
 Long parseEnd = System.nanoTime();

 System.out.println("Write time: " + (writeEnd - writeStart));
 System.out.println("Parse time: " + (parseEnd - parseStart));
 }
 }
}

当我在Windows上运行客户端和服务器时,速度非常快。但是当我在Ubuntu上运行时,服务器需要很长时间(70(ms))来解析客户端消息:当我将3传递给客户端时,这些是一些结果:

全部以纳秒为单位

视窗:

Client Results: 
Write time: 54401508
Parse time: 95818962
Write time: 279563
Parse time: 201593
Write time: 200568
Parse time: 138500



Server Results: 
Parse time: 207099065
Write time: 42572640
Parse time: 20808241
Write time: 156966
Parse time: 209801
Write time: 124649

Ubuntu的:

Client Results:
Write time: 31205019
Parse time: 86399222
Write time: 101132
Parse time: 40619478
Write time: 214496
Parse time: 79164985


Server Results:
Parse time: 183947743
Write time: 25819286
Parse time: 28680184
Write time: 292955
Parse time: 79299932
Write time: 298012

我发现,如果我从put消息中删除setDt,当它只是setKey时,它在Ubuntu上也很快。但我需要dt。我不知道为什么它在Windows上速度快,在Ubuntu上速度慢。我想也许我的机器不同,但没有setDt它在Ubuntu上很快,所以问题不在于硬件。

我也试过proto3,结果相同。任何帮助是极大的赞赏。

更新:两个操作系统都是64位。 Java版本是:

Ubuntu:

java version "1.8.0_66" 
Java(TM) SE Runtime Environment (build 1.8.0_66-b17) 
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode) 

Windows:

java version "1.8.0_151" 
Java(TM) SE Runtime Environment (build 1.8.0_151-b12) 
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

由于

更新: 我在两个AWS EC2实例上重新进行了实验。一个Windows和另一个亚马逊Linux。这次我没有使用Ubuntu,但结果相同。我再次在Windows上获得了更快的结果。这真奇怪!

2 个答案:

答案 0 :(得分:1)

我发现,如果我们写字节,而不是调用writDelimetedTo和parseDelimitedFrom,则没有延迟:

服务器:

public class CodedServer {
    public static void main(String args[]) throws IOException, InterruptedException {
        ServerSocket ss = new ServerSocket(8080);
        System.out.println("Listening for client8");
        Socket socket = ss.accept();
        System.out.println("Server Results: ");
        CodedOutputStream out = CodedOutputStream.newInstance(socket.getOutputStream());
        CodedInputStream in = CodedInputStream.newInstance(socket.getInputStream());
        while (true) {

            Long parseStart = System.nanoTime();
            int size = in.readInt32();
            byte[] result = in.readRawBytes(size);
            PutMessage cm = PutMessage.parseFrom(result);
            Long parseEnd = System.nanoTime();

            Long writeStart = System.nanoTime();
            out.writeInt32NoTag(cm.getSerializedSize());
            cm.writeTo(out);
            out.flush();
            Long writeEnd = System.nanoTime();

            System.out.println("Parse time: " + (parseEnd - parseStart));
            System.out.println("Write time: " + (writeEnd - writeStart));

        }

    }
}

客户:

public class CodedClient {
    public static void main (String args[]) throws IOException, InterruptedException{
        Socket socket = new Socket(args[0], 8080);
        int A = new Integer(args[1]);
        PutMessage cm = PutMessage.newBuilder().setDt("sdfsdfsdf").setKey("Test Stringdfgdsfgsdfgsdfgsdfgsdfgsdfgsdfgsdfgsdfgsdfgsdfgsdfg").build();

        CodedOutputStream out = CodedOutputStream.newInstance(socket.getOutputStream());
        CodedInputStream in = CodedInputStream.newInstance(socket.getInputStream());

        System.out.println("Client Results9: ");
        for (int i=0;i < A; i++){
            Long writeStart = System.nanoTime();
            out.writeInt32NoTag(cm.getSerializedSize());
            cm.writeTo(out);
            out.flush();
            Long writeEnd = System.nanoTime();


            Long parseStart = System.nanoTime();
            int size = in.readInt32();
            byte[] result = in.readRawBytes(size);
            cm = PutMessage.parseFrom(result);
            Long parseEnd = System.nanoTime();

            System.out.println("Write time: " + (writeEnd - writeStart));
            System.out.println("Parse time: " + (parseEnd - parseStart));
        }
        System.out.println(cm.toString());
    }
}

我很高兴找到解决方案,但是我觉得为什么Google协议缓冲区实现两个标准功能会导致如此显着的延迟?

答案 1 :(得分:0)

您正在测量IO时间,而不是解析时间。如果要测量解析时间,请从字节数组或ByteArrayInputStream中解析。

在这种特殊情况下,Windows和Ubuntu之间的区别可能源于在实现更多数据之前等待一段时间的IO实现(为了减少数量开销)。我尝试的事情是在服务器上关闭或刷新流 - 或者只是在那里发送一些额外的数据。