I wanted to compare performance of a direct byte buffer (java.nio.ByteBuffer, off-heap) and a heap buffer (achieved via array) for both read and writes. My understanding was, ByteBuffer being off-heap gets at least two benefits over a heap buffer. First, it won't be considered for GC and secondly (i hope i got it right) JVM won't use an intermediate/temporary buffer when reading from and writing to it. These advantages may make off-heap buffer faster than heap buffer. If that's correct, should I not expect my benchmark to show the same? It always shows heap-buffer faster than non-heap one.
@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
There are several differences between a byte array and ByteBuffer class in Java, but the most important of them is that bytes from byte array always reside in Java heap space, but bytes in a ByteBuffer may potentially reside outside of the Java heap in case of direct byte buffer and memory mapped files.
A ByteBuffer is a buffer which provides for transferring bytes from a source to a destination. In addition to storage like a buffer array, it also provides abstractions such as current position, limit, capacity, etc. A FileChannel is used for transferring data to and from a file to a ByteBuffer.
A direct buffer is a chunk of native memory shared with Java from which you can perform a direct read. An instance of DirectByteBuffer can be created using the ByteBuffer. allocateDirect() factory method.
ByteBuffer holds a sequence of integer values to be used in an I/O operation. The ByteBuffer class provides the following four categories of operations upon long buffers: Absolute and relative get method that read single bytes. Absolute and relative put methods that write single bytes.
It won't be considered for GC
Of course it will be considered for GC.
It is the Garbage Collector that determines that the buffer is no longer in use, and then deallocates the memory.
Should I not expect my benchmark to show [that] off-heap buffer [is] faster than heap buffer?
Being off-heap doesn't make the buffer faster for memory access.
A direct buffer will be faster when Java exchanges the bytes in the buffer with the operating system. Since your code is not doing I/O, there is no performance benefit to using a direct buffer.
As the javadoc says it:
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.
In JDK9, both HEAP and DIRECT buffers use the sun.misc.Unsafe for raw memory access. There is ZERO performance difference between the two other than HEAP buffers allocate faster. There used to be a big penalty for writing multiple-byte primitives to HEAP buffers but that is gone now.
When reading/writing from IO the HEAP buffer is slower because all the data MUST be first copied to a ThreadLocal DIRECT buffer before being copied into your HEAP buffer.
Both objects can be garbage-collected, the difference is that DirectByteBuffer use LESS of JVM HEAP memory whereas HeapByteBuffer store all memory on the JVM HEAP. The garbage-collection process for DirectByteBuffer is more complicated then HeapByteBuffer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With