diff --git a/src/main/java/org/elasticsearch/common/bytes/BytesReference.java b/src/main/java/org/elasticsearch/common/bytes/BytesReference.java index 29d858c1748..a72346f9ee8 100644 --- a/src/main/java/org/elasticsearch/common/bytes/BytesReference.java +++ b/src/main/java/org/elasticsearch/common/bytes/BytesReference.java @@ -20,7 +20,6 @@ package org.elasticsearch.common.bytes; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.common.util.UnsafeUtils; import org.jboss.netty.buffer.ChannelBuffer; import java.io.IOException; @@ -42,16 +41,11 @@ public interface BytesReference { return false; } - if (a.hasArray() && b.hasArray()) { - // court-circuit to compare several bytes at once - return UnsafeUtils.equals(a.array(), a.arrayOffset(), b.array(), b.arrayOffset(), a.length()); - } else { - return slowBytesEquals(a, b); - } + return bytesEquals(a, b); } // pkg-private for testing - static boolean slowBytesEquals(BytesReference a, BytesReference b) { + static boolean bytesEquals(BytesReference a, BytesReference b) { assert a.length() == b.length(); for (int i = 0, end = a.length(); i < end; ++i) { if (a.get(i) != b.get(i)) { diff --git a/src/main/java/org/elasticsearch/common/hash/MurmurHash3.java b/src/main/java/org/elasticsearch/common/hash/MurmurHash3.java index 0da1120f5a8..c9c0d29c448 100644 --- a/src/main/java/org/elasticsearch/common/hash/MurmurHash3.java +++ b/src/main/java/org/elasticsearch/common/hash/MurmurHash3.java @@ -19,7 +19,7 @@ package org.elasticsearch.common.hash; -import org.elasticsearch.common.util.UnsafeUtils; +import org.elasticsearch.common.util.ByteUtils; /** @@ -41,7 +41,7 @@ public enum MurmurHash3 { protected static long getblock(byte[] key, int offset, int index) { int i_8 = index << 3; int blockOffset = offset + i_8; - return UnsafeUtils.readLongLE(key, blockOffset); + return ByteUtils.readLongLE(key, blockOffset); } protected static long fmix(long k) { @@ -68,8 +68,8 @@ public enum MurmurHash3 { final int len16 = length & 0xFFFFFFF0; // higher multiple of 16 that is lower than or equal to length final int end = offset + len16; for (int i = offset; i < end; i += 16) { - long k1 = UnsafeUtils.readLongLE(key, i); - long k2 = UnsafeUtils.readLongLE(key, i + 8); + long k1 = ByteUtils.readLongLE(key, i); + long k2 = ByteUtils.readLongLE(key, i + 8); k1 *= C1; k1 = Long.rotateLeft(k1, 31); diff --git a/src/main/java/org/elasticsearch/common/util/BytesRefHash.java b/src/main/java/org/elasticsearch/common/util/BytesRefHash.java index 90ee3c75f71..fc3cb285416 100644 --- a/src/main/java/org/elasticsearch/common/util/BytesRefHash.java +++ b/src/main/java/org/elasticsearch/common/util/BytesRefHash.java @@ -77,7 +77,7 @@ public final class BytesRefHash extends AbstractHash { final long slot = slot(rehash(code), mask); for (long index = slot; ; index = nextSlot(index, mask)) { final long id = id(index); - if (id == -1L || UnsafeUtils.equals(key, get(id, spare))) { + if (id == -1L || key.bytesEquals(get(id, spare))) { return id; } } @@ -99,7 +99,7 @@ public final class BytesRefHash extends AbstractHash { append(id, key, code); ++size; return id; - } else if (UnsafeUtils.equals(key, get(curId, spare))) { + } else if (key.bytesEquals(get(curId, spare))) { return -1 - curId; } } diff --git a/src/main/java/org/elasticsearch/common/util/UnsafeUtils.java b/src/main/java/org/elasticsearch/common/util/UnsafeUtils.java deleted file mode 100644 index 9f733b045a0..00000000000 --- a/src/main/java/org/elasticsearch/common/util/UnsafeUtils.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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.elasticsearch.common.util; - -import org.apache.lucene.util.BytesRef; -import sun.misc.Unsafe; - -import java.lang.reflect.Field; -import java.nio.ByteOrder; - -/** Utility methods that use {@link Unsafe}. */ -public enum UnsafeUtils { - ; - - private static final Unsafe UNSAFE; - private static final long BYTE_ARRAY_OFFSET; - private static final int BYTE_ARRAY_SCALE; - - static { - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - UNSAFE = (Unsafe) theUnsafe.get(null); - BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); - BYTE_ARRAY_SCALE = UNSAFE.arrayIndexScale(byte[].class); - } catch (IllegalAccessException e) { - throw new ExceptionInInitializerError("Cannot access Unsafe"); - } catch (NoSuchFieldException e) { - throw new ExceptionInInitializerError("Cannot access Unsafe"); - } catch (SecurityException e) { - throw new ExceptionInInitializerError("Cannot access Unsafe"); - } - } - - // Don't expose these methods directly, they are too easy to mis-use since they depend on the byte order. - // If you need methods to read integers, please expose a method that makes the byte order explicit such - // as readIntLE (little endian). - - // Also, please ***NEVER*** expose any method that writes using Unsafe, this is too dangerous - - private static long readLong(byte[] src, int offset) { - return UNSAFE.getLong(src, BYTE_ARRAY_OFFSET + offset); - } - - private static int readInt(byte[] src, int offset) { - return UNSAFE.getInt(src, BYTE_ARRAY_OFFSET + offset); - } - - private static short readShort(byte[] src, int offset) { - return UNSAFE.getShort(src, BYTE_ARRAY_OFFSET + offset); - } - - private static byte readByte(byte[] src, int offset) { - return UNSAFE.getByte(src, BYTE_ARRAY_OFFSET + BYTE_ARRAY_SCALE * offset); - } - - /** Compare the two given {@link BytesRef}s for equality. */ - public static boolean equals(BytesRef b1, BytesRef b2) { - if (b1.length != b2.length) { - return false; - } - return equals(b1.bytes, b1.offset, b2.bytes, b2.offset, b1.length); - } - - /** - * Compare b1[offset1:offset1+length)against b1[offset2:offset2+length). - */ - public static boolean equals(byte[] b1, int offset1, byte[] b2, int offset2, int length) { - int o1 = offset1; - int o2 = offset2; - int len = length; - while (len >= 8) { - if (readLong(b1, o1) != readLong(b2, o2)) { - return false; - } - len -= 8; - o1 += 8; - o2 += 8; - } - if (len >= 4) { - if (readInt(b1, o1) != readInt(b2, o2)) { - return false; - } - len -= 4; - o1 += 4; - o2 += 4; - } - if (len >= 2) { - if (readShort(b1, o1) != readShort(b2, o2)) { - return false; - } - len -= 2; - o1 += 2; - o2 += 2; - } - if (len == 1) { - if (readByte(b1, o1) != readByte(b2, o2)) { - return false; - } - } else { - assert len == 0; - } - return true; - } - - /** - * Read a long using little endian byte order. - */ - public static long readLongLE(byte[] src, int offset) { - long value = readLong(src, offset); - if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { - value = Long.reverseBytes(value); - } - return value; - } - - /** - * Read an int using little endian byte order. - */ - public static int readIntLE(byte[] src, int offset) { - int value = readInt(src, offset); - if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { - value = Integer.reverseBytes(value); - } - return value; - } - -} diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/HyperLogLogPlusPlus.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/HyperLogLogPlusPlus.java index c4980dc8cf9..82988dd3fa4 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/HyperLogLogPlusPlus.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/HyperLogLogPlusPlus.java @@ -31,8 +31,8 @@ import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.common.util.ByteArray; +import org.elasticsearch.common.util.ByteUtils; import org.elasticsearch.common.util.IntArray; -import org.elasticsearch.common.util.UnsafeUtils; import java.io.IOException; import java.nio.ByteBuffer; @@ -438,7 +438,7 @@ public final class HyperLogLogPlusPlus implements Releasable { private int get(long bucket, int index) { runLens.get(index(bucket, index), 4, readSpare); - return UnsafeUtils.readIntLE(readSpare.bytes, readSpare.offset); + return ByteUtils.readIntLE(readSpare.bytes, readSpare.offset); } private void set(long bucket, int index, int value) { diff --git a/src/test/java/org/elasticsearch/benchmark/common/util/BytesRefComparisonsBenchmark.java b/src/test/java/org/elasticsearch/benchmark/common/util/BytesRefComparisonsBenchmark.java deleted file mode 100644 index 7ff45eb7935..00000000000 --- a/src/test/java/org/elasticsearch/benchmark/common/util/BytesRefComparisonsBenchmark.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch 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.elasticsearch.benchmark.common.util; - -import com.carrotsearch.randomizedtesting.generators.RandomInts; -import com.carrotsearch.randomizedtesting.generators.RandomPicks; -import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.common.util.UnsafeUtils; - -import java.util.Random; -import java.util.concurrent.TimeUnit; - -public class BytesRefComparisonsBenchmark { - - private static final Random R = new Random(0); - private static final int ITERS = 100; - - // To avoid JVM optimizations - @SuppressWarnings("unused") - private static boolean DUMMY; - - enum Comparator { - SAFE { - boolean compare(BytesRef b1, BytesRef b2) { - return b1.bytesEquals(b2); - } - }, - UNSAFE { - @Override - boolean compare(BytesRef b1, BytesRef b2) { - return UnsafeUtils.equals(b1, b2); - } - }; - abstract boolean compare(BytesRef b1, BytesRef b2); - } - - private static BytesRef[] buildBytesRefs(int minLen, int maxLen, int count, int uniqueCount) { - final BytesRef[] uniqueRefs = new BytesRef[uniqueCount]; - for (int i = 0; i < uniqueCount; ++i) { - final int len = RandomInts.randomIntBetween(R, minLen, maxLen); - final byte[] bytes = new byte[len]; - for (int j = 0; j < bytes.length; ++j) { - bytes[j] = (byte) R.nextInt(2); // so that some instances have common prefixes - } - uniqueRefs[i] = new BytesRef(bytes); - } - final BytesRef[] result = new BytesRef[count]; - for (int i = 0; i < count; ++i) { - result[i] = RandomPicks.randomFrom(R, uniqueRefs); - } - int totalLen = 0; - for (BytesRef b : result) { - totalLen += b.length; - } - final byte[] data = new byte[totalLen]; - int offset = 0; - for (int i = 0; i < count; ++i) { - final BytesRef b = result[i]; - System.arraycopy(b.bytes, b.offset, data, offset, b.length); - result[i] = new BytesRef(data, offset, b.length); - offset += b.length; - } - if (offset != totalLen) { - throw new AssertionError(); - } - return result; - } - - private static long bench(Comparator comparator, BytesRef[] refs, int iters) { - boolean xor = false; - final long start = System.nanoTime(); - for (int iter = 0; iter < iters; ++iter) { - for (int i = 0; i < refs.length; ++i) { - for (int j = i + 1; j < refs.length; ++j) { - xor ^= comparator.compare(refs[i], refs[j]); - } - } - } - DUMMY = xor; - return System.nanoTime() - start; - } - - public static void main(String[] args) throws InterruptedException { - // warmup - BytesRef[] bytes = buildBytesRefs(2, 20, 1000, 100); - final long start = System.nanoTime(); - while (System.nanoTime() - start < TimeUnit.SECONDS.toNanos(10)) { - for (Comparator comparator : Comparator.values()) { - bench(comparator, bytes, 1); - } - } - - System.out.println("## Various lengths"); - // make sure GC doesn't hurt results - System.gc(); - Thread.sleep(2000); - for (Comparator comparator : Comparator.values()) { - bench(comparator, bytes, ITERS); - } - for (int i = 0; i < 3; ++i) { - for (Comparator comparator : Comparator.values()) { - System.out.println(comparator + " " + new TimeValue(bench(comparator, bytes, ITERS), TimeUnit.NANOSECONDS)); - } - } - - for (int len = 2; len <= 20; ++len) { - System.out.println("## Length = " + len); - bytes = buildBytesRefs(len, len, 1000, 100); - System.gc(); - Thread.sleep(2000); - for (Comparator comparator : Comparator.values()) { - bench(comparator, bytes, ITERS); - } - for (int i = 0; i < 3; ++i) { - for (Comparator comparator : Comparator.values()) { - System.out.println(comparator + " " + new TimeValue(bench(comparator, bytes, ITERS), TimeUnit.NANOSECONDS)); - } - } - } - } - -} diff --git a/src/test/java/org/elasticsearch/common/bytes/BytesReferenceTests.java b/src/test/java/org/elasticsearch/common/bytes/BytesReferenceTests.java index 187a458c293..aaf2ef557fb 100644 --- a/src/test/java/org/elasticsearch/common/bytes/BytesReferenceTests.java +++ b/src/test/java/org/elasticsearch/common/bytes/BytesReferenceTests.java @@ -37,13 +37,13 @@ public class BytesReferenceTests extends ElasticsearchTestCase { final BytesArray b1 = new BytesArray(array1, offset1, len); final BytesArray b2 = new BytesArray(array2, offset2, len); assertTrue(BytesReference.Helper.bytesEqual(b1, b2)); - assertTrue(BytesReference.Helper.slowBytesEquals(b1, b2)); + assertTrue(BytesReference.Helper.bytesEquals(b1, b2)); assertEquals(Arrays.hashCode(b1.toBytes()), b1.hashCode()); assertEquals(BytesReference.Helper.bytesHashCode(b1), BytesReference.Helper.slowHashCode(b2)); // test same instance assertTrue(BytesReference.Helper.bytesEqual(b1, b1)); - assertTrue(BytesReference.Helper.slowBytesEquals(b1, b1)); + assertTrue(BytesReference.Helper.bytesEquals(b1, b1)); assertEquals(BytesReference.Helper.bytesHashCode(b1), BytesReference.Helper.slowHashCode(b1)); if (len > 0) { @@ -54,7 +54,7 @@ public class BytesReferenceTests extends ElasticsearchTestCase { // test changed bytes array1[offset1 + randomInt(len - 1)] += 13; assertFalse(BytesReference.Helper.bytesEqual(b1, b2)); - assertFalse(BytesReference.Helper.slowBytesEquals(b1, b2)); + assertFalse(BytesReference.Helper.bytesEquals(b1, b2)); } }