diff --git a/src/java/org/apache/cassandra/db/memtable/AbstractMemtable.java b/src/java/org/apache/cassandra/db/memtable/AbstractMemtable.java index 8983979a9112..b5e560961e6b 100644 --- a/src/java/org/apache/cassandra/db/memtable/AbstractMemtable.java +++ b/src/java/org/apache/cassandra/db/memtable/AbstractMemtable.java @@ -196,10 +196,9 @@ protected static class ColumnsCollector public void update(RegularAndStaticColumns columns) { - for (ColumnMetadata s : columns.statics) - update(s); - for (ColumnMetadata r : columns.regulars) - update(r); + if (!columns.statics.isEmpty()) + columns.statics.apply(this::update); + columns.regulars.apply(this::update); } public void update(ColumnsCollector other) @@ -253,6 +252,8 @@ public void update(EncodingStats newStats) { EncodingStats current = stats.get(); EncodingStats updated = current.mergeWith(newStats); + if (current == updated) + return; if (stats.compareAndSet(current, updated)) return; } diff --git a/src/java/org/apache/cassandra/db/rows/AbstractCell.java b/src/java/org/apache/cassandra/db/rows/AbstractCell.java index aa5536593dcc..d8adb0683e17 100644 --- a/src/java/org/apache/cassandra/db/rows/AbstractCell.java +++ b/src/java/org/apache/cassandra/db/rows/AbstractCell.java @@ -149,9 +149,10 @@ public Cell updateAllTimesWithNewCellPathForComplexColumnData(@Nonnull CellPa public int dataSize() { CellPath path = path(); - return TypeSizes.sizeof(timestamp()) - + TypeSizes.sizeof(ttl()) - + TypeSizes.sizeof(localDeletionTime()) + // NOTE: TypeSizes.sizeof(localDeletionTime()) - method call like this is not eliminated by JIT in case of a megamorphic call + return TypeSizes.LONG_SIZE // timestamp() + + TypeSizes.INT_SIZE // ttl() + + TypeSizes.LONG_SIZE // localDeletionTime() + valueSize() + (path == null ? 0 : path.dataSize()); } diff --git a/src/java/org/apache/cassandra/db/rows/BTreeRow.java b/src/java/org/apache/cassandra/db/rows/BTreeRow.java index bf6b5b5061c1..7020f96b479b 100644 --- a/src/java/org/apache/cassandra/db/rows/BTreeRow.java +++ b/src/java/org/apache/cassandra/db/rows/BTreeRow.java @@ -525,8 +525,8 @@ public Row transform(Function function) @Override public Row clone(Cloner cloner) { - Object[] tree = BTree.transform(btree, c -> c.clone(cloner)); - return BTreeRow.create(cloner.clone(clustering), primaryKeyLivenessInfo, deletion, tree); + Object[] tree = BTree.transform(btree, ColumnData::clone, cloner); + return BTreeRow.create(cloner.clone(clustering), primaryKeyLivenessInfo, deletion, tree, minLocalDeletionTime); } public int dataSize() diff --git a/src/java/org/apache/cassandra/db/rows/ComplexColumnData.java b/src/java/org/apache/cassandra/db/rows/ComplexColumnData.java index 20c4045bac47..8de4cc9943e8 100644 --- a/src/java/org/apache/cassandra/db/rows/ComplexColumnData.java +++ b/src/java/org/apache/cassandra/db/rows/ComplexColumnData.java @@ -257,7 +257,7 @@ public ComplexColumnData transform(Function, ? extends Cell< @Override public ColumnData clone(Cloner cloner) { - return transform(c -> cloner.clone(c)); + return transform(cloner::clone); } public int estimateCloneSize(Cloner cloner) diff --git a/src/java/org/apache/cassandra/db/rows/NativeCell.java b/src/java/org/apache/cassandra/db/rows/NativeCell.java index 56301cfa7a49..2c77583ec61e 100644 --- a/src/java/org/apache/cassandra/db/rows/NativeCell.java +++ b/src/java/org/apache/cassandra/db/rows/NativeCell.java @@ -20,6 +20,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; +import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.db.marshal.AddressBasedNativeData; import org.apache.cassandra.db.marshal.ByteArrayAccessor; import org.apache.cassandra.db.marshal.NativeAccessor; @@ -198,6 +199,16 @@ public int valueSize() return NativeEndianMemoryUtil.getInt(peer + LENGTH); } + public int dataSize() + { + // NOTE: TypeSizes.sizeof(localDeletionTime()) - method calls like these are not eliminated by JIT in case of a megamorphic call + return TypeSizes.LONG_SIZE // timestamp() + + TypeSizes.INT_SIZE // ttl() + + TypeSizes.LONG_SIZE // localDeletionTime() + + valueSize() + + (hasPath() ? pathDataSize() : 0); + } + public CellPath path() { if (!hasPath()) @@ -208,6 +219,12 @@ public CellPath path() return CellPath.create(MemoryUtil.getByteBuffer(offset + 4, size, ByteOrder.BIG_ENDIAN)); } + private int pathDataSize() + { + long offset = getAddress() + valueSize(); + return NativeEndianMemoryUtil.getInt(offset); + } + public Cell withUpdatedValue(ByteBuffer newValue) { throw new UnsupportedOperationException(); diff --git a/src/java/org/apache/cassandra/utils/btree/BTree.java b/src/java/org/apache/cassandra/utils/btree/BTree.java index 0f84b6a5a906..5306a7bdb7f6 100644 --- a/src/java/org/apache/cassandra/utils/btree/BTree.java +++ b/src/java/org/apache/cassandra/utils/btree/BTree.java @@ -1273,6 +1273,7 @@ private static Object[] transformAndFilterLeaf(Object[] leaf, BiFunct * The result of any transformation must sort identically as their originals, wrt other results. *

* If no modifications are made, the original is returned. + * NOTE: codewise *identical* to {@link #transform(Object[], BiFunction, Object)} */ public static Object[] transform(Object[] tree, Function function) { @@ -1316,6 +1317,55 @@ public static Object[] transform(Object[] tree, Function + * If no modifications are made, the original is returned. + * NOTE: codewise *identical* to {@link #transform(Object[], Function)} + */ + public static Object[] transform(Object[] tree, BiFunction function, I2 param) + { + if (isEmpty(tree)) // isEmpty determined by identity; must return input + return tree; + + if (isLeaf(tree)) // escape hatch for fast leaf transformation + return transformLeaf(tree, function, param); + + Object[] result = tree; // optimistically assume we'll return our input unmodified + int keyCount = shallowSizeOfBranch(tree); + for (int i = 0; i < keyCount; ++i) + { + // operate on a pair of (child,key) each loop + Object[] curChild = (Object[]) tree[keyCount + i]; + Object[] updChild = transform(curChild, function, param); + Object curKey = tree[i]; + Object updKey = function.apply((I) curKey, param); + if (result == tree) + { + if (curChild == updChild && curKey == updKey) + continue; // if output still same as input, loop + + // otherwise initialise output to a copy of input up to this point + result = transformCopyBranchHelper(tree, keyCount, i, i); + } + result[keyCount + i] = updChild; + result[i] = updKey; + } + // final unrolled copy of loop for last child only (unbalanced with keys) + Object[] curChild = (Object[]) tree[2 * keyCount]; + Object[] updChild = transform(curChild, function, param); + if (result == tree) + { + if (curChild == updChild) + return tree; + result = transformCopyBranchHelper(tree, keyCount, keyCount, keyCount); + } + result[2 * keyCount] = updChild; + result[2 * keyCount + 1] = tree[2 * keyCount + 1]; // take the original sizeMap, as we are exactly the same shape + return result; + } + // create a copy of a branch, with the exact same size, copying the specified number of keys and children private static Object[] transformCopyBranchHelper(Object[] branch, int keyCount, int copyKeyCount, int copyChildCount) { @@ -1326,6 +1376,7 @@ private static Object[] transformCopyBranchHelper(Object[] branch, int keyCount, } // an efficient transformAndFilter implementation suitable for a tree consisting of a single leaf root + // NOTE: codewise *identical* to {@link #transformLeaf(Object[], BiFunction, Object)} private static Object[] transformLeaf(Object[] leaf, Function apply) { Object[] result = leaf; // optimistically assume we'll return our input unmodified @@ -1348,6 +1399,30 @@ private static Object[] transformLeaf(Object[] leaf, Function Object[] transformLeaf(Object[] leaf, BiFunction apply, I2 param) + { + Object[] result = leaf; // optimistically assume we'll return our input unmodified + int size = sizeOfLeaf(leaf); + for (int i = 0; i < size; ++i) + { + Object current = leaf[i]; + Object updated = apply.apply((I) current, param); + if (result == leaf) + { + if (current == updated) + continue; // if output still same as input, loop + + // otherwise initialise output to a copy of input up to this point + result = new Object[leaf.length]; + System.arraycopy(leaf, 0, result, 0, i); + } + result[i] = updated; + } + return result; + } + public static boolean equals(Object[] a, Object[] b) { return size(a) == size(b) && Iterators.elementsEqual(iterator(a), iterator(b));