直接 java.nio.ByteBuffer 与 Java 阵列性能测试
Direct java.nio.ByteBuffer vs Java Array Performance Test
我想比较直接字节缓冲区(java.nio.ByteBuffer,堆外)和堆缓冲区(通过数组实现)的读取和写入性能。我的理解是,堆外的 ByteBuffer 比堆缓冲区至少有两个好处。首先,它不会被考虑用于 GC,其次(我希望我做对了)JVM 在读取和写入缓冲区时不会使用 intermediate/temporary 缓冲区。这些优点可能使堆外缓冲区比堆缓冲区更快。如果那是正确的,我不应该期望我的基准测试显示相同吗?它总是比非堆缓冲区更快地显示堆缓冲区。
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Fork(value = 2, jvmArgs = {"-Xms2G", "-Xmx4G"})
@Warmup(iterations = 3)
@Measurement(iterations = 10)
public class BasicTest {
@Param({"100000"})
private int N;
final int bufferSize = 10000;
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(8 * bufferSize);
long buffer[] = new long[bufferSize];
public static void main(String arep[]) throws Exception {
Options opt = new OptionsBuilder()
.include(BasicTest.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
@Benchmark
public void offHeapBuffer(Blackhole blackhole) {
IntStream.range(0, bufferSize).forEach(index -> {
byteBuffer.putLong(index, 500 * index);
blackhole.consume(byteBuffer.get(index));
});
}
@Benchmark
public void heapBuffer(Blackhole blackhole) {
IntStream.range(0, bufferSize).forEach(index -> {
buffer[index] = 500 * index;
blackhole.consume(buffer[index]);
});
}
}
Run complete. Total time: 00:00:37
Benchmark (N) Mode Cnt Score Error Units
BasicTest.heapBuffer 100000 avgt 10 0.039 ± 0.003 ms/op
BasicTest.offHeapBuffer 100000 avgt 10 0.050 ± 0.007 ms/op
It won't be considered for GC
当然会考虑GC。
是垃圾收集器判断缓冲区不再被使用,然后释放内存。
Should I not expect my benchmark to show [that] off-heap buffer [is] faster than heap buffer?
堆外不会使缓冲区更快地进行内存访问。
当 Java 与操作系统交换缓冲区中的字节时,直接 缓冲区会更快。由于您的代码没有执行 I/O,因此使用直接缓冲区没有性能优势。
正如 javadoc 所说:
Given a direct byte buffer, the Java virtual machine will make a best effort to perform native I/O operations directly upon it. That is, it will attempt to avoid copying the buffer's content to (or from) an intermediate buffer before (or after) each invocation of one of the underlying operating system's native I/O operations.
在 JDK9 中,HEAP 和 DIRECT 缓冲区都使用 sun.misc.Unsafe 进行原始内存访问。除了 HEAP 缓冲区分配速度更快之外,两者之间的性能差异为零。过去,将多字节基元写入 HEAP 缓冲区会受到很大的惩罚,但现在已经不存在了。
当 reading/writing 来自 IO 时,HEAP 缓冲区较慢,因为在复制到 HEAP 缓冲区之前,必须首先将所有数据复制到 ThreadLocal DIRECT 缓冲区。
这两个对象都可以进行垃圾回收,不同之处在于DirectByteBuffer 使用较少的JVM HEAP 内存,而HeapByteBuffer 将所有内存存储在JVM HEAP 上。 DirectByteBuffer 的垃圾回收过程比 HeapByteBuffer 更复杂。
我想比较直接字节缓冲区(java.nio.ByteBuffer,堆外)和堆缓冲区(通过数组实现)的读取和写入性能。我的理解是,堆外的 ByteBuffer 比堆缓冲区至少有两个好处。首先,它不会被考虑用于 GC,其次(我希望我做对了)JVM 在读取和写入缓冲区时不会使用 intermediate/temporary 缓冲区。这些优点可能使堆外缓冲区比堆缓冲区更快。如果那是正确的,我不应该期望我的基准测试显示相同吗?它总是比非堆缓冲区更快地显示堆缓冲区。
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Fork(value = 2, jvmArgs = {"-Xms2G", "-Xmx4G"})
@Warmup(iterations = 3)
@Measurement(iterations = 10)
public class BasicTest {
@Param({"100000"})
private int N;
final int bufferSize = 10000;
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(8 * bufferSize);
long buffer[] = new long[bufferSize];
public static void main(String arep[]) throws Exception {
Options opt = new OptionsBuilder()
.include(BasicTest.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
@Benchmark
public void offHeapBuffer(Blackhole blackhole) {
IntStream.range(0, bufferSize).forEach(index -> {
byteBuffer.putLong(index, 500 * index);
blackhole.consume(byteBuffer.get(index));
});
}
@Benchmark
public void heapBuffer(Blackhole blackhole) {
IntStream.range(0, bufferSize).forEach(index -> {
buffer[index] = 500 * index;
blackhole.consume(buffer[index]);
});
}
}
Run complete. Total time: 00:00:37
Benchmark (N) Mode Cnt Score Error Units
BasicTest.heapBuffer 100000 avgt 10 0.039 ± 0.003 ms/op
BasicTest.offHeapBuffer 100000 avgt 10 0.050 ± 0.007 ms/op
It won't be considered for GC
当然会考虑GC。
是垃圾收集器判断缓冲区不再被使用,然后释放内存。
Should I not expect my benchmark to show [that] off-heap buffer [is] faster than heap buffer?
堆外不会使缓冲区更快地进行内存访问。
当 Java 与操作系统交换缓冲区中的字节时,直接 缓冲区会更快。由于您的代码没有执行 I/O,因此使用直接缓冲区没有性能优势。
正如 javadoc 所说:
Given a direct byte buffer, the Java virtual machine will make a best effort to perform native I/O operations directly upon it. That is, it will attempt to avoid copying the buffer's content to (or from) an intermediate buffer before (or after) each invocation of one of the underlying operating system's native I/O operations.
在 JDK9 中,HEAP 和 DIRECT 缓冲区都使用 sun.misc.Unsafe 进行原始内存访问。除了 HEAP 缓冲区分配速度更快之外,两者之间的性能差异为零。过去,将多字节基元写入 HEAP 缓冲区会受到很大的惩罚,但现在已经不存在了。
当 reading/writing 来自 IO 时,HEAP 缓冲区较慢,因为在复制到 HEAP 缓冲区之前,必须首先将所有数据复制到 ThreadLocal DIRECT 缓冲区。
这两个对象都可以进行垃圾回收,不同之处在于DirectByteBuffer 使用较少的JVM HEAP 内存,而HeapByteBuffer 将所有内存存储在JVM HEAP 上。 DirectByteBuffer 的垃圾回收过程比 HeapByteBuffer 更复杂。