You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by st...@apache.org on 2018/07/19 03:40:13 UTC

hbase git commit: HBASE-20875 MemStoreLABImp::copyIntoCell uses 7% CPU when writing

Repository: hbase
Updated Branches:
  refs/heads/branch-2.0 5594f0b9f -> 9c44f9594


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.


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/9c44f959
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/9c44f959
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/9c44f959

Branch: refs/heads/branch-2.0
Commit: 9c44f95948b829edaf0f3c57837ad0cfdc4889c1
Parents: 5594f0b
Author: Michael Stack <st...@apache.org>
Authored: Wed Jul 11 22:40:04 2018 -0400
Committer: Michael Stack <st...@apache.org>
Committed: Wed Jul 18 20:40:01 2018 -0700

----------------------------------------------------------------------
 .../hbase/regionserver/MemStoreLABImpl.java     | 69 +++++++++++++++++++-
 1 file changed, 66 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/9c44f959/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MemStoreLABImpl.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MemStoreLABImpl.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MemStoreLABImpl.java
index ac7223f..404635d 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MemStoreLABImpl.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MemStoreLABImpl.java
@@ -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);
   }
 
   /**
@@ -134,6 +138,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 = cell instanceof ExtendedCell? ((ExtendedCell)cell).getSerializedSize():
         KeyValueUtil.length(cell);
@@ -170,6 +215,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();
@@ -181,6 +227,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) {
@@ -188,9 +251,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);
     }
   }