在Java中有效地通过套接字发送大型int []

Jer*_*ers 14 java sockets performance android casting

我正在开发一个Java应用程序,我需要尽快通过套接字连接从一部Android手机向另一部Android手机发送500,000个整数数组.主要的瓶颈似乎是转换整数,因此套接字可以使用它们,无论我使用ObjectOutputStreams,ByteBuffers还是低级掩码和移位转换.通过套接字将int []从一个Java应用程序发送到另一个Java应用程序的最快方法是什么?

这是迄今为止我尝试过的所有内容的代码,LG Optimus V上的基准测试我正在测试(600 MHz ARM处理器,Android 2.2).

低级掩码和移位:0.2秒

public static byte[] intToByte(int[] input)
{
    byte[] output = new byte[input.length*4];

    for(int i = 0; i < input.length; i++) {
        output[i*4] = (byte)(input[i] & 0xFF);
        output[i*4 + 1] = (byte)((input[i] & 0xFF00) >>> 8);
        output[i*4 + 2] = (byte)((input[i] & 0xFF0000) >>> 16);
        output[i*4 + 3] = (byte)((input[i] & 0xFF000000) >>> 24);
    }

    return output;
}
Run Code Online (Sandbox Code Playgroud)

使用ByteBuffer和IntBuffer:0.75秒

public static byte[] intToByte(int[] input)
{
    ByteBuffer byteBuffer = ByteBuffer.allocate(input.length * 4);        
    IntBuffer intBuffer = byteBuffer.asIntBuffer();
    intBuffer.put(input);

    byte[] array = byteBuffer.array();

    return array;
}
Run Code Online (Sandbox Code Playgroud)

ObjectOutputStream:3.1秒(我尝试使用DataOutPutStream和writeInt()而不是writeObject()来改变它,但它并没有太大区别)

public static void sendSerialDataTCP(String address, int[] array) throws IOException
{
    Socket senderSocket = new Socket(address, 4446);

    OutputStream os = senderSocket.getOutputStream();
    BufferedOutputStream  bos = new BufferedOutputStream (os);
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(array);

    oos.flush();
    bos.flush();
    os.flush();
    oos.close();
    os.close();
    bos.close();

    senderSocket.close();
}
Run Code Online (Sandbox Code Playgroud)

最后,我用来发送byte []的代码比intToByte()函数增加了0.2秒

public static void sendDataTCP(String address, byte[] data) throws IOException
{
    Socket senderSocket = new Socket(address, 4446);

    OutputStream os = senderSocket.getOutputStream();
    os.write(data, 0, data.length);
    os.flush();

    senderSocket.close();
}
Run Code Online (Sandbox Code Playgroud)

我在套接字的两边编写代码,所以我可以尝试任何类型的字节序,压缩,序列化等.必须有一种方法在Java中更有效地进行这种转换.请帮忙!

par*_*fal 5

正如我在评论中指出的那样,我认为你正在抨击处理器的极限.由于这可能对其他人有所帮助,我会将其分解.这是将整数转换为字节的循环:

    for(int i = 0; i < input.length; i++) {
        output[i*4] = (byte)(input[i] & 0xFF);
        output[i*4 + 1] = (byte)((input[i] & 0xFF00) >>> 8);
        output[i*4 + 2] = (byte)((input[i] & 0xFF0000) >>> 16);
        output[i*4 + 3] = (byte)((input[i] & 0xFF000000) >>> 24);
    }
Run Code Online (Sandbox Code Playgroud)

该循环执行500,000次.600Mhz处理器每秒可处理大约600,000,000次操作.因此,对于每个操作,循环的每次迭代将消耗大约1/1200秒.

再次,使用非常粗略的数字(我不知道ARM指令集,因此每个操作可能有更多或更少),这是一个操作计数:

  • 测试/分支:5(检索计数器,检索数组长度,比较,分支,增量计数器)
  • 掩码和移位:10 x 4(检索计数器,检索输入数组基础,添加,检索掩码,移位,乘法计数器,添加偏移量,添加到输出基数,存储)

好的,所以在粗略的数字中,这个循环最多需要55/1200秒,或0.04秒.但是,您没有处理最佳案例场景.首先,对于这么大的数组,您不会从处理器缓存中受益,因此您将在每个数组存储和加载中引入等待状态.

另外,我描述的基本操作可能会或可能不会直接转换为机器代码.如果不是(我怀疑没有),那么循环的成本将超过我所描述的.

最后,如果你真的不走运,JVM还没有JIT编写你的代码,所以对于循环的某些部分(或全部)它解释字节码而不是执行本机指令.我不太了解达尔维克对此发表评论.

  • 我很想说"因为没有魔法",但它确实是一系列的实现选择,其中之一就是键入内存.是的,如果您使用的是C,您可以创建一个缓冲区,使用`int*`写入该缓冲区,而不必担心将整数转换为字节.如果你有一个操作系统可以从进程内存到设备内存进行DMA传输,你可以获得另一个提升.Java的目标是让您远离硬件.如果你需要接近硬件,那么你需要考虑一种可以让你到达那里的语言. (4认同)