From 385591963fa2fe0a06f16b839b9390fa055afb2a Mon Sep 17 00:00:00 2001 From: "Mateusz \"Serafin\" Gajewski" Date: Sat, 11 May 2024 17:26:12 +0200 Subject: [PATCH] Move Slice to use MemorySegment --- src/main/java/io/airlift/slice/JvmUtils.java | 82 ----- src/main/java/io/airlift/slice/SizeOf.java | 19 ++ src/main/java/io/airlift/slice/Slice.java | 282 +++++++----------- src/main/java/io/airlift/slice/XxHash64.java | 113 +++---- .../io/airlift/slice/MemoryCopyBenchmark.java | 20 +- src/test/java/io/airlift/slice/TestSlice.java | 2 +- 6 files changed, 197 insertions(+), 321 deletions(-) delete mode 100644 src/main/java/io/airlift/slice/JvmUtils.java diff --git a/src/main/java/io/airlift/slice/JvmUtils.java b/src/main/java/io/airlift/slice/JvmUtils.java deleted file mode 100644 index 488e28fd..00000000 --- a/src/main/java/io/airlift/slice/JvmUtils.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.airlift.slice; - -import sun.misc.Unsafe; - -import java.lang.reflect.Field; -import java.nio.Buffer; -import java.nio.ByteOrder; - -import static io.airlift.slice.Preconditions.checkArgument; -import static sun.misc.Unsafe.ARRAY_BOOLEAN_INDEX_SCALE; -import static sun.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE; -import static sun.misc.Unsafe.ARRAY_DOUBLE_INDEX_SCALE; -import static sun.misc.Unsafe.ARRAY_FLOAT_INDEX_SCALE; -import static sun.misc.Unsafe.ARRAY_INT_INDEX_SCALE; -import static sun.misc.Unsafe.ARRAY_LONG_INDEX_SCALE; -import static sun.misc.Unsafe.ARRAY_SHORT_INDEX_SCALE; - -final class JvmUtils -{ - static final Unsafe unsafe; - private static final long ADDRESS_OFFSET; - - static { - if (!ByteOrder.LITTLE_ENDIAN.equals(ByteOrder.nativeOrder())) { - throw new UnsupportedOperationException("Slice only supports little endian machines."); - } - - try { - // fetch theUnsafe object - Field field = Unsafe.class.getDeclaredField("theUnsafe"); - field.setAccessible(true); - unsafe = (Unsafe) field.get(null); - if (unsafe == null) { - throw new RuntimeException("Unsafe access not available"); - } - - // verify the stride of arrays matches the width of primitives - assertArrayIndexScale("Boolean", ARRAY_BOOLEAN_INDEX_SCALE, 1); - assertArrayIndexScale("Byte", ARRAY_BYTE_INDEX_SCALE, 1); - assertArrayIndexScale("Short", ARRAY_SHORT_INDEX_SCALE, 2); - assertArrayIndexScale("Int", ARRAY_INT_INDEX_SCALE, 4); - assertArrayIndexScale("Long", ARRAY_LONG_INDEX_SCALE, 8); - assertArrayIndexScale("Float", ARRAY_FLOAT_INDEX_SCALE, 4); - assertArrayIndexScale("Double", ARRAY_DOUBLE_INDEX_SCALE, 8); - - // fetch the address field for direct buffers - ADDRESS_OFFSET = unsafe.objectFieldOffset(Buffer.class.getDeclaredField("address")); - } - catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - private static void assertArrayIndexScale(String name, int actualIndexScale, int expectedIndexScale) - { - if (actualIndexScale != expectedIndexScale) { - throw new IllegalStateException(name + " array index scale must be " + expectedIndexScale + ", but is " + actualIndexScale); - } - } - - static long bufferAddress(Buffer buffer) - { - checkArgument(buffer.isDirect(), "buffer is not direct"); - - return unsafe.getLong(buffer, ADDRESS_OFFSET); - } - - private JvmUtils() {} -} diff --git a/src/main/java/io/airlift/slice/SizeOf.java b/src/main/java/io/airlift/slice/SizeOf.java index f32f7885..29c29014 100644 --- a/src/main/java/io/airlift/slice/SizeOf.java +++ b/src/main/java/io/airlift/slice/SizeOf.java @@ -20,6 +20,7 @@ import org.openjdk.jol.vm.VM; import org.openjdk.jol.vm.VirtualMachine; +import java.lang.foreign.MemorySegment; import java.util.AbstractMap; import java.util.List; import java.util.Map; @@ -79,6 +80,24 @@ public final class SizeOf private static final int SIMPLE_ENTRY_INSTANCE_SIZE = instanceSize(AbstractMap.SimpleEntry.class); + public static long sizeOf(MemorySegment segment) + { + // final long length; + // final boolean readOnly; + // final MemorySessionImpl scope (int state); + long size = LONG_INSTANCE_SIZE + BOOLEAN_INSTANCE_SIZE + INTEGER_INSTANCE_SIZE; + if (segment.isNative()) { + return size + + LONG_INSTANCE_SIZE; // addr; + } + + return size + + LONG_INSTANCE_SIZE // offset + + segment.heapBase() // base + .map(value -> sizeOfByteArray(((byte[]) value).length)) + .orElse(0L); + } + public static long sizeOf(boolean[] array) { return (array == null) ? 0 : sizeOfBooleanArray(array.length); diff --git a/src/main/java/io/airlift/slice/Slice.java b/src/main/java/io/airlift/slice/Slice.java index fe7c3889..7c42f027 100644 --- a/src/main/java/io/airlift/slice/Slice.java +++ b/src/main/java/io/airlift/slice/Slice.java @@ -18,33 +18,20 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.lang.invoke.VarHandle; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; 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; -import static io.airlift.slice.SizeOf.SIZE_OF_FLOAT; -import static io.airlift.slice.SizeOf.SIZE_OF_INT; -import static io.airlift.slice.SizeOf.SIZE_OF_LONG; -import static io.airlift.slice.SizeOf.SIZE_OF_SHORT; import static io.airlift.slice.SizeOf.instanceSize; import static io.airlift.slice.SizeOf.sizeOf; -import static java.lang.invoke.MethodHandles.byteArrayViewVarHandle; +import static java.lang.Math.toIntExact; import static java.nio.ByteOrder.LITTLE_ENDIAN; 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 @@ -52,18 +39,23 @@ public final class Slice private static final int INSTANCE_SIZE = instanceSize(Slice.class); private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0); - private static final VarHandle SHORT_HANDLE = byteArrayViewVarHandle(short[].class, LITTLE_ENDIAN); - private static final VarHandle INT_HANDLE = byteArrayViewVarHandle(int[].class, LITTLE_ENDIAN); - private static final VarHandle LONG_HANDLE = byteArrayViewVarHandle(long[].class, LITTLE_ENDIAN); - private static final VarHandle FLOAT_HANDLE = byteArrayViewVarHandle(float[].class, LITTLE_ENDIAN); - private static final VarHandle DOUBLE_HANDLE = byteArrayViewVarHandle(double[].class, LITTLE_ENDIAN); + private static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE.withOrder(LITTLE_ENDIAN); + private static final ValueLayout.OfShort SHORT = ValueLayout.JAVA_SHORT_UNALIGNED.withOrder(LITTLE_ENDIAN); + private static final ValueLayout.OfInt INT = ValueLayout.JAVA_INT_UNALIGNED.withOrder(LITTLE_ENDIAN); + private static final ValueLayout.OfLong LONG = ValueLayout.JAVA_LONG_UNALIGNED.withOrder(LITTLE_ENDIAN); + private static final ValueLayout.OfFloat FLOAT = ValueLayout.JAVA_FLOAT_UNALIGNED.withOrder(LITTLE_ENDIAN); + private static final ValueLayout.OfDouble DOUBLE = ValueLayout.JAVA_DOUBLE_UNALIGNED.withOrder(LITTLE_ENDIAN); + + private static final int SIZE_OF_INT = toIntExact(INT.byteSize()); + private static final int SIZE_OF_LONG = toIntExact(LONG.byteSize()); // Do not move this field above the constants used in the empty constructor static final Slice EMPTY_SLICE = new Slice(); - private final byte[] base; + private final MemorySegment segment; private final int baseOffset; + private final boolean compact; /** * Size of the slice @@ -84,10 +76,11 @@ private Slice() { // Since this is used to create a constant in this class, be careful to not use // other uninitialized constants. - this.base = new byte[0]; + this.segment = MemorySegment.ofArray(new byte[0]); + this.compact = true; this.baseOffset = 0; this.size = 0; - this.retainedSize = INSTANCE_SIZE; + this.retainedSize = INSTANCE_SIZE + SizeOf.sizeOf(segment); } /** @@ -95,14 +88,20 @@ private Slice() */ Slice(byte[] base) { - requireNonNull(base, "base is null"); - if (base.length == 0) { - throw new IllegalArgumentException("Empty array"); + this(MemorySegment.ofArray(base)); + } + + Slice(MemorySegment segment) + { + requireNonNull(segment, "segment is null"); + if (segment.byteSize() == 0) { + throw new IllegalArgumentException("Empty memory segment"); } - this.base = base; + this.segment = segment; + this.compact = true; this.baseOffset = 0; - this.size = base.length; - this.retainedSize = INSTANCE_SIZE + sizeOf(base); + this.size = toIntExact(segment.byteSize()); + this.retainedSize = INSTANCE_SIZE + SizeOf.sizeOf(segment); } /** @@ -113,16 +112,7 @@ private Slice() */ Slice(byte[] base, int offset, int length) { - requireNonNull(base, "base is null"); - if (base.length == 0) { - throw new IllegalArgumentException("Empty array"); - } - checkFromIndexSize(offset, length, base.length); - - this.base = base; - this.baseOffset = offset; - this.size = length; - this.retainedSize = INSTANCE_SIZE + sizeOf(base); + this(base, offset, length, INSTANCE_SIZE + sizeOf(base)); } /** @@ -130,13 +120,17 @@ private Slice() */ Slice(byte[] base, int baseOffset, int size, long retainedSize) { - requireNonNull(base, "base is null"); - if (base.length == 0) { - throw new IllegalArgumentException("Empty array"); - } - checkFromIndexSize(baseOffset, size, base.length); + this(MemorySegment.ofArray(base), baseOffset, size, retainedSize); + } - this.base = requireNonNull(base, "base is null"); + Slice(MemorySegment segment, int baseOffset, int size, long retainedSize) + { + requireNonNull(segment, "segment is null"); + if (segment.byteSize() == 0) { + throw new IllegalArgumentException("Empty memory segment"); + } + this.compact = baseOffset == 0 && segment.byteSize() == size; + this.segment = segment.asSlice(baseOffset, size); this.baseOffset = baseOffset; this.size = size; // INSTANCE_SIZE is not included, as the caller is responsible for including it. @@ -165,7 +159,7 @@ public long getRetainedSize() */ public boolean isCompact() { - return baseOffset == 0 && size == base.length; + return compact; } /** @@ -175,7 +169,14 @@ public boolean isCompact() @SuppressFBWarnings("EI_EXPOSE_REP") public byte[] byteArray() { - return base; + return segment.heapBase() + .map(value -> (byte[]) value) + .orElseThrow(); + } + + public MemorySegment asSegment() + { + return segment; } /** @@ -191,7 +192,7 @@ public int byteArrayOffset() */ public void fill(byte value) { - Arrays.fill(base, baseOffset, baseOffset + size, value); + segment.fill(value); } /** @@ -204,7 +205,7 @@ public void clear() public void clear(int offset, int length) { - Arrays.fill(base, baseOffset, baseOffset + size, (byte) 0); + segment.asSlice(offset, length).fill((byte) 0); } /** @@ -215,13 +216,12 @@ public void clear(int offset, int length) */ public byte getByte(int index) { - checkFromIndexSize(index, SIZE_OF_BYTE, length()); return getByteUnchecked(index); } public byte getByteUnchecked(int index) { - return base[baseOffset + index]; + return segment.get(BYTE, index); } /** @@ -245,13 +245,12 @@ public short getUnsignedByte(int index) */ public short getShort(int index) { - checkFromIndexSize(index, SIZE_OF_SHORT, length()); return getShortUnchecked(index); } public short getShortUnchecked(int index) { - return (short) SHORT_HANDLE.get(base, baseOffset + index); + return segment.get(SHORT, index); } /** @@ -275,13 +274,12 @@ public int getUnsignedShort(int index) */ public int getInt(int index) { - checkFromIndexSize(index, SIZE_OF_INT, length()); return getIntUnchecked(index); } public int getIntUnchecked(int index) { - return (int) INT_HANDLE.get(base, baseOffset + index); + return segment.get(INT, index); } /** @@ -305,13 +303,12 @@ public long getUnsignedInt(int index) */ public long getLong(int index) { - checkFromIndexSize(index, SIZE_OF_LONG, length()); return getLongUnchecked(index); } public long getLongUnchecked(int index) { - return (long) LONG_HANDLE.get(base, baseOffset + index); + return segment.get(LONG, index); } /** @@ -323,13 +320,12 @@ public long getLongUnchecked(int index) */ public float getFloat(int index) { - checkFromIndexSize(index, SIZE_OF_FLOAT, length()); return getFloatUnchecked(index); } public float getFloatUnchecked(int index) { - return (float) FLOAT_HANDLE.get(base, baseOffset + index); + return segment.get(FLOAT, index); } /** @@ -341,13 +337,12 @@ public float getFloatUnchecked(int index) */ public double getDouble(int index) { - checkFromIndexSize(index, SIZE_OF_DOUBLE, length()); return getDoubleUnchecked(index); } public double getDoubleUnchecked(int index) { - return (double) DOUBLE_HANDLE.get(base, baseOffset + index); + return segment.get(DOUBLE, index); } /** @@ -377,10 +372,7 @@ 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(segment, index, destination.segment, destinationIndex, length); } /** @@ -410,10 +402,7 @@ 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(segment, BYTE, index, destination, destinationIndex, length); } /** @@ -498,10 +487,7 @@ 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(segment, SHORT, index, destination, destinationIndex, length); } /** @@ -546,10 +532,7 @@ 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(segment, INT, index, destination, destinationIndex, length); } /** @@ -594,10 +577,7 @@ 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(segment, LONG, index, destination, destinationIndex, length); } /** @@ -642,10 +622,7 @@ 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(segment, FLOAT, index, destination, destinationIndex, length); } /** @@ -690,10 +667,7 @@ 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(segment, DOUBLE, index, destination, destinationIndex, length); } /** @@ -705,13 +679,12 @@ public void getDoubles(int index, double[] destination, int destinationIndex, in */ public void setByte(int index, int value) { - checkFromIndexSize(index, SIZE_OF_BYTE, length()); setByteUnchecked(index, value); } void setByteUnchecked(int index, int value) { - base[baseOffset + index] = (byte) (value & 0xFF); + segment.set(BYTE, index, (byte) (value & 0xFF)); } /** @@ -724,13 +697,12 @@ void setByteUnchecked(int index, int value) */ public void setShort(int index, int value) { - checkFromIndexSize(index, SIZE_OF_SHORT, length()); setShortUnchecked(index, value); } void setShortUnchecked(int index, int value) { - SHORT_HANDLE.set(base, baseOffset + index, (short) (value & 0xFFFF)); + segment.set(SHORT, index, (short) (value & 0xFFFF)); } /** @@ -742,13 +714,12 @@ void setShortUnchecked(int index, int value) */ public void setInt(int index, int value) { - checkFromIndexSize(index, SIZE_OF_INT, length()); setIntUnchecked(index, value); } void setIntUnchecked(int index, int value) { - INT_HANDLE.set(base, baseOffset + index, value); + segment.set(INT, index, value); } /** @@ -760,13 +731,12 @@ void setIntUnchecked(int index, int value) */ public void setLong(int index, long value) { - checkFromIndexSize(index, SIZE_OF_LONG, length()); setLongUnchecked(index, value); } void setLongUnchecked(int index, long value) { - LONG_HANDLE.set(base, baseOffset + index, value); + segment.set(LONG, index, value); } /** @@ -778,8 +748,7 @@ void setLongUnchecked(int index, long value) */ public void setFloat(int index, float value) { - checkFromIndexSize(index, SIZE_OF_FLOAT, length()); - FLOAT_HANDLE.set(base, baseOffset + index, value); + segment.set(FLOAT, index, value); } /** @@ -791,8 +760,7 @@ public void setFloat(int index, float value) */ public void setDouble(int index, double value) { - checkFromIndexSize(index, SIZE_OF_DOUBLE, length()); - DOUBLE_HANDLE.set(base, baseOffset + index, value); + segment.set(DOUBLE, index, value); } /** @@ -822,10 +790,7 @@ 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(source.segment, sourceIndex, segment, index, length); } /** @@ -852,9 +817,7 @@ 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(source, sourceIndex, segment, BYTE, index, length); } /** @@ -904,9 +867,7 @@ 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(source, sourceIndex, segment, SHORT, index, length); } /** @@ -933,9 +894,7 @@ 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(source, sourceIndex, segment, INT, index, length); } /** @@ -962,9 +921,7 @@ 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(source, sourceIndex, segment, LONG, index, length); } /** @@ -991,9 +948,7 @@ 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(source, sourceIndex, segment, FLOAT, index, length); } /** @@ -1020,9 +975,7 @@ 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(source, sourceIndex, segment, DOUBLE, index, length); } /** @@ -1039,7 +992,7 @@ public Slice slice(int index, int length) return Slices.EMPTY_SLICE; } - return new Slice(base, baseOffset + index, length, retainedSize); + return new Slice(segment, index, length, retainedSize); } /** @@ -1051,7 +1004,10 @@ public Slice copy() if (size == 0) { return Slices.EMPTY_SLICE; } - return new Slice(Arrays.copyOfRange(base, baseOffset, baseOffset + size)); + MemorySegment copy = MemorySegment.ofArray(new byte[size]); + MemorySegment.copy(segment, 0, copy, 0, size); + // Calculate SizeOf MemorySegment correctly + return new Slice(copy, 0, size, 0); } /** @@ -1064,7 +1020,10 @@ public Slice copy(int index, int length) if (length == 0) { return Slices.EMPTY_SLICE; } - return new Slice(Arrays.copyOfRange(base, baseOffset + index, baseOffset + index + length)); + MemorySegment copy = MemorySegment.ofArray(new byte[length]); + MemorySegment.copy(segment, index, copy, 0, length); + // Calculate SizeOf MemorySegment correctly + return new Slice(copy, 0, length, 0); } public int indexOfByte(int b) @@ -1208,16 +1167,18 @@ public int compareTo(int offset, int length, Slice that, int otherOffset, int ot return 0; } - checkFromIndexSize(offset, length, length()); - checkFromIndexSize(otherOffset, otherLength, that.length()); - - return Arrays.compareUnsigned( - base, - baseOffset + offset, - baseOffset + offset + length, - that.base, - that.baseOffset + otherOffset, - that.baseOffset + otherOffset + otherLength); + // Find index of the first mismatched byte + long mismatch = MemorySegment.mismatch(segment, offset, offset + length, that.segment, otherOffset, otherOffset + otherLength); + if (mismatch == -1) { + return 0; + } + if (mismatch >= length) { + return -1; + } + if (mismatch >= otherLength) { + return 1; + } + return Byte.compareUnsigned(segment.get(BYTE, offset + mismatch), that.segment.get(BYTE, otherOffset + mismatch)); } /** @@ -1280,21 +1241,12 @@ public boolean equals(int offset, int length, Slice that, int otherOffset, int o return true; } - checkFromIndexSize(offset, length, length()); - checkFromIndexSize(otherOffset, otherLength, that.length()); - return equalsUnchecked(offset, that, otherOffset, length); } boolean equalsUnchecked(int offset, Slice that, int otherOffset, int length) { - return Arrays.equals( - base, - baseOffset + offset, - baseOffset + offset + length, - that.base, - that.baseOffset + otherOffset, - that.baseOffset + otherOffset + length); + return MemorySegment.mismatch(segment, offset, offset + length, that.segment, otherOffset, otherOffset + length) == -1; } /** @@ -1372,13 +1324,11 @@ public ByteBuffer toByteBuffer() public ByteBuffer toByteBuffer(int index, int length) { - checkFromIndexSize(index, length, length()); - if (length() == 0) { return EMPTY_BYTE_BUFFER; } - return ByteBuffer.wrap(byteArray(), byteArrayOffset() + index, length).slice(); + return segment.asSlice(index, length).asByteBuffer(); } /** @@ -1388,40 +1338,10 @@ public ByteBuffer toByteBuffer(int index, int length) public String toString() { StringBuilder builder = new StringBuilder("Slice{"); - builder.append("base=").append(identityToString(base)).append(", "); + builder.append("segment=").append(segment.address()).append(", "); builder.append("baseOffset=").append(baseOffset); builder.append(", length=").append(length()); builder.append('}'); return builder.toString(); } - - private static String identityToString(Object o) - { - if (o == null) { - return null; - } - 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/main/java/io/airlift/slice/XxHash64.java b/src/main/java/io/airlift/slice/XxHash64.java index 3d0b4eb5..e167c3cd 100644 --- a/src/main/java/io/airlift/slice/XxHash64.java +++ b/src/main/java/io/airlift/slice/XxHash64.java @@ -15,12 +15,13 @@ import java.io.IOException; import java.io.InputStream; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; -import static io.airlift.slice.JvmUtils.unsafe; import static java.lang.Long.rotateLeft; import static java.lang.Math.min; -import static java.util.Objects.checkFromIndexSize; -import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; +import static java.lang.Math.toIntExact; +import static java.nio.ByteOrder.LITTLE_ENDIAN; public final class XxHash64 { @@ -34,8 +35,12 @@ public final class XxHash64 private final long seed; - private static final long BUFFER_ADDRESS = ARRAY_BYTE_BASE_OFFSET; - private final byte[] buffer = new byte[32]; + private static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE.withOrder(LITTLE_ENDIAN); + private static final ValueLayout.OfInt INT = ValueLayout.JAVA_INT_UNALIGNED.withOrder(LITTLE_ENDIAN); + private static final ValueLayout.OfLong LONG = ValueLayout.JAVA_LONG_UNALIGNED.withOrder(LITTLE_ENDIAN); + + private final MemorySegment buffer = MemorySegment.ofArray(new byte[32]); + private int bufferSize; private long bodyLength; @@ -66,8 +71,7 @@ public XxHash64 update(byte[] data) public XxHash64 update(byte[] data, int offset, int length) { - checkFromIndexSize(offset, length, data.length); - updateHash(data, ARRAY_BYTE_BASE_OFFSET + offset, length); + updateHash(MemorySegment.ofArray(data), offset, length); return this; } @@ -76,10 +80,20 @@ public XxHash64 update(Slice data) return update(data, 0, data.length()); } + public XxHash64 update(MemorySegment segment) + { + return update(segment, 0, toIntExact(segment.byteSize())); + } + public XxHash64 update(Slice data, int offset, int length) { - checkFromIndexSize(offset, length, data.length()); - updateHash(data.byteArray(), (long) data.byteArrayOffset() + ARRAY_BYTE_BASE_OFFSET + offset, length); + updateHash(data.asSegment(), offset, length); + return this; + } + + public XxHash64 update(MemorySegment data, int offset, int length) + { + updateHash(data, offset, length); return this; } @@ -95,60 +109,58 @@ public long hash() hash += bodyLength + bufferSize; - return updateTail(hash, buffer, BUFFER_ADDRESS, 0, bufferSize); + return updateTail(hash, buffer, 0, 0, bufferSize); } private long computeBody() { long hash = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); - hash = update(hash, v1); - hash = update(hash, v2); - hash = update(hash, v3); - hash = update(hash, v4); + hash = (hash ^ mix(0, v1)) * PRIME64_1 + PRIME64_4; + hash = (hash ^ mix(0, v2)) * PRIME64_1 + PRIME64_4; + hash = (hash ^ mix(0, v3)) * PRIME64_1 + PRIME64_4; + hash = (hash ^ mix(0, v4)) * PRIME64_1 + PRIME64_4; return hash; } - private void updateHash(byte[] base, long address, int length) + private void updateHash(MemorySegment base, int offset, int length) { if (bufferSize > 0) { int available = min(32 - bufferSize, length); - - unsafe.copyMemory(base, address, buffer, BUFFER_ADDRESS + bufferSize, available); - + MemorySegment.copy(base, offset, buffer, bufferSize, available); bufferSize += available; - address += available; + offset += available; length -= available; if (bufferSize == 32) { - updateBody(buffer, BUFFER_ADDRESS, bufferSize); + updateBody(buffer, 0, bufferSize); bufferSize = 0; } } if (length >= 32) { - int index = updateBody(base, address, length); - address += index; + int index = updateBody(base, offset, length); + offset += index; length -= index; } if (length > 0) { - unsafe.copyMemory(base, address, buffer, BUFFER_ADDRESS, length); + MemorySegment.copy(base, offset, buffer, 0, length); bufferSize = length; } } - private int updateBody(byte[] base, long address, int length) + private int updateBody(MemorySegment base, long offset, int length) { int remaining = length; while (remaining >= 32) { - v1 = mix(v1, unsafe.getLong(base, address)); - v2 = mix(v2, unsafe.getLong(base, address + 8)); - v3 = mix(v3, unsafe.getLong(base, address + 16)); - v4 = mix(v4, unsafe.getLong(base, address + 24)); + v1 = mix(v1, base.get(LONG, offset)); + v2 = mix(v2, base.get(LONG, offset + 8)); + v3 = mix(v3, base.get(LONG, offset + 16)); + v4 = mix(v4, base.get(LONG, offset + 24)); - address += 32; + offset += 32; remaining -= 32; } @@ -209,14 +221,9 @@ public static long hash(Slice data, int offset, int length) public static long hash(long seed, Slice data, int offset, int length) { - checkFromIndexSize(offset, length, data.length()); - - byte[] base = data.byteArray(); - final long address = (long) data.byteArrayOffset() + ARRAY_BYTE_BASE_OFFSET + offset; - long hash; if (length >= 32) { - hash = updateBody(seed, base, address, length); + hash = updateBody(seed, data.asSegment(), offset, length); } else { hash = seed + PRIME64_5; @@ -228,23 +235,23 @@ public static long hash(long seed, Slice data, int offset, int length) // this is the point up to which updateBody() processed int index = length & 0xFFFFFFE0; - return updateTail(hash, base, address, index, length); + return updateTail(hash, data.asSegment(), offset, index, length); } - private static long updateTail(long hash, byte[] base, long address, int index, int length) + private static long updateTail(long hash, MemorySegment base, int offset, int index, int length) { while (index <= length - 8) { - hash = updateTail(hash, unsafe.getLong(base, address + index)); + hash = updateTail(hash, base.get(LONG, offset + index)); index += 8; } if (index <= length - 4) { - hash = updateTail(hash, unsafe.getInt(base, address + index)); + hash = updateTail(hash, base.get(INT, offset + index)); index += 4; } while (index < length) { - hash = updateTail(hash, unsafe.getByte(base, address + index)); + hash = updateTail(hash, base.get(BYTE, offset + index)); index++; } @@ -253,7 +260,7 @@ private static long updateTail(long hash, byte[] base, long address, int index, return hash; } - private static long updateBody(long seed, byte[] base, long address, int length) + private static long updateBody(long seed, MemorySegment base, long offset, int length) { long v1 = seed + PRIME64_1 + PRIME64_2; long v2 = seed + PRIME64_2; @@ -262,21 +269,21 @@ private static long updateBody(long seed, byte[] base, long address, int length) int remaining = length; while (remaining >= 32) { - v1 = mix(v1, unsafe.getLong(base, address)); - v2 = mix(v2, unsafe.getLong(base, address + 8)); - v3 = mix(v3, unsafe.getLong(base, address + 16)); - v4 = mix(v4, unsafe.getLong(base, address + 24)); + v1 = mix(v1, base.get(LONG, offset)); + v2 = mix(v2, base.get(LONG, offset + 8)); + v3 = mix(v3, base.get(LONG, offset + 16)); + v4 = mix(v4, base.get(LONG, offset + 24)); - address += 32; + offset += 32; remaining -= 32; } long hash = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); - hash = update(hash, v1); - hash = update(hash, v2); - hash = update(hash, v3); - hash = update(hash, v4); + hash = (hash ^ mix(0, v1)) * PRIME64_1 + PRIME64_4; + hash = (hash ^ mix(0, v2)) * PRIME64_1 + PRIME64_4; + hash = (hash ^ mix(0, v3)) * PRIME64_1 + PRIME64_4; + hash = (hash ^ mix(0, v4)) * PRIME64_1 + PRIME64_4; return hash; } @@ -286,12 +293,6 @@ private static long mix(long current, long value) return rotateLeft(current + value * PRIME64_2, 31) * PRIME64_1; } - private static long update(long hash, long value) - { - long temp = hash ^ mix(0, value); - return temp * PRIME64_1 + PRIME64_4; - } - private static long updateTail(long hash, long value) { long temp = hash ^ mix(0, value); diff --git a/src/test/java/io/airlift/slice/MemoryCopyBenchmark.java b/src/test/java/io/airlift/slice/MemoryCopyBenchmark.java index 72fa1451..d559e558 100644 --- a/src/test/java/io/airlift/slice/MemoryCopyBenchmark.java +++ b/src/test/java/io/airlift/slice/MemoryCopyBenchmark.java @@ -28,11 +28,12 @@ import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.runner.options.VerboseMode; +import sun.misc.Unsafe; +import java.lang.reflect.Field; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; -import static io.airlift.slice.JvmUtils.unsafe; import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; @SuppressWarnings("restriction") @@ -42,10 +43,27 @@ @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) public class MemoryCopyBenchmark { + private static final Unsafe unsafe; + private static final int PAGE_SIZE = 4 * 1024; private static final int N_PAGES = 256 * 1024; private static final int ALLOC_SIZE = PAGE_SIZE * N_PAGES; + static { + try { + // fetch theUnsafe object + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + unsafe = (Unsafe) field.get(null); + if (unsafe == null) { + throw new RuntimeException("Unsafe access not available"); + } + } + catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + @State(Scope.Thread) public static class Buffers { diff --git a/src/test/java/io/airlift/slice/TestSlice.java b/src/test/java/io/airlift/slice/TestSlice.java index e53ee939..7d22bced 100644 --- a/src/test/java/io/airlift/slice/TestSlice.java +++ b/src/test/java/io/airlift/slice/TestSlice.java @@ -763,7 +763,7 @@ private static void assertBytesStreams(Slice slice, int index) public void testRetainedSize() throws Exception { - int sliceInstanceSize = instanceSize(Slice.class); + int sliceInstanceSize = instanceSize(Slice.class) + 80; // MemorySegment overhead Slice slice = Slices.allocate(10); assertThat(slice.getRetainedSize()).isEqualTo(sizeOfByteArray(10) + sliceInstanceSize); assertThat(slice.length()).isEqualTo(10);