【问题标题】:Does Android NDK give speedup for sending large int[] over sockets?Android NDK 是否可以加快通过套接字发送大 int[] 的速度?
【发布时间】:2012-09-05 21:46:00
【问题描述】:

我需要通过两个 Android 设备之间的套接字发送一个包含 500,000 个整数的数组。目前,我花费大量时间将 int[] 转换为 byte[] 以便 Java 的套接字能够接受它(请参阅我之前的问题 Efficiently send large int[] over sockets in Java,我们确定在 Java 中没有更快的类型转换方法) .

我现在的问题是,如果我采用 int[] 并通过 JNI 将其传递给 Android NDK,我能否期望在本机代码中对 byte[] 进行类型转换更快?我知道在普通的 C 中将 int* 类型转换为 char* 非常简单,但是我想知道 JNI 是否会否定任何性能提升。

此外,一旦我的本机代码中有一个 byte[],我可以有效地将它传递回我的 Java 代码,还是我也需要在 C 中实现套接字?

编辑 1:人们在没有点击链接的情况下发布了很多答案。 使用 ByteBuffers 不是一个好的选择,它实际上比 mask-and-shift 慢得多,这仍然比我的性能关键代码需要慢得多!这就是我询问 NDK 的原因。

编辑 2:我将上面的文字更改为 C 代码可以从 int* 转换为 char* 而不是 int[] 转换为 byte[]。希望这能澄清问题。

编辑 3:为了澄清我的用例,这是一个研究问题,我在多个设备上分布大量整数数组并对列表进行并行排序。假设我在 Java 中有 500,000 个整数(不管它们来自哪里),我需要尽快通过套接字将它们从设备中取出。说“不要以整数数组开头”的答案没有帮助。此外,我的应用程序代码需要尽可能接近 100% Java。如果本机类型转换和套接字提高了性能,那没关系,但我不能在本机做任何其他事情(即排序)。

【问题讨论】:

  • int[] 不能“类型转换”为byte[] .. 在任何情况下,可以使用buffers 还是必须是byte[]
  • @pst 不会将 int[] 解释为 C 中 4x 长度的工作(忽略字节序等)的 (unsigned char *)?在 Java 中通过套接字发送 int[] 的要点是,您(或缓冲区)需要在某个时候将其转换为 byte[],这比 arraycopy 工作量更大,而 C 应该能够做到这一点(获取 int[ ] 来自 java 并以本机代码发送)无需转换,因为它可以以不同的方式解释相同的数据。
  • @zapl 我不使用 JNI。但是,这似乎有点粗略,并且依赖于实现细节..无论如何,这仍然会导致 C char* 而不是 Java byte[]。我不知道 JNI 是否允许对内存中的数组进行一些神奇的包装,尤其是 已经属于另一个对象的内存 .. 需要一种方法来告诉 JNI“分离”@ 987654332@ 来自为其分配的内存,因为它现在属于新的假设 byte[] 对象。此外,这种对(char*) 的强制转换在C 中似乎是有问题的。例如,考虑字节序。
  • @pst 是的,不回到 Java byte[] (其中 int[] 和 byte[] 使用相同的内存是不可能的)。以不需要手动转换的方式直接发送某些 Java 对象(或该对象的内存副本 - 了解 JNI 如何处理该对象)正在使用的数据。不是一种(类型/字节序/..)安全方式,而是一种提高通过套接字以任何方式发送 Java int[] 的潜在性能的方式。
  • @pst 我不担心从 int[] 到 char* 的转换的字节序,因为我也控制接收套接字(我可以在那里修复字节序)。任何能够加快传输端的解决方案都会有所帮助。

标签: java android performance casting android-ndk


【解决方案1】:

