blob: fec4e5a920b41c64e2ffbfe0853db57f4e609684 [file] [log] [blame]
/***
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.tajo.util;
import com.google.common.primitives.Longs;
import com.google.common.primitives.UnsignedBytes;
import com.google.common.primitives.UnsignedLongs;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.nio.ByteOrder;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Comparator;
/**
* This is borrowed from Guava's UnsignedBytes.
*/
public class UnsafeComparer implements Comparator<byte[]> {
public static final UnsafeComparer INSTANCE;
static {
INSTANCE = new UnsafeComparer();
}
public Comparator<byte []> get() {
return INSTANCE;
}
private UnsafeComparer() {}
static final boolean littleEndian =
ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN);
/*
* The following static final fields exist for performance reasons.
*
* In UnsignedBytesBenchmark, accessing the following objects via static
* final fields is the fastest (more than twice as fast as the Java
* implementation, vs ~1.5x with non-final static fields, on x86_32)
* under the Hotspot server compiler. The reason is obviously that the
* non-final fields need to be reloaded inside the loop.
*
* And, no, defining (final or not) local variables out of the loop still
* isn't as good because the null check on the theUnsafe object remains
* inside the loop and BYTE_ARRAY_BASE_OFFSET doesn't get
* constant-folded.
*
* The compiler can treat static final fields as compile-time constants
* and can constant-fold them while (final or not) local variables are
* run time values.
*/
static final Unsafe theUnsafe;
/** The offset to the first element in a byte array. */
static final int BYTE_ARRAY_BASE_OFFSET;
static {
theUnsafe = (Unsafe) AccessController.doPrivileged(
new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return f.get(null);
} catch (NoSuchFieldException e) {
// It doesn't matter what we throw;
// it's swallowed in getBestComparator().
throw new Error();
} catch (IllegalAccessException e) {
throw new Error();
}
}
});
BYTE_ARRAY_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);
// sanity check - this should never fail
if (theUnsafe.arrayIndexScale(byte[].class) != 1) {
throw new AssertionError();
}
}
@SuppressWarnings("unused")
public static int compareStatic(byte[] left, byte[] right) {
return INSTANCE.compare(left, right);
}
@Override public int compare(byte[] left, byte[] right) {
int minLength = Math.min(left.length, right.length);
int minWords = minLength / Longs.BYTES;
/*
* 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 * Longs.BYTES; i += Longs.BYTES) {
long lw = theUnsafe.getLong(left, BYTE_ARRAY_BASE_OFFSET + (long) i);
long rw = theUnsafe.getLong(right, BYTE_ARRAY_BASE_OFFSET + (long) i);
long diff = lw ^ rw;
if (diff != 0) {
if (!littleEndian) {
return UnsignedLongs.compare(lw, rw);
}
// 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));
}
}
// The epilogue to cover the last (minLength % 8) elements.
for (int i = minWords * Longs.BYTES; i < minLength; i++) {
int result = UnsignedBytes.compare(left[i], right[i]);
if (result != 0) {
return result;
}
}
return left.length - right.length;
}
}