HBASE-12090 Bytes: more Unsafe, more Faster. (Vladimir Rodionov)

This commit is contained in:
Lars Hofhansl 2014-09-27 19:41:13 -07:00
parent 4e56a19cf1
commit 5aeec324e7

View File

@ -40,6 +40,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.classification.InterfaceAudience;
@ -54,6 +55,7 @@ import sun.misc.Unsafe;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.apache.hadoop.hbase.util.Bytes.LexicographicalComparerHolder.UnsafeComparer;
/** /**
* Utility class that handles byte arrays, conversions to/from other types, * Utility class that handles byte arrays, conversions to/from other types,
@ -785,12 +787,16 @@ public class Bytes implements Comparable<Bytes> {
if (length != SIZEOF_LONG || offset + length > bytes.length) { if (length != SIZEOF_LONG || offset + length > bytes.length) {
throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_LONG); throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_LONG);
} }
long l = 0; if (UnsafeComparer.isAvailable()) {
for(int i = offset; i < offset + length; i++) { return toLongUnsafe(bytes, offset);
l <<= 8; } else {
l ^= bytes[i] & 0xFF; long l = 0;
for(int i = offset; i < offset + length; i++) {
l <<= 8;
l ^= bytes[i] & 0xFF;
}
return l;
} }
return l;
} }
private static IllegalArgumentException private static IllegalArgumentException
@ -822,11 +828,32 @@ public class Bytes implements Comparable<Bytes> {
throw new IllegalArgumentException("Not enough room to put a long at" throw new IllegalArgumentException("Not enough room to put a long at"
+ " offset " + offset + " in a " + bytes.length + " byte array"); + " offset " + offset + " in a " + bytes.length + " byte array");
} }
for(int i = offset + 7; i > offset; i--) { if (UnsafeComparer.isAvailable()) {
bytes[i] = (byte) val; return putLongUnsafe(bytes, offset, val);
val >>>= 8; } else {
for(int i = offset + 7; i > offset; i--) {
bytes[i] = (byte) val;
val >>>= 8;
}
bytes[offset] = (byte) val;
return offset + SIZEOF_LONG;
} }
bytes[offset] = (byte) val; }
/**
* Put a long value out to the specified byte array position (Unsafe).
* @param bytes the byte array
* @param offset position in the array
* @param val long to write out
* @return incremented offset
*/
public static int putLongUnsafe(byte[] bytes, int offset, long val)
{
if (UnsafeComparer.littleEndian) {
val = Long.reverseBytes(val);
}
UnsafeComparer.theUnsafe.putLong(bytes, (long) offset +
UnsafeComparer.BYTE_ARRAY_BASE_OFFSET , val);
return offset + SIZEOF_LONG; return offset + SIZEOF_LONG;
} }
@ -956,12 +983,64 @@ public class Bytes implements Comparable<Bytes> {
if (length != SIZEOF_INT || offset + length > bytes.length) { if (length != SIZEOF_INT || offset + length > bytes.length) {
throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_INT); throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_INT);
} }
int n = 0; if (UnsafeComparer.isAvailable()) {
for(int i = offset; i < (offset + length); i++) { return toIntUnsafe(bytes, offset);
n <<= 8; } else {
n ^= bytes[i] & 0xFF; int n = 0;
for(int i = offset; i < (offset + length); i++) {
n <<= 8;
n ^= bytes[i] & 0xFF;
}
return n;
}
}
/**
* Converts a byte array to an int value (Unsafe version)
* @param bytes byte array
* @param offset offset into array
* @return the int value
*/
public static int toIntUnsafe(byte[] bytes, int offset) {
if (UnsafeComparer.littleEndian) {
return Integer.reverseBytes(UnsafeComparer.theUnsafe.getInt(bytes,
(long) offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET));
} else {
return UnsafeComparer.theUnsafe.getInt(bytes,
(long) offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET);
}
}
/**
* Converts a byte array to an short value (Unsafe version)
* @param bytes byte array
* @param offset offset into array
* @return the short value
*/
public static short toShortUnsafe(byte[] bytes, int offset) {
if (UnsafeComparer.littleEndian) {
return Short.reverseBytes(UnsafeComparer.theUnsafe.getShort(bytes,
(long) offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET));
} else {
return UnsafeComparer.theUnsafe.getShort(bytes,
(long) offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET);
}
}
/**
* Converts a byte array to an long value (Unsafe version)
* @param bytes byte array
* @param offset offset into array
* @return the long value
*/
public static long toLongUnsafe(byte[] bytes, int offset) {
if (UnsafeComparer.littleEndian) {
return Long.reverseBytes(UnsafeComparer.theUnsafe.getLong(bytes,
(long) offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET));
} else {
return UnsafeComparer.theUnsafe.getLong(bytes,
(long) offset + UnsafeComparer.BYTE_ARRAY_BASE_OFFSET);
} }
return n;
} }
/** /**
@ -1000,11 +1079,32 @@ public class Bytes implements Comparable<Bytes> {
throw new IllegalArgumentException("Not enough room to put an int at" throw new IllegalArgumentException("Not enough room to put an int at"
+ " offset " + offset + " in a " + bytes.length + " byte array"); + " offset " + offset + " in a " + bytes.length + " byte array");
} }
for(int i= offset + 3; i > offset; i--) { if (UnsafeComparer.isAvailable()) {
bytes[i] = (byte) val; return putIntUnsafe(bytes, offset, val);
val >>>= 8; } else {
for(int i= offset + 3; i > offset; i--) {
bytes[i] = (byte) val;
val >>>= 8;
}
bytes[offset] = (byte) val;
return offset + SIZEOF_INT;
} }
bytes[offset] = (byte) val; }
/**
* Put an int value out to the specified byte array position (Unsafe).
* @param bytes the byte array
* @param offset position in the array
* @param val int to write out
* @return incremented offset
*/
public static int putIntUnsafe(byte[] bytes, int offset, int val)
{
if (UnsafeComparer.littleEndian) {
val = Integer.reverseBytes(val);
}
UnsafeComparer.theUnsafe.putInt(bytes, (long) offset +
UnsafeComparer.BYTE_ARRAY_BASE_OFFSET , val);
return offset + SIZEOF_INT; return offset + SIZEOF_INT;
} }
@ -1027,7 +1127,7 @@ public class Bytes implements Comparable<Bytes> {
* @return the short value * @return the short value
*/ */
public static short toShort(byte[] bytes) { public static short toShort(byte[] bytes) {
return toShort(bytes, 0, SIZEOF_SHORT); return toShortUnsafe(bytes, 0);
} }
/** /**
@ -1053,11 +1153,15 @@ public class Bytes implements Comparable<Bytes> {
if (length != SIZEOF_SHORT || offset + length > bytes.length) { if (length != SIZEOF_SHORT || offset + length > bytes.length) {
throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_SHORT); throw explainWrongLengthOrOffset(bytes, offset, length, SIZEOF_SHORT);
} }
short n = 0; if (UnsafeComparer.isAvailable()) {
n ^= bytes[offset] & 0xFF; return toShortUnsafe(bytes, offset);
n <<= 8; } else {
n ^= bytes[offset+1] & 0xFF; short n = 0;
return n; n ^= bytes[offset] & 0xFF;
n <<= 8;
n ^= bytes[offset+1] & 0xFF;
return n;
}
} }
/** /**
@ -1087,9 +1191,30 @@ public class Bytes implements Comparable<Bytes> {
throw new IllegalArgumentException("Not enough room to put a short at" throw new IllegalArgumentException("Not enough room to put a short at"
+ " offset " + offset + " in a " + bytes.length + " byte array"); + " offset " + offset + " in a " + bytes.length + " byte array");
} }
bytes[offset+1] = (byte) val; if (UnsafeComparer.isAvailable()) {
val >>= 8; return putShortUnsafe(bytes, offset, val);
bytes[offset] = (byte) val; } else {
bytes[offset+1] = (byte) val;
val >>= 8;
bytes[offset] = (byte) val;
return offset + SIZEOF_SHORT;
}
}
/**
* Put a short value out to the specified byte array position (Unsafe).
* @param bytes the byte array
* @param offset position in the array
* @param val short to write out
* @return incremented offset
*/
public static int putShortUnsafe(byte[] bytes, int offset, short val)
{
if (UnsafeComparer.littleEndian) {
val = Short.reverseBytes(val);
}
UnsafeComparer.theUnsafe.putShort(bytes, (long) offset +
UnsafeComparer.BYTE_ARRAY_BASE_OFFSET , val);
return offset + SIZEOF_SHORT; return offset + SIZEOF_SHORT;
} }
@ -1397,12 +1522,37 @@ public class Bytes implements Comparable<Bytes> {
/** /**
* Returns true if x1 is less than x2, when both values are treated as * Returns true if x1 is less than x2, when both values are treated as
* unsigned. * unsigned long.
*/ */
static boolean lessThanUnsigned(long x1, long x2) { static boolean lessThanUnsignedLong(long x1, long x2) {
return (x1 + Long.MIN_VALUE) < (x2 + Long.MIN_VALUE); return (x1 + Long.MIN_VALUE) < (x2 + Long.MIN_VALUE);
} }
/**
* Returns true if x1 is less than x2, when both values are treated as
* unsigned int.
*/
static boolean lessThanUnsignedInt(int x1, int x2) {
return (x1 & 0xffffffffL) < (x2 & 0xffffffffL);
}
/**
* Returns true if x1 is less than x2, when both values are treated as
* unsigned short.
*/
static boolean lessThanUnsignedShort(short x1, short x2) {
return (x1 & 0xffff) < (x2 & 0xffff);
}
/**
* Checks if Unsafe is available
* @return true, if available, false - otherwise
*/
public static boolean isAvailable()
{
return theUnsafe != null;
}
/** /**
* Lexicographically compare two arrays. * Lexicographically compare two arrays.
* *
@ -1417,16 +1567,17 @@ public class Bytes implements Comparable<Bytes> {
@Override @Override
public int compareTo(byte[] buffer1, int offset1, int length1, public int compareTo(byte[] buffer1, int offset1, int length1,
byte[] buffer2, int offset2, int length2) { byte[] buffer2, int offset2, int length2) {
// Short circuit equal case // Short circuit equal case
if (buffer1 == buffer2 && if (buffer1 == buffer2 &&
offset1 == offset2 && offset1 == offset2 &&
length1 == length2) { length1 == length2) {
return 0; return 0;
} }
int minLength = Math.min(length1, length2); final int minLength = Math.min(length1, length2);
int minWords = minLength / SIZEOF_LONG; final int minWords = minLength / SIZEOF_LONG;
int offset1Adj = offset1 + BYTE_ARRAY_BASE_OFFSET; final long offset1Adj = offset1 + BYTE_ARRAY_BASE_OFFSET;
int offset2Adj = offset2 + BYTE_ARRAY_BASE_OFFSET; final long offset2Adj = offset2 + BYTE_ARRAY_BASE_OFFSET;
/* /*
* Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a * Compare 8 bytes at a time. Benchmarking shows comparing 8 bytes at a
@ -1437,40 +1588,43 @@ public class Bytes implements Comparable<Bytes> {
long lw = theUnsafe.getLong(buffer1, offset1Adj + (long) i); long lw = theUnsafe.getLong(buffer1, offset1Adj + (long) i);
long rw = theUnsafe.getLong(buffer2, offset2Adj + (long) i); long rw = theUnsafe.getLong(buffer2, offset2Adj + (long) i);
long diff = lw ^ rw; long diff = lw ^ rw;
if(littleEndian){
lw = Long.reverseBytes(lw);
rw = Long.reverseBytes(rw);
}
if (diff != 0) { if (diff != 0) {
if (!littleEndian) { return lessThanUnsignedLong(lw, rw) ? -1 : 1;
return lessThanUnsigned(lw, rw) ? -1 : 1;
}
// Use binary search
int n = 0;
int y;
int x = (int) diff;
if (x == 0) {
x = (int) (diff >>> 32);
n = 32;
}
y = x << 16;
if (y == 0) {
n += 16;
} else {
x = y;
}
y = x << 8;
if (y == 0) {
n += 8;
}
return (int) (((lw >>> n) & 0xFFL) - ((rw >>> n) & 0xFFL));
} }
} }
int offset = minWords * SIZEOF_LONG;
// The epilogue to cover the last (minLength % 8) elements. if (minLength - offset >= SIZEOF_INT) {
for (int i = minWords * SIZEOF_LONG; i < minLength; i++) { int il = theUnsafe.getInt(buffer1, offset1Adj + offset);
int a = (buffer1[offset1 + i] & 0xff); int ir = theUnsafe.getInt(buffer2, offset2Adj + offset);
int b = (buffer2[offset2 + i] & 0xff); if(littleEndian){
il = Integer.reverseBytes(il);
ir = Integer.reverseBytes(ir);
}
if(il != ir){
return lessThanUnsignedInt(il, ir) ? -1: 1;
}
offset += SIZEOF_INT;
}
if (minLength - offset >= SIZEOF_SHORT) {
short sl = theUnsafe.getShort(buffer1, offset1Adj + offset);
short sr = theUnsafe.getShort(buffer2, offset2Adj + offset);
if(littleEndian){
sl = Short.reverseBytes(sl);
sr = Short.reverseBytes(sr);
}
if(sl != sr){
return lessThanUnsignedShort(sl, sr) ? -1: 1;
}
offset += SIZEOF_SHORT;
}
if (minLength - offset == 1) {
int a = (buffer1[(int)(offset1 + offset)] & 0xff);
int b = (buffer2[(int)(offset2 + offset)] & 0xff);
if (a != b) { if (a != b) {
return a - b; return a - b;
} }