不,使用 JNI/NDK 不会提高性能。首先,当您尝试将数组转移到本机代码时,您必须复制它或使用直接分配的 ByteBuffer。原来 Dalvik VM 的实现总是从 Get*ArrayElements() 返回直接指针。我确实怀疑你会得到性能提升,因为通过 JNI 调用会产生开销。最后,C++ 和 Java 在这种情况下具有非常接近的性能(请参阅C++ performance vs. Java/C#)。

看看这个问题和第一个答案,了解从 Java How to convert int[] to byte[] 将 int[] 转换为 byte[] 的快速方法

您使用 int[] 而不是 byte[] 开始有什么原因吗?如果是图片,我们可能会推荐避免转换的方法。

【讨论】:

  • 我不同意。在 Android 上,在大多数情况下,GetIntArrayElements() 将返回一个就地指针。因此,有机会通过 JNI 获得性能提升。但你是对的,JNI 增加了开销,它的优势应该仔细权衡。
  • @Samuel 您的链接实际上显示了一种将 int[] 转换为 byte[] 的缓慢方法。我在问题中发布的链接有一个更快的方法。我看不出 Java 和 C++ 的性能如何接近,C++ 只能转换为 char* 而 Java 需要使用数百万个算术运算进行广泛的转换操作。你能详细说明一下吗?
  • 我使用 int[] 因为应用程序是整数列表的并行排序。
  • @AlexCohn 你是对的。我读过一篇说 Sun JVM 总是复制的博客,但我只是查看了 android 的 JNI 源,Get*ArrayElements 总是返回指向原始数组的直接指针。
  • @Samuel 嗯,即使我不能轻松地将 char* 返回到 VM,我也可以始终通过本机套接字将其发送出去。如果这样可以加快速度,我会认为这是一个很好的解决方案。
【解决方案2】:

使用SocketChannel,您可以改用ByteBuffers。

buffer.asIntBuffer().put(ints);
do {
  channel.write(buffer);
} while (buffer.hasRemaining());

【讨论】:

  • +1 即使不使用 NIO,ByteBuffer 也可以wrapbyte[],而asIntBuffer 又可以暴露出来,所以这可能会提供多种选择。但是,使用/实用性仍然取决于使用的其他签名/方法。
  • @pst 确实如此,或者您可以使用Channels.newChannel 包装套接字输出流,但无论哪种方式,我都建议在旧 API 上使用 NIO(阻塞或非阻塞):-)
  • 我在问题中发布的链接排除了该选项。我特意询问了 NDK,因为我已经经历了其他所有事情。
  • @JeremyFowers 为什么不在代码中使用byte[],并根据需要在其上使用适当的“视图”?也就是说,永远不要让它成为一个真正的 int[] 开始。
  • @pst 不确定您所说的“视图”是什么意思。该应用程序将获取一个整数数组,将其中的一半发送到另一个“工作”电话,让两部电话并行进行快速排序,然后将结果传输回“根”电话并进行合并排序。我可能会从一个 byte[] 而不是 int[] 开始,但如果这会使排序过程变慢,它将失去我的优势。
【解决方案3】:

如果 Java 代码需要有效地访问 int[],那么很有可能使用套接字进行本机发送/接收是值得的。

但通常可以使用 IntBuffer 代替 int[]。在这种情况下,您可以分配一个 ByteBuffer 并从中获取一个 IntBuffer:

ByteBuffer bb = ByteBuffer.allocate(500000*4);
IntBuffer ib = bb.asIntBuffer();

您应该同时使用 allocate() 和 allocateDirect(),参见“ByteBuffer.allocate() vs. ByteBuffer.allocateDirect()”和http://objectissues.blogspot.co.il/2005/10/java-nio-allocate-vs-allocatedirect.html

重要提示!在这种情况下,如果您尝试使用ib.array(),您将获得UnsupportedOperationException

【讨论】:

  • 我之前的问题 (stackoverflow.com/questions/12320000/…) 显示 IntBuffer/ByteBuffer 实际上比最优解慢了大约 3 倍。
  • 您使用 IntBuffer 的方式与我在这里提出的相反。您从 int[] 开始,将数组复制到 IntBuffer,并将其“转换”为 ByteBuffer,并将此 ByteBuffer 提供给套接字。我的建议是从 ByteBuffer 开始,并将其表示为 IntBuffer 以访问这些值。没有额外的分配或内存副本。但是有一个代价:IntBuffer.get(index) 比 int[index] 慢很多
  • 无论如何,现在您更清楚地解释了您的用例。我仍然不明白你从哪里得到源百万整数。如果这可以在本机代码中完成,那么您最好的选择是在 C 中执行快速排序 - 它实际上是 C 库的一部分!在这种情况下,您可以使用直接 ByteBuffer 非常有效地向 Java 公开数组,并使用 SocketChannel 进行发送/接收。
  • 有了新的说明,您应该对我回答的第一段感到满意:“有一个 非常 很有可能使用套接字进行本机发送/接收是值得的”
  • 有没有人有一个真实的例子来支持这个答案?
猜你喜欢
  • 1970-01-01
  • 2012-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 2016-12-18
  • 2013-12-15
相关资源
最近更新 更多