HBASE-14480 Small optimization in SingleByteBuff.

This commit is contained in:
anoopsjohn 2015-10-05 12:00:19 +05:30
parent dd955fa417
commit b014ba1243
3 changed files with 150 additions and 9 deletions

View File

@ -22,6 +22,9 @@ import java.nio.ByteBuffer;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.util.ByteBufferUtils;
import org.apache.hadoop.hbase.util.ObjectIntPair;
import org.apache.hadoop.hbase.util.UnsafeAccess;
import sun.nio.ch.DirectBuffer;
/**
* An implementation of ByteBuff where a single BB backs the BBI. This just acts
@ -30,11 +33,23 @@ import org.apache.hadoop.hbase.util.ObjectIntPair;
@InterfaceAudience.Private
public class SingleByteBuff extends ByteBuff {
private static final boolean UNSAFE_AVAIL = UnsafeAccess.isAvailable();
// Underlying BB
private final ByteBuffer buf;
// To access primitive values from underlying ByteBuffer using Unsafe
private long unsafeOffset;
private Object unsafeRef = null;
public SingleByteBuff(ByteBuffer buf) {
this.buf = buf;
if (buf.hasArray()) {
this.unsafeOffset = UnsafeAccess.BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset();
this.unsafeRef = buf.array();
} else {
this.unsafeOffset = ((DirectBuffer) buf).address();
}
}
@Override
@ -134,12 +149,15 @@ public class SingleByteBuff extends ByteBuff {
@Override
public byte get(int index) {
return ByteBufferUtils.toByte(this.buf, index);
if (UNSAFE_AVAIL) {
return UnsafeAccess.toByte(this.unsafeRef, this.unsafeOffset + index);
}
return this.buf.get(index);
}
@Override
public byte getByteAfterPosition(int offset) {
return ByteBufferUtils.toByte(this.buf, this.buf.position() + offset);
return get(this.buf.position() + offset);
}
@Override
@ -219,12 +237,15 @@ public class SingleByteBuff extends ByteBuff {
@Override
public short getShort(int index) {
return ByteBufferUtils.toShort(this.buf, index);
if (UNSAFE_AVAIL) {
return UnsafeAccess.toShort(unsafeRef, unsafeOffset + index);
}
return this.buf.getShort(index);
}
@Override
public short getShortAfterPosition(int offset) {
return ByteBufferUtils.toShort(this.buf, this.buf.position() + offset);
return getShort(this.buf.position() + offset);
}
@Override
@ -240,12 +261,15 @@ public class SingleByteBuff extends ByteBuff {
@Override
public int getInt(int index) {
return ByteBufferUtils.toInt(this.buf, index);
if (UNSAFE_AVAIL) {
return UnsafeAccess.toInt(unsafeRef, unsafeOffset + index);
}
return this.buf.getInt(index);
}
@Override
public int getIntAfterPosition(int offset) {
return ByteBufferUtils.toInt(this.buf, this.buf.position() + offset);
return getInt(this.buf.position() + offset);
}
@Override
@ -261,12 +285,15 @@ public class SingleByteBuff extends ByteBuff {
@Override
public long getLong(int index) {
return ByteBufferUtils.toLong(this.buf, index);
if (UNSAFE_AVAIL) {
return UnsafeAccess.toLong(unsafeRef, unsafeOffset + index);
}
return this.buf.getLong(index);
}
@Override
public long getLongAfterPosition(int offset) {
return ByteBufferUtils.toLong(this.buf, this.buf.position() + offset);
return getLong(this.buf.position() + offset);
}
@Override

View File

@ -40,7 +40,7 @@ public final class UnsafeAccess {
static final Unsafe theUnsafe;
/** The offset to the first element in a byte array. */
static final long BYTE_ARRAY_BASE_OFFSET;
public static final long BYTE_ARRAY_BASE_OFFSET;
static final boolean littleEndian = ByteOrder.nativeOrder()
.equals(ByteOrder.LITTLE_ENDIAN);
@ -181,6 +181,20 @@ public final class UnsafeAccess {
return getAsShort(buf, offset);
}
/**
* Reads a short value at the given Object's offset considering it was written in big-endian
* format.
* @param ref
* @param offset
* @return short value at offset
*/
public static short toShort(Object ref, long offset) {
if (littleEndian) {
return Short.reverseBytes(theUnsafe.getShort(ref, offset));
}
return theUnsafe.getShort(ref, offset);
}
/**
* Reads bytes at the given offset as a short value.
* @param buf
@ -209,6 +223,20 @@ public final class UnsafeAccess {
return getAsInt(buf, offset);
}
/**
* Reads a int value at the given Object's offset considering it was written in big-endian
* format.
* @param ref
* @param offset
* @return int value at offset
*/
public static int toInt(Object ref, long offset) {
if (littleEndian) {
return Integer.reverseBytes(theUnsafe.getInt(ref, offset));
}
return theUnsafe.getInt(ref, offset);
}
/**
* Reads bytes at the given offset as an int value.
* @param buf
@ -237,6 +265,20 @@ public final class UnsafeAccess {
return getAsLong(buf, offset);
}
/**
* Reads a long value at the given Object's offset considering it was written in big-endian
* format.
* @param ref
* @param offset
* @return long value at offset
*/
public static long toLong(Object ref, long offset) {
if (littleEndian) {
return Long.reverseBytes(theUnsafe.getLong(ref, offset));
}
return theUnsafe.getLong(ref, offset);
}
/**
* Reads bytes at the given offset as a long value.
* @param buf
@ -411,4 +453,14 @@ public final class UnsafeAccess {
return theUnsafe.getByte(buf.array(), BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset() + offset);
}
}
/**
* Returns the byte at the given offset of the object
* @param ref
* @param offset
* @return the byte at the given offset
*/
public static byte toByte(Object ref, long offset) {
return theUnsafe.getByte(ref, offset);
}
}

View File

@ -0,0 +1,62 @@
/**
* Copyright The Apache Software Foundation
*
* 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.hadoop.hbase.nio;
import static org.junit.Assert.assertEquals;
import java.nio.ByteBuffer;
import org.apache.hadoop.hbase.testclassification.MiscTests;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@Category({ MiscTests.class, SmallTests.class })
public class TestSingleByteBuff {
@Test
public void testPositionalReads() {
// Off heap buffer
testPositionalReads(ByteBuffer.allocateDirect(15));
// On heap buffer
testPositionalReads(ByteBuffer.allocate(15));
}
private void testPositionalReads(ByteBuffer bb) {
int i = 9;
short s = 5;
byte b = 2;
long l = 1234L;
bb.putInt(i);
bb.putLong(l);
bb.put(b);
bb.putShort(s);
SingleByteBuff sbb = new SingleByteBuff(bb);
assertEquals(i, sbb.getInt(0));
assertEquals(l, sbb.getLong(4));
assertEquals(b, sbb.get(12));
assertEquals(s, sbb.getShort(13));
sbb.rewind();
assertEquals(i, sbb.getIntAfterPosition(0));
assertEquals(l, sbb.getLongAfterPosition(4));
assertEquals(b, sbb.getByteAfterPosition(12));
assertEquals(s, sbb.getShortAfterPosition(13));
}
}