14

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

这是迄今为止我尝试过的所有代码的代码,以及我正在测试的 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;
}

使用 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;
}

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

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

4

4 回答 4

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

此循环执行 500,000 次。您的 600Mhz 处理器每秒可以处理大约 600,000,000 次操作。因此,循环的每次迭代每次操作将消耗大约 1/1200 秒。

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

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

OK, so in rough numbers, this loop takes at best 55/1200 of a second, or 0.04 seconds. However, you're not dealing with best case scenario. For one thing, with an array this large you're not going to benefit from a processor cache, so you'll introduce wait states into every array store and load.

Plus, the basic operations that I described may or may not translate directly into machine code. If not (and I suspect not), the loop will cost more than I've described.

Finally, if you're really unlucky, the JVM hasn't JIT-ed your code, so for some portion (or all) of the loop it's interpreting bytecode rather than executing native instructions. I don't know enough about Dalvik to comment on that.

于 2012-09-07T15:09:14.873 回答
1

Java 是 IMO 从来没有打算像你在 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;
}
于 2012-09-07T14:42:36.353 回答
1

如果您不反对使用库,您可能需要查看 Google 的Protocol Buffers。它是为更复杂的对象序列化而构建的,但我敢打赌,他们努力弄清楚如何在 Java 中快速序列化整数数组。

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

于 2012-09-07T14:55:59.040 回答
1

我会这样做:

Socket senderSocket = new Socket(address, 4446);

OutputStream os = senderSocket.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);
DataOutputStream dos = new DataOutputStream(bos);

dos.writeInt(array.length);
for(int i : array) dos.writeInt(i);
dos.close();

另一方面,读起来像:

Socket recieverSocket = ...;
InputStream is = recieverSocket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
DataInputStream dis = new DataInputStream(bis);

int length = dis.readInt();
int[] array = new int[length];

for(int i = 0; i < length; i++) array[i] = dis.readInt();
dis.close();
于 2012-09-07T15:03:04.753 回答