有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

安卓在Java中通过socket高效地发送大int[]

我正在开发一个Java应用程序,在这个应用程序中,我需要通过socket连接尽快将500000个整数的数组从一部Android手机发送到另一部Android手机。主要的瓶颈似乎是转换整数,以便socket可以接受它们,无论我使用ObjectOutputStreams、ByteBuffers还是低级掩码和移位转换。通过socket将int[]从一个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;
}

使用字节缓冲区和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;
}

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();
}

最后,我用来发送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();
}

我在socket的两侧编写代码,这样我就可以尝试任何类型的endianness、压缩、序列化等。必须有一种方法在Java中更有效地进行这种转换。请帮忙


共 (3) 个答案

  1. # 1 楼答案

    Java从来没有像在C中那样,能够有效地从int[]byte[]重新解释内存区域。它甚至没有这样的内存地址模型

    您要么需要使用本地方式发送数据,要么可以尝试找到一些微优化。但我怀疑你会收获很多

    例如,这可能比你的版本快一点(如果可以的话)

    public static byte[] intToByte(int[] input)
    {
        byte[] output = new byte[input.length*4];
    
        for(int i = 0; i < input.length; i++) {
            int position = i << 2;
            output[position | 0] = (byte)((input[i] >>  0) & 0xFF);
            output[position | 1] = (byte)((input[i] >>  8) & 0xFF);
            output[position | 2] = (byte)((input[i] >> 16) & 0xFF);
            output[position | 3] = (byte)((input[i] >> 24) & 0xFF);
        }
        return output;
    }
    
  2. # 2 楼答案

    正如我在评论中指出的,我认为你正在挑战处理器的极限。因为这可能对其他人有帮助,所以我将详细介绍。下面是将整数转换为字节的循环:

        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);
        }
    

    这个循环执行了500000次。600Mhz处理器每秒可以处理大约600000000次操作。因此,循环的每次迭代将为每次操作消耗大约1/1200秒的时间

    同样,使用非常粗略的数字(我不知道ARM指令集,所以每个操作可能或多或少),这里是一个操作计数:

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

    好的,在粗略的数字中,这个循环最多需要55/1200秒,或者0.04秒。然而,你并不是在处理最好的情况。首先,对于这么大的阵列,你不会从处理器缓存中受益,所以你会在每个阵列存储和加载中引入等待状态

    另外,我描述的基本操作可能会也可能不会直接转化为机器代码。如果没有(我怀疑没有),这个循环的成本将比我描述的要高

    最后,如果你真的很不走运,JVM没有JIT你的代码,所以对于循环的部分(或全部)来说,它是在解释字节码,而不是执行本机指令。我对Dalvik了解不够,无法对此发表评论

  3. # 3 楼答案

    如果你不反对使用图书馆,你可能想从谷歌上查一查Protocol Buffers。它是为更复杂的对象序列化而构建的,但我敢打赌,他们努力研究如何在Java中快速序列化整数数组

    编辑:我查看了Protobuf源代码,它使用了类似于低级掩码和移位的东西