diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java index 1cbee83..f6c4fe5 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/ByteBufferUtils.java @@ -22,13 +22,20 @@ import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Field; import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.security.AccessController; +import java.security.PrivilegedAction; import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.WritableUtils; +import sun.misc.Unsafe; +import sun.nio.ch.DirectBuffer; + /** * Utility functions for working with byte buffers, such as reading/writing * variable-length long numbers. @@ -476,9 +483,8 @@ public final class ByteBufferUtils { } public static int compareTo(ByteBuffer buf1, int o1, int len1, ByteBuffer buf2, int o2, int len2) { - if (buf1.hasArray() && buf2.hasArray()) { - return Bytes.compareTo(buf1.array(), buf1.arrayOffset() + o1, len1, buf2.array(), - buf2.arrayOffset() + o2, len2); + if (UnsafeUtil.isAvailable()) { + return UnsafeUtil.compareTo(buf1, o1, len1, buf2, o2, len2); } int end1 = o1 + len1; int end2 = o2 + len2; @@ -491,4 +497,131 @@ public final class ByteBufferUtils { } return len1 - len2; } + + static class UnsafeUtil { + static final Unsafe theUnsafe; + + /** The offset to the first element in a byte array. */ + static long BYTE_ARRAY_BASE_OFFSET; + + static { + theUnsafe = (Unsafe) AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + return f.get(null); + } catch (Exception e) { + return null; + } + } + }); + if (theUnsafe != null) + BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); + } + + static final boolean littleEndian = ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN); + + static boolean isAvailable() { + return theUnsafe != null; + } + + static boolean lessThanUnsignedLong(long x1, long x2) { + if (littleEndian) { + x1 = Long.reverseBytes(x1); + x2 = Long.reverseBytes(x2); + } + return (x1 + Long.MIN_VALUE) < (x2 + Long.MIN_VALUE); + } + + static boolean lessThanUnsignedInt(int x1, int x2) { + if (littleEndian) { + x1 = Integer.reverseBytes(x1); + x2 = Integer.reverseBytes(x2); + } + return (x1 & 0xffffffffL) < (x2 & 0xffffffffL); + } + + static boolean lessThanUnsignedShort(short x1, short x2) { + if (littleEndian) { + x1 = Short.reverseBytes(x1); + x2 = Short.reverseBytes(x2); + } + return (x1 & 0xffff) < (x2 & 0xffff); + } + + static long getLong(ByteBuffer buf, int offset) { + if (buf.isDirect()) { + return theUnsafe.getLong(((DirectBuffer) buf).address() + offset); + } else { + assert buf.hasArray(); + return theUnsafe.getLong(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset); + } + } + + static int getInt(ByteBuffer buf, int offset) { + if (buf.isDirect()) { + return theUnsafe.getInt(((DirectBuffer) buf).address() + offset); + } else { + assert buf.hasArray(); + return theUnsafe.getInt(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset); + } + } + + static short getShort(ByteBuffer buf, int offset) { + if (buf.isDirect()) { + return theUnsafe.getShort(((DirectBuffer) buf).address() + offset); + } else { + assert buf.hasArray(); + return theUnsafe.getShort(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset); + } + } + + static int compareTo(ByteBuffer buffer1, int offset1, int length1, ByteBuffer buffer2, + int offset2, int length2) { + final int minLength = Math.min(length1, length2); + final int minWords = minLength / Bytes.SIZEOF_LONG; + + /* + * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a time is no slower than + * comparing 4 bytes at a time even on 32-bit. On the other hand, it is substantially faster + * on 64-bit. + */ + for (int i = 0; i < minWords * Bytes.SIZEOF_LONG; i += Bytes.SIZEOF_LONG) { + long lw = getLong(buffer1, offset1 + i); + long rw = getLong(buffer2, offset2 + i); + long diff = lw ^ rw; + if (diff != 0) { + return lessThanUnsignedLong(lw, rw) ? -1 : 1; + } + } + int offset = minWords * Bytes.SIZEOF_LONG; + + if (minLength - offset >= Bytes.SIZEOF_INT) { + int il = getInt(buffer1, offset1 + offset); + int ir = getInt(buffer2, offset2 + offset); + if (il != ir) { + return lessThanUnsignedInt(il, ir) ? -1 : 1; + } + offset += Bytes.SIZEOF_INT; + } + if (minLength - offset >= Bytes.SIZEOF_SHORT) { + short sl = getShort(buffer1, offset1 + offset); + short sr = getShort(buffer2, offset2 + offset); + if (sl != sr) { + return lessThanUnsignedShort(sl, sr) ? -1 : 1; + } + offset += Bytes.SIZEOF_SHORT; + } + if (minLength - offset == 1) { + int a = (buffer1.get(offset1 + offset) & 0xff); + int b = (buffer2.get(offset2 + offset) & 0xff); + if (a != b) { + return a - b; + } + } + return length1 - length2; + } + } }