Last active
January 1, 2021 23:34
-
-
Save hugmanrique/786bab5031d2b1b8bf07214a89adc0ee to your computer and use it in GitHub Desktop.
Panama (3-b385) MemoryAccess vs Unsafe vs ByteBuffer on the heap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Results on JDK build 16-panama+3-385 (2020/12/10):