Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Direct java.nio.ByteBuffer vs Java Array Performance Test

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

like image 542
Abidi Avatar asked Nov 23 '19 14:11

Abidi


People also ask

What is the difference between ByteBuffer and byte array?

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.

What is Java NIO ByteBuffer?

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.

What is a direct 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.

How does ByteBuffer work in Java?

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.


2 Answers

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.

like image 95
Andreas Avatar answered Sep 19 '22 16:09

Andreas


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.

like image 45
Johnny V Avatar answered Sep 21 '22 16:09

Johnny V