Skip to content

Instantly share code, notes, and snippets.

@hugmanrique
Last active January 1, 2021 23:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hugmanrique/786bab5031d2b1b8bf07214a89adc0ee to your computer and use it in GitHub Desktop.
Save hugmanrique/786bab5031d2b1b8bf07214a89adc0ee to your computer and use it in GitHub Desktop.
Panama (3-b385) MemoryAccess vs Unsafe vs ByteBuffer on the heap
package me.hugmanrique.mab;
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
import jdk.incubator.foreign.MemoryAccess;
import jdk.incubator.foreign.MemorySegment;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import sun.misc.Unsafe;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Fork(1)
public class MemoryAccessBenchmark {
// Based on https://git.io/JL5kQ by M. Cimadamore
static final int ELEM_COUNT = 1_000_000;
static final int CARRIER_SIZE = (int) JAVA_INT.byteSize();
static final int ALLOC_SIZE = ELEM_COUNT * CARRIER_SIZE;
static final Unsafe UNSAFE = UnsafeUtil.UNSAFE;
byte[] array;
ByteBuffer buffer;
MemorySegment segment;
@Setup
public void setup() {
array = new byte[ALLOC_SIZE];
buffer = ByteBuffer.allocate(ALLOC_SIZE);
segment = MemorySegment.ofArray(new byte[ALLOC_SIZE]);
for (int rep = 0; rep < 5; rep++) {
for (int i = 0; i < ELEM_COUNT; i++) {
UNSAFE.putInt(array, Unsafe.ARRAY_BYTE_BASE_OFFSET + (i * 4L), i);
buffer.putInt(i, i);
MemoryAccess.setIntAtIndex(segment, i, i);
}
}
}
@TearDown
public void teardown() {
array = null;
buffer = null;
segment.close();
}
@Benchmark
public int heap_ints_unsafe() {
int sum = 0;
for (int k = 0; k < ALLOC_SIZE; k += 4) {
UNSAFE.putInt(array, Unsafe.ARRAY_BYTE_BASE_OFFSET + k, k + 1);
int value = UNSAFE.getInt(array, k);
sum += value;
}
return sum;
}
@Benchmark
public int heap_ints_buffer() {
int sum = 0;
for (int k = 0; k < ELEM_COUNT; k++) {
buffer.putInt(k, k + 1);
int value = buffer.getInt(k);
sum += value;
}
return sum;
}
@Benchmark
public int heap_ints_memoryaccess() {
int sum = 0;
for (int k = 0; k < ELEM_COUNT; k++) {
MemoryAccess.setIntAtOffset(segment, k, k + 1);
int value = MemoryAccess.getIntAtOffset(segment, k);
sum += value;
}
return sum;
}
@Benchmark
public int heap_int_manual() {
int sum = 0;
for (int k = 0; k < ALLOC_SIZE; k += 4) {
int value = k + 1;
array[k] = (byte) (value >> 24);
array[k + 1] = (byte) (value >> 16);
array[k + 2] = (byte) (value >> 8);
array[k + 3] = (byte) value;
value = (array[k] << 24)
| ((array[k + 1] << 16) & 0xFF)
| ((array[k + 2] << 8) & 0xFF)
| (array[k + 3] & 0xFF);
sum += value;
}
return sum;
}
public static void main(final String[] args) throws RunnerException {
var options = new OptionsBuilder()
.include(MemoryAccessBenchmark.class.getSimpleName())
.forks(1)
.build();
new Runner(options).run();
}
}
@hugmanrique
Copy link
Author

hugmanrique commented Jan 1, 2021

Results on JDK build 16-panama+3-385 (2020/12/10):

Benchmark                                     Mode  Cnt        Score       Error  Units
MemoryAccessBenchmark.heap_ints_unsafe        avgt    5   268422.047 ±  3470.436  ns/op
MemoryAccessBenchmark.heap_ints_buffer        avgt    5   771392.161 ± 51517.404  ns/op
MemoryAccessBenchmark.heap_ints_memoryaccess  avgt    5   306507.668 ±   384.992  ns/op
MemoryAccessBenchmark.heap_ints_manual        avgt    5  1552070.800 ±  8535.541  ns/op

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment