diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 59537c2..9eff550 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: ['17', '21'] + java: ['24'] steps: - uses: actions/checkout@v4 - name: Setup JDK ${{ matrix.java }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9195b17..da4c2a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: - name: Install java uses: actions/setup-java@v4 with: - java-version: '21' + java-version: '24' distribution: 'temurin' gpg-private-key: ${{ secrets.JRELEASER_GPG_SECRET_KEY }} gpg-passphrase: MAVEN_GPG_PASSPHRASE diff --git a/pom.xml b/pom.xml index bc74356..504c085 100644 --- a/pom.xml +++ b/pom.xml @@ -28,8 +28,8 @@ -missing 3584m - 17 - 17 + 22 + 22 8 true true diff --git a/src/main/java/io/airlift/slice/Slice.java b/src/main/java/io/airlift/slice/Slice.java index fe7c388..b71e0b8 100644 --- a/src/main/java/io/airlift/slice/Slice.java +++ b/src/main/java/io/airlift/slice/Slice.java @@ -18,13 +18,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.foreign.MemorySegment; import java.lang.invoke.VarHandle; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Arrays; -import static io.airlift.slice.JvmUtils.unsafe; import static io.airlift.slice.Preconditions.checkArgument; import static io.airlift.slice.SizeOf.SIZE_OF_BYTE; import static io.airlift.slice.SizeOf.SIZE_OF_DOUBLE; @@ -39,12 +39,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.checkFromIndexSize; import static java.util.Objects.requireNonNull; -import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; -import static sun.misc.Unsafe.ARRAY_DOUBLE_BASE_OFFSET; -import static sun.misc.Unsafe.ARRAY_FLOAT_BASE_OFFSET; -import static sun.misc.Unsafe.ARRAY_INT_BASE_OFFSET; -import static sun.misc.Unsafe.ARRAY_LONG_BASE_OFFSET; -import static sun.misc.Unsafe.ARRAY_SHORT_BASE_OFFSET; public final class Slice implements Comparable @@ -377,10 +371,10 @@ public void getBytes(int index, Slice destination) */ public void getBytes(int index, Slice destination, int destinationIndex, int length) { - checkFromIndexSize(destinationIndex, length, destination.length()); - checkFromIndexSize(index, length, length()); - - System.arraycopy(base, baseOffset + index, destination.base, destination.baseOffset + destinationIndex, length); + MemorySegment.copy( + MemorySegment.ofArray(base), baseOffset + index, + MemorySegment.ofArray(destination.base), destination.baseOffset + destinationIndex, + length); } /** @@ -410,10 +404,10 @@ public void getBytes(int index, byte[] destination) */ public void getBytes(int index, byte[] destination, int destinationIndex, int length) { - checkFromIndexSize(index, length, length()); - checkFromIndexSize(destinationIndex, length, destination.length); - - System.arraycopy(base, baseOffset + index, destination, destinationIndex, length); + MemorySegment.copy( + MemorySegment.ofArray(base), baseOffset + index, + MemorySegment.ofArray(destination), destinationIndex, + length); } /** @@ -498,10 +492,10 @@ public void getShorts(int index, short[] destination) */ public void getShorts(int index, short[] destination, int destinationIndex, int length) { - checkFromIndexSize(index, length * Short.BYTES, length()); - checkFromIndexSize(destinationIndex, length, destination.length); - - copyFromBase(index, destination, ARRAY_SHORT_BASE_OFFSET + ((long) destinationIndex * Short.BYTES), length * Short.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(base), index, + MemorySegment.ofArray(destination), (long) destinationIndex * Short.BYTES, + (long) length * Short.BYTES); } /** @@ -546,10 +540,10 @@ public void getInts(int index, int[] destination) */ public void getInts(int index, int[] destination, int destinationIndex, int length) { - checkFromIndexSize(index, length * Integer.BYTES, length()); - checkFromIndexSize(destinationIndex, length, destination.length); - - copyFromBase(index, destination, ARRAY_INT_BASE_OFFSET + ((long) destinationIndex * Integer.BYTES), length * Integer.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(base), index, + MemorySegment.ofArray(destination), (long) destinationIndex * Integer.BYTES, + (long) length * Integer.BYTES); } /** @@ -594,10 +588,10 @@ public void getLongs(int index, long[] destination) */ public void getLongs(int index, long[] destination, int destinationIndex, int length) { - checkFromIndexSize(index, length * Long.BYTES, length()); - checkFromIndexSize(destinationIndex, length, destination.length); - - copyFromBase(index, destination, ARRAY_LONG_BASE_OFFSET + ((long) destinationIndex * Long.BYTES), length * Long.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(base), index, + MemorySegment.ofArray(destination), (long) destinationIndex * Long.BYTES, + (long) length * Long.BYTES); } /** @@ -642,10 +636,10 @@ public void getFloats(int index, float[] destination) */ public void getFloats(int index, float[] destination, int destinationIndex, int length) { - checkFromIndexSize(index, length * Float.BYTES, length()); - checkFromIndexSize(destinationIndex, length, destination.length); - - copyFromBase(index, destination, ARRAY_FLOAT_BASE_OFFSET + ((long) destinationIndex * Float.BYTES), length * Float.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(base), index, + MemorySegment.ofArray(destination), (long) destinationIndex * Float.BYTES, + (long) length * Float.BYTES); } /** @@ -690,10 +684,10 @@ public void getDoubles(int index, double[] destination) */ public void getDoubles(int index, double[] destination, int destinationIndex, int length) { - checkFromIndexSize(index, length * Double.BYTES, length()); - checkFromIndexSize(destinationIndex, length, destination.length); - - copyFromBase(index, destination, ARRAY_DOUBLE_BASE_OFFSET + ((long) destinationIndex * Double.BYTES), length * Double.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(base), index, + MemorySegment.ofArray(destination), (long) destinationIndex * Double.BYTES, + (long) length * Double.BYTES); } /** @@ -822,10 +816,10 @@ public void setBytes(int index, Slice source) */ public void setBytes(int index, Slice source, int sourceIndex, int length) { - checkFromIndexSize(index, length, length()); - checkFromIndexSize(sourceIndex, length, source.length()); - - System.arraycopy(source.base, source.baseOffset + sourceIndex, base, baseOffset + index, length); + MemorySegment.copy( + MemorySegment.ofArray(source.base), source.baseOffset + sourceIndex, + MemorySegment.ofArray(base), baseOffset + index, + length); } /** @@ -852,9 +846,10 @@ public void setBytes(int index, byte[] source) */ public void setBytes(int index, byte[] source, int sourceIndex, int length) { - checkFromIndexSize(index, length, length()); - checkFromIndexSize(sourceIndex, length, source.length); - System.arraycopy(source, sourceIndex, base, baseOffset + index, length); + MemorySegment.copy( + MemorySegment.ofArray(source), sourceIndex, + MemorySegment.ofArray(base), baseOffset + index, + length); } /** @@ -904,9 +899,10 @@ public void setShorts(int index, short[] source) */ public void setShorts(int index, short[] source, int sourceIndex, int length) { - checkFromIndexSize(index, length, length()); - checkFromIndexSize(sourceIndex, length, source.length); - copyToBase(index, source, ARRAY_SHORT_BASE_OFFSET + ((long) sourceIndex * Short.BYTES), length * Short.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(source), (long) sourceIndex * Short.BYTES, + MemorySegment.ofArray(base), index, + (long) length * Short.BYTES); } /** @@ -933,9 +929,10 @@ public void setInts(int index, int[] source) */ public void setInts(int index, int[] source, int sourceIndex, int length) { - checkFromIndexSize(index, length, length()); - checkFromIndexSize(sourceIndex, length, source.length); - copyToBase(index, source, ARRAY_INT_BASE_OFFSET + ((long) sourceIndex * Integer.BYTES), length * Integer.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(source), (long) sourceIndex * Integer.BYTES, + MemorySegment.ofArray(base), index, + (long) length * Integer.BYTES); } /** @@ -962,9 +959,10 @@ public void setLongs(int index, long[] source) */ public void setLongs(int index, long[] source, int sourceIndex, int length) { - checkFromIndexSize(index, length, length()); - checkFromIndexSize(sourceIndex, length, source.length); - copyToBase(index, source, ARRAY_LONG_BASE_OFFSET + ((long) sourceIndex * Long.BYTES), length * Long.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(source), (long) sourceIndex * Long.BYTES, + MemorySegment.ofArray(base), index, + (long) length * Long.BYTES); } /** @@ -991,9 +989,10 @@ public void setFloats(int index, float[] source) */ public void setFloats(int index, float[] source, int sourceIndex, int length) { - checkFromIndexSize(index, length, length()); - checkFromIndexSize(sourceIndex, length, source.length); - copyToBase(index, source, ARRAY_FLOAT_BASE_OFFSET + ((long) sourceIndex * Float.BYTES), length * Float.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(source), (long) sourceIndex * Float.BYTES, + MemorySegment.ofArray(base), index, + (long) length * Float.BYTES); } /** @@ -1020,9 +1019,10 @@ public void setDoubles(int index, double[] source) */ public void setDoubles(int index, double[] source, int sourceIndex, int length) { - checkFromIndexSize(index, length, length()); - checkFromIndexSize(sourceIndex, length, source.length); - copyToBase(index, source, ARRAY_DOUBLE_BASE_OFFSET + ((long) sourceIndex * Double.BYTES), length * Double.BYTES); + MemorySegment.copy( + MemorySegment.ofArray(source), (long) sourceIndex * Double.BYTES, + MemorySegment.ofArray(base), index, + (long) length * Double.BYTES); } /** @@ -1402,26 +1402,4 @@ private static String identityToString(Object o) } return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o)); } - - private void copyFromBase(int index, Object dest, long destAddress, int length) - { - int baseAddress = ARRAY_BYTE_BASE_OFFSET + baseOffset + index; - // The Unsafe Javadoc specifies that the transfer size is 8 iff length % 8 == 0 - // so ensure that we copy big chunks whenever possible, even at the expense of two separate copy operations - // todo the optimization only works if the baseOffset is is a multiple of 8 for both src and dest - int bytesToCopy = length - (length % 8); - unsafe.copyMemory(base, baseAddress, dest, destAddress, bytesToCopy); - unsafe.copyMemory(base, baseAddress + bytesToCopy, dest, destAddress + bytesToCopy, length - bytesToCopy); - } - - private void copyToBase(int index, Object src, long srcAddress, int length) - { - int baseAddress = ARRAY_BYTE_BASE_OFFSET + baseOffset + index; - // The Unsafe Javadoc specifies that the transfer size is 8 iff length % 8 == 0 - // so ensure that we copy big chunks whenever possible, even at the expense of two separate copy operations - // todo the optimization only works if the baseOffset is is a multiple of 8 for both src and dest - int bytesToCopy = length - (length % 8); - unsafe.copyMemory(src, srcAddress, base, baseAddress, bytesToCopy); - unsafe.copyMemory(src, srcAddress + bytesToCopy, base, baseAddress + bytesToCopy, length - bytesToCopy); - } } diff --git a/src/test/java/io/airlift/slice/MemoryCopyBenchmark.java b/src/test/java/io/airlift/slice/MemoryCopyBenchmark.java index 72fa145..a360b25 100644 --- a/src/test/java/io/airlift/slice/MemoryCopyBenchmark.java +++ b/src/test/java/io/airlift/slice/MemoryCopyBenchmark.java @@ -23,12 +23,14 @@ import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.results.format.ResultFormatType; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.runner.options.VerboseMode; +import java.lang.foreign.MemorySegment; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; @@ -38,8 +40,8 @@ @SuppressWarnings("restriction") @BenchmarkMode(Mode.Throughput) @Fork(1) -@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) -@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Warmup(iterations = 10, time = 100, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 100, timeUnit = TimeUnit.MILLISECONDS) public class MemoryCopyBenchmark { private static final int PAGE_SIZE = 4 * 1024; @@ -65,10 +67,12 @@ public static class Buffers "SLICE", "CUSTOM_LOOP", "UNSAFE", + "FFM", }) public CopyStrategy copyStrategy; Slice data; + MemorySegment segment; long startOffset; long destOffset; @@ -79,6 +83,7 @@ public void fillWithBogusData() for (int idx = 0; idx < data.length() / 8; idx++) { data.setLong(idx, ThreadLocalRandom.current().nextLong()); } + segment = MemorySegment.ofArray(data.byteArray()); long startOffsetPages = ThreadLocalRandom.current().nextInt(N_PAGES / 4); long destOffsetPages = ThreadLocalRandom.current().nextInt(N_PAGES / 4) + N_PAGES / 2; @@ -91,7 +96,7 @@ public void fillWithBogusData() @Benchmark public Slice copy(Buffers buffers) { - buffers.copyStrategy.doCopy(buffers.data, buffers.startOffset, buffers.destOffset, buffers.size); + buffers.copyStrategy.doCopy(buffers.data, buffers.segment, buffers.startOffset, buffers.destOffset, buffers.size); return buffers.data; } @@ -99,7 +104,7 @@ public enum CopyStrategy { ARRAY_COPY { @Override - public void doCopy(Slice data, long src, long dest, int length) + public void doCopy(Slice data, MemorySegment segment, long src, long dest, int length) { byte[] byteArray = data.byteArray(); int byteArrayOffset = data.byteArrayOffset(); @@ -109,7 +114,7 @@ public void doCopy(Slice data, long src, long dest, int length) SLICE { @Override - public void doCopy(Slice data, long src, long dest, int length) + public void doCopy(Slice data, MemorySegment segment, long src, long dest, int length) { data.setBytes((int) dest, data, (int) src, length); } @@ -117,7 +122,7 @@ public void doCopy(Slice data, long src, long dest, int length) CUSTOM_LOOP { @Override - public void doCopy(Slice data, long src, long dest, int length) + public void doCopy(Slice data, MemorySegment segment, long src, long dest, int length) { byte[] base = data.byteArray(); long offset = data.byteArrayOffset() + ARRAY_BYTE_BASE_OFFSET; @@ -141,7 +146,7 @@ public void doCopy(Slice data, long src, long dest, int length) UNSAFE { @Override - public void doCopy(Slice data, long srcOffset, long destOffset, int length) + public void doCopy(Slice data, MemorySegment segment, long srcOffset, long destOffset, int length) { byte[] base = data.byteArray(); long address = data.byteArrayOffset() + ARRAY_BYTE_BASE_OFFSET; @@ -151,9 +156,17 @@ public void doCopy(Slice data, long srcOffset, long destOffset, int length) unsafe.copyMemory(base, srcOffset, base, destOffset, bytesToCopy); unsafe.copyMemory(base, srcOffset + bytesToCopy, base, destOffset + bytesToCopy, length - bytesToCopy); } + }, + + FFM { + @Override + public void doCopy(Slice data, MemorySegment segment, long srcOffset, long destOffset, int length) + { + MemorySegment.copy(segment, srcOffset, segment, destOffset, length); + } }; - public abstract void doCopy(Slice data, long src, long dest, int length); + public abstract void doCopy(Slice data, MemorySegment segment, long src, long dest, int length); } public static void main(String[] args) @@ -161,6 +174,7 @@ public static void main(String[] args) { Options options = new OptionsBuilder() .verbosity(VerboseMode.NORMAL) + .resultFormat(ResultFormatType.JSON) .include(".*" + MemoryCopyBenchmark.class.getSimpleName() + ".*") .build();