Ver Fonte

Use VarHandles un ByteUtils (#80442)

This change switches the use of direct shift instructions
to direct hardware memory loads when converting ints,longs,
doubles to byte[] and vice-versa.
Nikola Grcevski há 4 anos atrás
pai
commit
fb9473ac15

+ 11 - 21
server/src/main/java/org/elasticsearch/common/util/ByteUtils.java

@@ -8,12 +8,18 @@
 
 package org.elasticsearch.common.util;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+
 /** Utility methods to do byte-level encoding. These methods are biased towards little-endian byte order because it is the most
  *  common byte order and reading several bytes at once may be optimizable in the future with the help of sun.mist.Unsafe. */
 public enum ByteUtils {
     ;
 
-    public static final int MAX_BYTES_VLONG = 9;
+    public static final VarHandle LITTLE_ENDIAN_INT = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN);
+
+    public static final VarHandle LITTLE_ENDIAN_LONG = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN);
 
     /** Zig-zag decode. */
     public static long zigZagDecode(long n) {
@@ -27,38 +33,22 @@ public enum ByteUtils {
 
     /** Write a long in little-endian format. */
     public static void writeLongLE(long l, byte[] arr, int offset) {
-        for (int i = 0; i < 8; ++i) {
-            arr[offset++] = (byte) l;
-            l >>>= 8;
-        }
-        assert l == 0;
+        LITTLE_ENDIAN_LONG.set(arr, offset, l);
     }
 
     /** Write a long in little-endian format. */
     public static long readLongLE(byte[] arr, int offset) {
-        long l = arr[offset++] & 0xFFL;
-        for (int i = 1; i < 8; ++i) {
-            l |= (arr[offset++] & 0xFFL) << (8 * i);
-        }
-        return l;
+        return (long) LITTLE_ENDIAN_LONG.get(arr, offset);
     }
 
     /** Write an int in little-endian format. */
     public static void writeIntLE(int l, byte[] arr, int offset) {
-        for (int i = 0; i < 4; ++i) {
-            arr[offset++] = (byte) l;
-            l >>>= 8;
-        }
-        assert l == 0;
+        LITTLE_ENDIAN_INT.set(arr, offset, l);
     }
 
     /** Read an int in little-endian format. */
     public static int readIntLE(byte[] arr, int offset) {
-        int l = arr[offset++] & 0xFF;
-        for (int i = 1; i < 4; ++i) {
-            l |= (arr[offset++] & 0xFF) << (8 * i);
-        }
-        return l;
+        return (int) LITTLE_ENDIAN_INT.get(arr, offset);
     }
 
     /** Write a double in little-endian format. */

+ 59 - 0
server/src/test/java/org/elasticsearch/common/util/ByteUtilsTests.java

@@ -12,6 +12,8 @@ import org.elasticsearch.test.ESTestCase;
 
 import java.io.IOException;
 
+import static org.hamcrest.Matchers.is;
+
 public class ByteUtilsTests extends ESTestCase {
 
     public void testZigZag(long l) {
@@ -55,4 +57,61 @@ public class ByteUtilsTests extends ESTestCase {
         }
     }
 
+    private byte[] readLongLEHelper(long number, int offset) {
+        byte[] arr = new byte[8];
+        ByteUtils.writeLongLE(number, arr, offset);
+        return arr;
+    }
+
+    public void testLongToBytes() {
+        assertThat(readLongLEHelper(123456L, 0), is(new byte[] { 64, -30, 1, 0, 0, 0, 0, 0 }));
+        assertThat(readLongLEHelper(-123456L, 0), is(new byte[] { -64, 29, -2, -1, -1, -1, -1, -1 }));
+        assertThat(readLongLEHelper(0L, 0), is(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }));
+        assertThat(readLongLEHelper(Long.MAX_VALUE + 1, 0), is(new byte[] { 0, 0, 0, 0, 0, 0, 0, -128 }));
+        assertThat(readLongLEHelper(Long.MAX_VALUE + 127, 0), is(new byte[] { 126, 0, 0, 0, 0, 0, 0, -128 }));
+        assertThat(readLongLEHelper(Long.MIN_VALUE - 1, 0), is(new byte[] { -1, -1, -1, -1, -1, -1, -1, 127 }));
+        assertThat(readLongLEHelper(Long.MIN_VALUE - 127, 0), is(new byte[] { -127, -1, -1, -1, -1, -1, -1, 127 }));
+    }
+
+    public void testBytesToLong() {
+        assertThat(ByteUtils.readLongLE(new byte[] { 64, -30, 1, 0, 0, 0, 0, 0 }, 0), is(123456L));
+        assertThat(ByteUtils.readLongLE(new byte[] { -64, 29, -2, -1, -1, -1, -1, -1 }, 0), is(-123456L));
+        assertThat(ByteUtils.readLongLE(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, 0), is(0L));
+        assertThat(ByteUtils.readLongLE(new byte[] { 0, 0, 0, 0, 0, 0, 0, -128 }, 0), is(Long.MIN_VALUE));
+        assertThat(ByteUtils.readLongLE(new byte[] { 126, 0, 0, 0, 0, 0, 0, -128 }, 0), is(Long.MIN_VALUE + 127 - 1));
+        assertThat(ByteUtils.readLongLE(new byte[] { -1, -1, -1, -1, -1, -1, -1, 127 }, 0), is(Long.MAX_VALUE));
+        assertThat(ByteUtils.readLongLE(new byte[] { -127, -1, -1, -1, -1, -1, -1, 127, 0 }, 0), is(Long.MAX_VALUE - 127 + 1));
+
+        assertThat(ByteUtils.readLongLE(new byte[] { 100, 64, -30, 1, 0, 0, 0, 0, 0 }, 1), is(123456L));
+        assertThat(ByteUtils.readLongLE(new byte[] { -100, -64, 29, -2, -1, -1, -1, -1, -1 }, 1), is(-123456L));
+    }
+
+    private byte[] readIntLEHelper(int number, int offset) {
+        byte[] arr = new byte[4];
+        ByteUtils.writeIntLE(number, arr, offset);
+        return arr;
+    }
+
+    public void testIntToBytes() {
+        assertThat(readIntLEHelper(123456, 0), is(new byte[] { 64, -30, 1, 0 }));
+        assertThat(readIntLEHelper(-123456, 0), is(new byte[] { -64, 29, -2, -1 }));
+        assertThat(readIntLEHelper(0, 0), is(new byte[] { 0, 0, 0, 0 }));
+        assertThat(readIntLEHelper(Integer.MAX_VALUE + 1, 0), is(new byte[] { 0, 0, 0, -128 }));
+        assertThat(readIntLEHelper(Integer.MAX_VALUE + 127, 0), is(new byte[] { 126, 0, 0, -128 }));
+        assertThat(readIntLEHelper(Integer.MIN_VALUE - 1, 0), is(new byte[] { -1, -1, -1, 127 }));
+        assertThat(readIntLEHelper(Integer.MIN_VALUE - 127, 0), is(new byte[] { -127, -1, -1, 127 }));
+    }
+
+    public void testBytesToInt() {
+        assertThat(ByteUtils.readIntLE(new byte[] { 64, -30, 1, 0 }, 0), is(123456));
+        assertThat(ByteUtils.readIntLE(new byte[] { -64, 29, -2, -1 }, 0), is(-123456));
+        assertThat(ByteUtils.readIntLE(new byte[] { 0, 0, 0, 0 }, 0), is(0));
+        assertThat(ByteUtils.readIntLE(new byte[] { 0, 0, 0, -128 }, 0), is(Integer.MIN_VALUE));
+        assertThat(ByteUtils.readIntLE(new byte[] { 126, 0, 0, -128 }, 0), is(Integer.MIN_VALUE + 127 - 1));
+        assertThat(ByteUtils.readIntLE(new byte[] { -1, -1, -1, 127 }, 0), is(Integer.MAX_VALUE));
+        assertThat(ByteUtils.readIntLE(new byte[] { -127, -1, -1, 127, 0 }, 0), is(Integer.MAX_VALUE - 127 + 1));
+
+        assertThat(ByteUtils.readIntLE(new byte[] { 100, 64, -30, 1, 0 }, 1), is(123456));
+        assertThat(ByteUtils.readIntLE(new byte[] { -100, -64, 29, -2, -1 }, 1), is(-123456));
+    }
 }