Java 何时使用数组、缓冲区或直接缓冲区
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18913001/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When to use Array, Buffer or direct Buffer
提问by TwoThe
Question
题
While writing a Matrix class for use with OpenGL libraries, I came across the question of whether to use Java arrays or a Buffer strategy to store the data (JOGL offers direct-buffer copy for Matrix operations). To analyze this, I wrote a small performance test program that compares the relative speeds of loop and bulk operations on Arrays vs Buffers vs direct Buffers.
在编写用于 OpenGL 库的 Matrix 类时,我遇到了是使用 Java 数组还是使用 Buffer 策略来存储数据的问题(JOGL 为 Matrix 操作提供直接缓冲区复制)。为了分析这一点,我编写了一个小型性能测试程序,比较了数组、缓冲区和直接缓冲区上循环和批量操作的相对速度。
I'd like to share my results with you here (as I find them rather interesting). Please feel free to comment and/or point out any mistakes.
The code can be viewed at pastebin.com/is7UaiMV.
我想在这里与您分享我的结果(因为我觉得它们很有趣)。请随时发表评论和/或指出任何错误。
可以在pastebin.com/is7UaiMV查看代码。
Notes
笔记
Loop-read array is implemented as A[i] = B[i]as otherwise the JIT optimizer will completely remove that code. Actual var = A[i]seems to be pretty much the same.
In the sample result for array size of 10,000 it is very likely that the JIT optimizer has replaced the looped array access with a System.arraycopy like implementation.
There is no bulk-get buffer->buffer as Java implements A.get(B)as B.put(A), therefore the results would be the same as the bulk-put results.
循环读取数组实现为A[i] = B[i]否则 JIT 优化器将完全删除该代码。实际var = A[i]似乎几乎相同。
在数组大小为 10,000 的示例结果中,JIT 优化器很可能已将循环数组访问替换为类似 System.arraycopy 的实现。
没有批量获取缓冲区->缓冲区,因为 Java 将A.get(B) 实现为B.put(A),因此结果将与批量放置结果相同。
Conclusion
结论
Under almost all situations it is strongly recommended to use the Java internal Arrays. Not only is the put/get speed massively faster, the JIT is as well able to perform much better optimizations on the final code.
在几乎所有情况下,强烈建议使用 Java 内部数组。不仅 put/get 速度大大加快,JIT 还能够对最终代码执行更好的优化。
Buffers should onlybe used if boththe following applies:
仅当以下两种情况都适用时才应使用缓冲区:
- You need to process large amountsof data.
- That data is mostly or always bulk-processed.
- 您需要处理大量数据。
- 该数据主要或始终是批量处理的。
Note that a backened-buffer has a Java Array backening the content of the buffer. It is recommended to do operations on this back-buffer instead of looping put/get.
请注意,后备缓冲区有一个 Java 数组来支持缓冲区的内容。建议在此后台缓冲区上执行操作,而不是循环放置/获取。
Direct buffers should onlybe used if you worry about memory usageand never access the underlying data. They are slightly slower than non-direct buffers, much slower if the underlying data is accessed, but use less memory. In addition there is an extra overhead when converting non-byte data (like float-arrays) into bytes when using a direct buffer.
仅当您担心内存使用并且从不访问底层数据时才应使用直接缓冲区。它们比非直接缓冲区稍慢,如果访问底层数据则慢得多,但使用更少的内存。此外,在使用直接缓冲区时,将非字节数据(如浮点数组)转换为字节会产生额外的开销。
For more details see here:
有关更多详细信息,请参见此处:
- Why only ByteBuffers are useful when using direct buffers
- Internal overhead on NIO and what slows buffers down
Sample results
示例结果
Note: Percentage is only for ease of reading and has no real meaning.
注意:百分比只是为了便于阅读,没有实际意义。
Using arrays of size 16 with 10,000,000 iterations...
使用大小为 16 的数组和 10,000,000 次迭代...
-- Array tests: -----------------------------------------
Loop-write array: 87.29 ms 11,52%
Arrays.fill: 64.51 ms 8,51%
Loop-read array: 42.11 ms 5,56%
System.arraycopy: 47.25 ms 6,23%
-- Buffer tests: ----------------------------------------
Loop-put buffer: 603.71 ms 79,65%
Index-put buffer: 536.05 ms 70,72%
Bulk-put array->buffer: 105.43 ms 13,91%
Bulk-put buffer->buffer: 99.09 ms 13,07%
Bulk-put bufferD->buffer: 80.38 ms 10,60%
Loop-get buffer: 505.77 ms 66,73%
Index-get buffer: 562.84 ms 74,26%
Bulk-get buffer->array: 137.86 ms 18,19%
-- Direct buffer tests: ---------------------------------
Loop-put bufferD: 570.69 ms 75,29%
Index-put bufferD: 562.76 ms 74,25%
Bulk-put array->bufferD: 712.16 ms 93,96%
Bulk-put buffer->bufferD: 83.53 ms 11,02%
Bulk-put bufferD->bufferD: 118.00 ms 15,57%
Loop-get bufferD: 528.62 ms 69,74%
Index-get bufferD: 560.36 ms 73,93%
Bulk-get bufferD->array: 757.95 ms 100,00%
Using arrays of size 1,000 with 100,000 iterations...
使用大小为 1,000 的数组和 100,000 次迭代...
-- Array tests: -----------------------------------------
Loop-write array: 22.10 ms 6,21%
Arrays.fill: 10.37 ms 2,91%
Loop-read array: 81.12 ms 22,79%
System.arraycopy: 10.59 ms 2,97%
-- Buffer tests: ----------------------------------------
Loop-put buffer: 355.98 ms 100,00%
Index-put buffer: 353.80 ms 99,39%
Bulk-put array->buffer: 16.33 ms 4,59%
Bulk-put buffer->buffer: 5.40 ms 1,52%
Bulk-put bufferD->buffer: 4.95 ms 1,39%
Loop-get buffer: 299.95 ms 84,26%
Index-get buffer: 343.05 ms 96,37%
Bulk-get buffer->array: 15.94 ms 4,48%
-- Direct buffer tests: ---------------------------------
Loop-put bufferD: 355.11 ms 99,75%
Index-put bufferD: 348.63 ms 97,93%
Bulk-put array->bufferD: 190.86 ms 53,61%
Bulk-put buffer->bufferD: 5.60 ms 1,57%
Bulk-put bufferD->bufferD: 7.73 ms 2,17%
Loop-get bufferD: 344.10 ms 96,66%
Index-get bufferD: 333.03 ms 93,55%
Bulk-get bufferD->array: 190.12 ms 53,41%
Using arrays of size 10,000 with 100,000 iterations...
使用大小为 10,000 的数组和 100,000 次迭代...
-- Array tests: -----------------------------------------
Loop-write array: 156.02 ms 4,37%
Arrays.fill: 109.06 ms 3,06%
Loop-read array: 300.45 ms 8,42%
System.arraycopy: 147.36 ms 4,13%
-- Buffer tests: ----------------------------------------
Loop-put buffer: 3385.94 ms 94,89%
Index-put buffer: 3568.43 ms 100,00%
Bulk-put array->buffer: 159.40 ms 4,47%
Bulk-put buffer->buffer: 5.31 ms 0,15%
Bulk-put bufferD->buffer: 6.61 ms 0,19%
Loop-get buffer: 2907.21 ms 81,47%
Index-get buffer: 3413.56 ms 95,66%
Bulk-get buffer->array: 177.31 ms 4,97%
-- Direct buffer tests: ---------------------------------
Loop-put bufferD: 3319.25 ms 93,02%
Index-put bufferD: 3538.16 ms 99,15%
Bulk-put array->bufferD: 1849.45 ms 51,83%
Bulk-put buffer->bufferD: 5.60 ms 0,16%
Bulk-put bufferD->bufferD: 7.63 ms 0,21%
Loop-get bufferD: 3227.26 ms 90,44%
Index-get bufferD: 3413.94 ms 95,67%
Bulk-get bufferD->array: 1848.24 ms 51,79%
采纳答案by Holger
Direct buffers are not meant to accelerate access from Java code. (If that were possible there was something wrong with the JVM's own array implementation.)
直接缓冲区并不是为了加速从 Java 代码的访问。(如果可能的话,JVM 自己的数组实现就会有问题。)
These byte buffers are for interfacing with other components as you can write a byte buffer to a ByteChannel
and you can use direct buffers in conjunction with native code such as with the OpenGL libraries you mentioned. It's intended to accelerate theseoperation then. Using a graphics card's chip for rendering can accelerate the overall operation to a degree more than compensating the possibly slower access to the buffer from Java code.
这些字节缓冲区用于与其他组件接口,因为您可以将字节缓冲区写入 aByteChannel
并且您可以将直接缓冲区与本机代码(例如您提到的 OpenGL 库)结合使用。它旨在加速这些操作。使用图形卡的芯片进行渲染可以在一定程度上加速整体操作,而不是补偿 Java 代码对缓冲区的可能较慢的访问。
By the way, if you measure the access speed to a byte buffer, especially the direct byte buffers, it's worth changing the byte order to the nativebyte order before acquiring a FloatBuffer
view:
顺便说一句,如果您测量对字节缓冲区的访问速度,尤其是直接字节缓冲区,则值得在获取视图之前将字节顺序更改为本机字节顺序FloatBuffer
:
FloatBuffer bufferD = ByteBuffer.allocateDirect(SIZE * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
回答by Pacerier
Tldr:
网址:
Use direct buffers onlyif we need to do efficient high-speed I/O.
仅当我们需要进行高效的高速I/O时才使用直接缓冲区。
If we need efficient high-speed non-I/Ooperations, default array is the best choice.
如果我们需要高效的高速非 I/O操作,默认数组是最好的选择。
If we need to do buffer-like operations on a default array, and we can afford to be slow, then use an array-backed buffer.
如果我们需要对默认数组执行类似缓冲区的操作,并且我们可以承受慢,那么请使用数组支持的缓冲区。
Tsdr:
tsdr:
Your tests did not test any I/O operations and it's conclusion is therefore wrong.
您的测试没有测试任何 I/O 操作,因此得出的结论是错误的。
Your conclusion states (emphasis not mine):
你的结论指出(强调不是我的):
Direct buffers should onlybe used if you worry about memory usageand never access the underlying data. They are slightly slower than non-direct buffers, much slower if the underlying data is accessed, but use less memory. In addition there is an extra overhead when converting non-byte data (like float-arrays) into bytes when using a direct buffer.
仅当您担心内存使用并且从不访问底层数据时才应使用直接缓冲区。它们比非直接缓冲区稍慢,如果访问底层数据则慢得多,但使用更少的内存。此外,在使用直接缓冲区时,将非字节数据(如浮点数组)转换为字节会产生额外的开销。
That is clearly wrong. Direct buffers are meant to solve speed problems, not memory ones problems. Direct buffers should be used whenever you need high-performance I/O access. This includes file/network operations etc. It is definitely faster when used correctlyand is in fact the fastest that Java API provides out-of-the-box.
那显然是错误的。直接缓冲区旨在解决速度问题,而不是内存问题。当您需要高性能I/O 访问时,应使用直接缓冲区。这包括文件/网络操作等。正确使用时肯定会更快,实际上是 Java API 开箱即用提供的最快的。
When doing file/network operations, there is an extra overhead when converting non-byte data into bytes. This is true for everything, not just direct buffers.
在进行文件/网络操作时,将非字节数据转换为字节会产生额外的开销。这适用于所有事物,而不仅仅是直接缓冲区。
Your conclusion also states:
您的结论还指出:
Note that a backened-buffer has a Java Array backening the content of the buffer. It is recommended to do operations on this back-buffer instead of looping put/get.
请注意,后备缓冲区有一个 Java 数组来支持缓冲区的内容。建议在此后台缓冲区上执行操作,而不是循环放置/获取。
This is true, but you are missing the whole point of array-backed buffers. Array-backed buffers is a facade patternon top of arrays. Array-backed buffers will never be faster than arrays themselves since internally they have to use the array.
这是真的,但是您错过了数组支持的缓冲区的全部意义。数组支持的缓冲区是数组之上的外观模式。数组支持的缓冲区永远不会比数组本身快,因为它们在内部必须使用数组。
As such, they are there for convenience, not for speed. In other words, if you need speed, it's recommended to choose array over array-facade. If you need convenience/readability, it's recommended to choose array-facade over array for buffer-like operations on array.
因此,它们的存在是为了方便,而不是为了速度。换句话说,如果你需要速度,建议选择 array 而不是 array-facade。如果您需要方便/可读性,建议在数组上选择数组外观而不是数组,以便对数组进行类似缓冲区的操作。
Also read:
另请阅读: