当前位置: 首页 > 面试题库 >

何时使用数组,缓冲区或直接缓冲区

上官德寿
2023-03-14
问题内容

在编写用于OpenGL库的Matrix类时,我遇到了一个问题,即使用Java数组还是使用Buffer策略存储数据(JOGL为Matrix操作提供直接缓冲区复制)。为了对此进行分析,我编写了一个小型性能测试程序,该程序比较了Arrays
vs Buffers和Direct Buffers上循环和批量操作的相对速度。

我想在这里与您分享我的结果(因为我发现它们很有趣)。请随时发表评论和/或指出任何错误。
可以在pastebin.com/is7UaiMV上查看该代码。

笔记

  • 循环读取数组实现为 A [i] = B [i] ,否则JIT优化器将完全删除该代码。实际 var = A [i] 似乎几乎相同。

  • 在数组大小为10,000的示例结果中,很可能JIT优化器已用类似System.arraycopy的实现代替了循环数组访问。

  • 由于Java将 A.get(B)实现B.put(A) ,因此没有大容量获取缓冲区-> buffer ,因此结果将与大容量获取结果相同。

结论

在几乎所有情况下,强烈建议使用Java内部数组。不仅放置/获取速度大大提高了,而且JIT还能对最终代码执行更好的优化。

只有 同时 满足以下 两个条件 时才 应使用缓冲区:

  • 您需要处理 大量 数据。
  • 该数据大部分或始终被 批量处理

请注意,backened-buffer具有Java Array来备份缓冲区的内容。建议在此后​​缓冲区上执行操作,而不是循环放置/获取。

当您担心 内存使用情况 并且从不访问基础数据时,
应使用直接缓冲区。它们比非直接缓冲区要慢一些,如果访问基础数据则要慢得多,但是使用更少的内存。此外,使用直接缓冲区将非字节数据(如float数组)转换为字节时,会产生额外的开销。

样品结果

注意:百分比仅是为了易于阅读,没有实际意义。

使用大小为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%

使用大小为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%

使用大小为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%

问题答案:

直接缓冲区并不旨在加速从Java代码的访问。(如果可能的话,JVM自己的数组实现有问题。)

这些字节缓冲区用于与其他组件接口,因为您可以向a写入字节缓冲区,ByteChannel并且可以将直接缓冲区与本机代码(例如您提到的OpenGL库)结合使用。旨在加速_这些_ 操作。使用图形卡的芯片进行渲染可以加快整体操作的速度,而不是补偿Java代码对缓冲区的较慢访问。

顺便说一句,如果您测量对字节缓冲区(特别是直接字节缓冲区)的访问速度,那么在获取视图之前,有必要将字节顺序更改为本 _机_字节顺序FloatBuffer

FloatBuffer bufferD = ByteBuffer.allocateDirect(SIZE * 4)
                                .order(ByteOrder.nativeOrder())
                                .asFloatBuffer();


 类似资料:
  • 问题内容: 由于它不在jvm heap&gc中,何时发布?还是一直保留到流程终止? 但是所有答案都是模糊的,没有一个明确的答案,是否有明确的答案?至少适用于 64位Linux 上的 Java 8 。 __ 问题答案: 不使用旧的Java终结器。相反,它使用内部API。它创建一个新线程并存储到每个创建的线程中(除了重复和切片指的是主缓冲区)。当变成 幻影可到达的 (也就是说,不再存在对字节缓冲区的强

  • 纯JavaScript是Unicode友好的,但二进制数据却不是这样。 在处理TCP流或文件系统时,必须处理八位字节流。 Node提供了Buffer类,它提供了存储类似于整数数组的原始数据的实例,但对应于V8堆外部的原始内存分配。 Buffer类是一个全局类,可以在应用程序中访问而无需导入缓冲区模块。 创建缓冲区 节点缓冲区可以以多种方式构建。 Method 1 以下是创建10个八位字节的无启动缓

  • 问题内容: 我们正在构建具有积极性能SLA的Web应用程序,由于JVM由于System.gc()调用而无法正常工作,因此这些SLA经常遭到违反。我们已经进行了一些调试,并确定在所有情况下都是调用System.gc()的内部应用服务器代码。在应用服务器启动或部署应用程序时,这种情况会发生几次,我们并不在意。但是,当应用程序通过内部应用程序服务器启动和运行并调用NIO类时,也会定期触发System.g

  • 我正在使用JOGL,但这个问题一般适用于OpenGL。似乎存在类似的问题,但它们要么针对GLSL代码,要么与复制帧缓冲区的内容有关,要么是一般建议-使用帧缓冲区对象,而不是。 我正在做一些阴影映射。如何使用帧缓冲对象将深度通道直接渲染到纹理? 能否请你贴一段初始化纹理和帧缓冲对象的代码,以及渲染场景前初始化一切的代码? 目前,我使用<code>glCopyTexSubImage2D<code>。我

  • 问题内容: 我有以下代码,旨在读取目录并将其压缩到tar.gz归档文件中。当我将代码部署到服务器上并使用一批文件对其进行测试时,它可以在前几个测试批处理中使用,但是在第4批或第5批处理之后,它将始终如一地为我提供java.lang.OutOfMemoryError:即使直接缓冲内存文件批处理大小保持不变,并且堆空间看起来不错。这是代码: } 这是一个例外: 我认为有一个缓冲区内存泄漏,因为它在前4

  • 主要内容:1 Buffer的基本使用,2 Buffer的容量、位置、限制,3 Buffer的类型,4 Buffer的分配,5 将数据写入Buffer,6 从Buffer读取数据与NIO通道进行交互时,将使用Java NIO缓冲区。如您所知,数据从通道读取到缓冲区,然后从缓冲区写入通道。 缓冲区本质上是一个内存块,您可以在其中写入数据,然后可以在以后再次读取。该内存块包装在NIO Buffer对象中,该对象提供了一组方法,可以更轻松地使用该内存块。 1 Buffer的基本使用 使用Buffer来读