HBASE-20875 MemStoreLABImp::copyIntoCell uses 7% CPU when writing
Make the #copyCellInto method smaller so it inlines; we do it by checking for the common type early and then taking a code path that presumes ByteBufferExtendedCell -- avoids checks.
This commit is contained in:
parent
8c85763327
commit
2bf5e46a33
|
@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.ByteBufferExtendedCell;
|
||||
import org.apache.hadoop.hbase.Cell;
|
||||
import org.apache.hadoop.hbase.ExtendedCell;
|
||||
import org.apache.hadoop.hbase.KeyValueUtil;
|
||||
|
@ -108,7 +109,10 @@ public class MemStoreLABImpl implements MemStoreLAB {
|
|||
|
||||
@Override
|
||||
public Cell copyCellInto(Cell cell) {
|
||||
return copyCellInto(cell, maxAlloc);
|
||||
// See head of copyBBECellInto for how it differs from copyCellInto
|
||||
return (cell instanceof ByteBufferExtendedCell)?
|
||||
copyBBECellInto((ByteBufferExtendedCell)cell, maxAlloc):
|
||||
copyCellInto(cell, maxAlloc);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -133,6 +137,47 @@ public class MemStoreLABImpl implements MemStoreLAB {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mostly a duplicate of {@link #copyCellInto(Cell, int)}} done for perf sake. It presumes
|
||||
* ByteBufferExtendedCell instead of Cell so we deal with a specific type rather than the
|
||||
* super generic Cell. Removes instanceof checks. Shrinkage is enough to make this inline where
|
||||
* before it was too big. Uses less CPU. See HBASE-20875 for evidence.
|
||||
* @see #copyCellInto(Cell, int)
|
||||
*/
|
||||
private Cell copyBBECellInto(ByteBufferExtendedCell cell, int maxAlloc) {
|
||||
int size = cell.getSerializedSize();
|
||||
Preconditions.checkArgument(size >= 0, "negative size");
|
||||
// Callers should satisfy large allocations from JVM heap so limit fragmentation.
|
||||
if (size > maxAlloc) {
|
||||
return null;
|
||||
}
|
||||
Chunk c = null;
|
||||
int allocOffset = 0;
|
||||
while (true) {
|
||||
// Try to get the chunk
|
||||
c = getOrMakeChunk();
|
||||
// We may get null because the some other thread succeeded in getting the lock
|
||||
// and so the current thread has to try again to make its chunk or grab the chunk
|
||||
// that the other thread created
|
||||
// Try to allocate from this chunk
|
||||
if (c != null) {
|
||||
allocOffset = c.alloc(size);
|
||||
if (allocOffset != -1) {
|
||||
// We succeeded - this is the common case - small alloc
|
||||
// from a big buffer
|
||||
break;
|
||||
}
|
||||
// not enough space!
|
||||
// try to retire this chunk
|
||||
tryRetireChunk(c);
|
||||
}
|
||||
}
|
||||
return copyBBECToChunkCell(cell, c.getData(), allocOffset, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #copyBBECellInto(ByteBufferExtendedCell, int)
|
||||
*/
|
||||
private Cell copyCellInto(Cell cell, int maxAlloc) {
|
||||
int size = Segment.getCellLength(cell);
|
||||
Preconditions.checkArgument(size >= 0, "negative size");
|
||||
|
@ -168,6 +213,7 @@ public class MemStoreLABImpl implements MemStoreLAB {
|
|||
/**
|
||||
* Clone the passed cell by copying its data into the passed buf and create a cell with a chunkid
|
||||
* out of it
|
||||
* @see #copyBBECToChunkCell(ByteBufferExtendedCell, ByteBuffer, int, int)
|
||||
*/
|
||||
private static Cell copyToChunkCell(Cell cell, ByteBuffer buf, int offset, int len) {
|
||||
int tagsLen = cell.getTagsLength();
|
||||
|
@ -179,6 +225,23 @@ public class MemStoreLABImpl implements MemStoreLAB {
|
|||
// serialization format only.
|
||||
KeyValueUtil.appendTo(cell, buf, offset, true);
|
||||
}
|
||||
return createChunkCell(buf, offset, len, tagsLen, cell.getSequenceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone the passed cell by copying its data into the passed buf and create a cell with a chunkid
|
||||
* out of it
|
||||
* @see #copyToChunkCell(Cell, ByteBuffer, int, int)
|
||||
*/
|
||||
private static Cell copyBBECToChunkCell(ByteBufferExtendedCell cell, ByteBuffer buf, int offset,
|
||||
int len) {
|
||||
int tagsLen = cell.getTagsLength();
|
||||
cell.write(buf, offset);
|
||||
return createChunkCell(buf, offset, len, tagsLen, cell.getSequenceId());
|
||||
}
|
||||
|
||||
private static Cell createChunkCell(ByteBuffer buf, int offset, int len, int tagsLen,
|
||||
long sequenceId) {
|
||||
// TODO : write the seqid here. For writing seqId we should create a new cell type so
|
||||
// that seqId is not used as the state
|
||||
if (tagsLen == 0) {
|
||||
|
@ -186,9 +249,9 @@ public class MemStoreLABImpl implements MemStoreLAB {
|
|||
// which directly return tagsLen as 0. So we avoid parsing many length components in
|
||||
// reading the tagLength stored in the backing buffer. The Memstore addition of every Cell
|
||||
// call getTagsLength().
|
||||
return new NoTagByteBufferChunkKeyValue(buf, offset, len, cell.getSequenceId());
|
||||
return new NoTagByteBufferChunkKeyValue(buf, offset, len, sequenceId);
|
||||
} else {
|
||||
return new ByteBufferChunkKeyValue(buf, offset, len, cell.getSequenceId());
|
||||
return new ByteBufferChunkKeyValue(buf, offset, len, sequenceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue