You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by am...@apache.org on 2025/04/01 09:29:35 UTC

(ignite-3) 01/01: Optimize column offset calculation for BinaryTuples.

This is an automated email from the ASF dual-hosted git repository.

amashenkov pushed a commit to branch ignite-24975
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit c26f8f49b961584f73088431c1ea439165a71b84
Author: amashenkov <am...@apache.org>
AuthorDate: Mon Mar 31 22:48:16 2025 +0300

    Optimize column offset calculation for BinaryTuples.
---
 .../internal/binarytuple/BinaryTupleBuilder.java   | 36 +++------
 .../internal/binarytuple/BinaryTupleCommon.java    | 85 ++++++++++++++++++++++
 .../internal/binarytuple/BinaryTupleParser.java    | 71 +++++++-----------
 .../ignite/internal/schema/BinaryTuplePrefix.java  | 25 ++-----
 4 files changed, 131 insertions(+), 86 deletions(-)

diff --git a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java
index cfa9eb52bf0..16b6e4a486b 100644
--- a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java
+++ b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleBuilder.java
@@ -30,6 +30,8 @@ import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.Period;
 import java.util.UUID;
+import org.apache.ignite.internal.binarytuple.BinaryTupleCommon.OffsetReadFunction;
+import org.apache.ignite.internal.binarytuple.BinaryTupleCommon.OffsetWriteFunction;
 import org.apache.ignite.internal.util.ByteUtils;
 import org.jetbrains.annotations.Nullable;
 
@@ -55,6 +57,9 @@ public class BinaryTupleBuilder {
     /** Starting position of variable-length values. */
     private final int valueBase;
 
+    /** Offset table write function. */
+    private final OffsetWriteFunction offsetWriteFunc;
+
     /** Buffer for tuple content. */
     private ByteBuffer buffer;
 
@@ -99,6 +104,7 @@ public class BinaryTupleBuilder {
         } else {
             entrySize = BinaryTupleCommon.flagsToEntrySize(BinaryTupleCommon.valueSizeToFlags(totalValueSize));
         }
+        offsetWriteFunc = BinaryTupleCommon.offsetWriteFunction(entrySize);
 
         valueBase = entryBase + entrySize * numElements;
 
@@ -603,21 +609,15 @@ public class BinaryTupleBuilder {
 
             int getIndex = valueBase;
             int putIndex = valueBase;
+
+            OffsetReadFunction offsetSource = BinaryTupleCommon.offsetReadFunction(entrySize);
+            OffsetWriteFunction offsetTarget = BinaryTupleCommon.offsetWriteFunction(desiredEntrySize);
+
             while (getIndex > entryBase) {
                 getIndex -= entrySize;
                 putIndex -= desiredEntrySize;
 
-                int value;
-                if (entrySize == 4) {
-                    value = buffer.getInt(getIndex);
-                } else {
-                    value = Short.toUnsignedInt(buffer.getShort(getIndex));
-                }
-                if (desiredEntrySize == 1) {
-                    buffer.put(putIndex, (byte) value);
-                } else {
-                    buffer.putShort(putIndex, (short) value);
-                }
+                offsetTarget.offset(buffer, putIndex, offsetSource.offset(buffer, getIndex));
             }
 
             offset = (entrySize - desiredEntrySize) * numElements;
@@ -745,19 +745,7 @@ public class BinaryTupleBuilder {
         assert elementIndex < numElements : "Element index overflow: " + elementIndex + " >= " + numElements;
 
         int offset = buffer.position() - valueBase;
-        switch (entrySize) {
-            case Byte.BYTES:
-                buffer.put(entryBase + elementIndex, (byte) offset);
-                break;
-            case Short.BYTES:
-                buffer.putShort(entryBase + elementIndex * Short.BYTES, (short) offset);
-                break;
-            case Integer.BYTES:
-                buffer.putInt(entryBase + elementIndex * Integer.BYTES, offset);
-                break;
-            default:
-                assert false;
-        }
+        offsetWriteFunc.offset(buffer, entryBase + elementIndex * entrySize, offset);
 
         elementIndex++;
         return this;
diff --git a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommon.java b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommon.java
index 9f97c8e16cd..7a25779b0c7 100644
--- a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommon.java
+++ b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleCommon.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.binarytuple;
 
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.nio.ByteBuffer;
 import org.apache.ignite.internal.lang.IgniteInternalException;
 
 /**
@@ -123,4 +124,88 @@ public class BinaryTupleCommon {
 
         return value;
     }
+
+    /**
+     * Gets a function that reads the field offset.
+     *
+     * @param entrySize Offset table entry size.
+     * @return Offset calculation function.
+     */
+    public static OffsetReadFunction offsetReadFunction(int entrySize) {
+        switch (entrySize) {
+            case Byte.BYTES:
+                return BinaryTupleCommon::readByteOffset;
+            case Short.BYTES:
+                return BinaryTupleCommon::readShortOffset;
+            case Integer.BYTES:
+                return BinaryTupleCommon::readIntOffset;
+            case Long.BYTES:
+                throw new BinaryTupleFormatException("Unsupported offset table size");
+            default:
+                throw new BinaryTupleFormatException("Invalid offset table size");
+        }
+    }
+
+    /**
+     * Gets a function that writes the field offset.
+     *
+     * @param entrySize Offset table entry size.
+     * @return Offset calculation function.
+     */
+    public static OffsetWriteFunction offsetWriteFunction(int entrySize) {
+        switch (entrySize) {
+            case Byte.BYTES:
+                return BinaryTupleCommon::writeByteOffset;
+            case Short.BYTES:
+                return BinaryTupleCommon::writeShortOffset;
+            case Integer.BYTES:
+                return BinaryTupleCommon::writeIntOffset;
+            case Long.BYTES:
+                throw new BinaryTupleFormatException("Unsupported offset table size");
+            default:
+                throw new BinaryTupleFormatException("Invalid offset table size");
+        }
+    }
+
+    private static int readByteOffset(ByteBuffer buffer, int index) {
+        return Byte.toUnsignedInt(buffer.get(index));
+    }
+
+    private static int readShortOffset(ByteBuffer buffer, int index) {
+        return Short.toUnsignedInt(buffer.getShort(index));
+    }
+
+    private static int readIntOffset(ByteBuffer buffer, int index) {
+        int offset = buffer.getInt(index);
+        if (offset < 0) {
+            throw new BinaryTupleFormatException("Unsupported offset table size");
+        }
+        return offset;
+    }
+
+    private static void writeByteOffset(ByteBuffer buffer, int index, int offset) {
+        buffer.put(index, (byte) offset);
+    }
+
+    private static void writeShortOffset(ByteBuffer buffer, int index, int offset) {
+        buffer.putShort(index, (short) offset);
+    }
+
+    private static void writeIntOffset(ByteBuffer buffer, int index, int offset) {
+        buffer.putInt(index, offset);
+    }
+
+    /** Offset read function. */
+    @FunctionalInterface
+    public interface OffsetReadFunction {
+        /** Returns offset of the given column index. */
+        int offset(ByteBuffer buffer, int index);
+    }
+
+    /** Offset write function. */
+    @FunctionalInterface
+    public interface OffsetWriteFunction {
+        /** Writes offset of the given column index. */
+        void offset(ByteBuffer buffer, int index, int offset);
+    }
 }
diff --git a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleParser.java b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleParser.java
index cd28909643e..dd45e31cef7 100644
--- a/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleParser.java
+++ b/modules/binary-tuple/src/main/java/org/apache/ignite/internal/binarytuple/BinaryTupleParser.java
@@ -28,6 +28,7 @@ import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.Period;
 import java.util.UUID;
+import org.apache.ignite.internal.binarytuple.BinaryTupleCommon.OffsetReadFunction;
 import org.apache.ignite.internal.util.ByteUtils;
 
 /**
@@ -69,6 +70,9 @@ public class BinaryTupleParser {
     /** Binary tuple. */
     private final ByteBuffer buffer;
 
+    /** Offset calculation function. */
+    private final OffsetReadFunction offsetFunc;
+
     /**
      * Constructor.
      *
@@ -87,13 +91,15 @@ public class BinaryTupleParser {
         entryBase = BinaryTupleCommon.HEADER_SIZE;
         entrySize = 1 << (flags & BinaryTupleCommon.VARSIZE_MASK);
         valueBase = entryBase + entrySize * numElements;
+
+        offsetFunc = BinaryTupleCommon.offsetReadFunction(entrySize);
     }
 
     /**
      * Returns the binary tuple size in bytes.
      */
     public int size() {
-        return valueBase + getOffset(valueBase - entrySize);
+        return valueBase + offsetFunc.offset(buffer, valueBase - entrySize);
     }
 
     /**
@@ -122,12 +128,9 @@ public class BinaryTupleParser {
 
         int entry = entryBase + index * entrySize;
 
-        int offset = valueBase;
-        if (index > 0) {
-            offset += getOffset(entry - entrySize);
-        }
+        int offset = valueBase + ((index > 0) ? offsetFunc.offset(buffer, entry - entrySize) : 0);
 
-        int nextOffset = valueBase + getOffset(entry);
+        int nextOffset = valueBase + offsetFunc.offset(buffer, entry);
         if (nextOffset < offset) {
             throw new BinaryTupleFormatException("Corrupted offset table");
         }
@@ -145,7 +148,7 @@ public class BinaryTupleParser {
         int offset = valueBase;
 
         for (int i = 0; i < numElements; i++) {
-            int nextOffset = valueBase + getOffset(entry);
+            int nextOffset = valueBase + offsetFunc.offset(buffer, entry);
             if (nextOffset < offset) {
                 throw new BinaryTupleFormatException("Corrupted offset table");
             }
@@ -504,32 +507,6 @@ public class BinaryTupleParser {
         }
     }
 
-    /**
-     * Gets an entry from the value offset table.
-     *
-     * @param index Byte index of the table entry.
-     * @return Entry value.
-     */
-    private int getOffset(int index) {
-        switch (entrySize) {
-            case Byte.BYTES:
-                return Byte.toUnsignedInt(buffer.get(index));
-            case Short.BYTES:
-                return Short.toUnsignedInt(buffer.getShort(index));
-            case Integer.BYTES: {
-                int offset = buffer.getInt(index);
-                if (offset < 0) {
-                    throw new BinaryTupleFormatException("Unsupported offset table size");
-                }
-                return offset;
-            }
-            case Long.BYTES:
-                throw new BinaryTupleFormatException("Unsupported offset table size");
-            default:
-                throw new BinaryTupleFormatException("Invalid offset table size");
-        }
-    }
-
     /**
      * Gets array of bytes from a given range in the buffer.
      */
@@ -560,17 +537,23 @@ public class BinaryTupleParser {
         long time = Integer.toUnsignedLong(buffer.getInt(offset));
 
         int nanos;
-        if (length == 4) {
-            nanos = ((int) time & ((1 << 10) - 1)) * 1000 * 1000;
-            time >>>= 10;
-        } else if (length == 5) {
-            time |= Byte.toUnsignedLong(buffer.get(offset + 4)) << 32;
-            nanos = ((int) time & ((1 << 20) - 1)) * 1000;
-            time >>>= 20;
-        } else {
-            time |= Short.toUnsignedLong(buffer.getShort(offset + 4)) << 32;
-            nanos = ((int) time & ((1 << 30) - 1));
-            time >>>= 30;
+        switch (length) {
+            case 4: {
+                nanos = ((int) time & ((1 << 10) - 1)) * 1000 * 1000;
+                time >>>= 10;
+                break;
+            }
+            case 5: {
+                time |= Byte.toUnsignedLong(buffer.get(offset + 4)) << 32;
+                nanos = ((int) time & ((1 << 20) - 1)) * 1000;
+                time >>>= 20;
+                break;
+            }
+            default: {
+                time |= Short.toUnsignedLong(buffer.getShort(offset + 4)) << 32;
+                nanos = ((int) time & ((1 << 30) - 1));
+                time >>>= 30;
+            }
         }
 
         int second = ((int) time) & 63;
diff --git a/modules/schema/src/main/java/org/apache/ignite/internal/schema/BinaryTuplePrefix.java b/modules/schema/src/main/java/org/apache/ignite/internal/schema/BinaryTuplePrefix.java
index 041d46a135a..ea435bf70da 100644
--- a/modules/schema/src/main/java/org/apache/ignite/internal/schema/BinaryTuplePrefix.java
+++ b/modules/schema/src/main/java/org/apache/ignite/internal/schema/BinaryTuplePrefix.java
@@ -21,6 +21,7 @@ import static org.apache.ignite.internal.binarytuple.BinaryTupleCommon.PREFIX_FL
 
 import java.nio.ByteBuffer;
 import org.apache.ignite.internal.binarytuple.BinaryTupleCommon;
+import org.apache.ignite.internal.binarytuple.BinaryTupleCommon.OffsetWriteFunction;
 import org.apache.ignite.internal.binarytuple.BinaryTuplePrefixBuilder;
 import org.apache.ignite.internal.binarytuple.BinaryTupleReader;
 import org.apache.ignite.internal.lang.InternalTuple;
@@ -127,20 +128,9 @@ public class BinaryTuplePrefix extends BinaryTupleReader implements InternalTupl
                 .put(tupleBuffer.duplicate().limit(dataBeginOffsetHolder[0])); // header
 
         int payloadEndPosition = dataEndOffsetHolder[0] - dataBeginOffsetHolder[0];
+        OffsetWriteFunction offsetWriteFunction = BinaryTupleCommon.offsetWriteFunction(entrySize);
         for (int idx = tuple.elementCount(); idx < numElements; idx++) {
-            switch (entrySize) {
-                case Byte.BYTES:
-                    prefixBuffer.put((byte) payloadEndPosition);
-                    break;
-                case Short.BYTES:
-                    prefixBuffer.putShort((short) payloadEndPosition);
-                    break;
-                case Integer.BYTES:
-                    prefixBuffer.putInt(payloadEndPosition);
-                    break;
-                default:
-                    assert false;
-            }
+            offsetWriteFunction.offset(prefixBuffer, prefixBuffer.position(), payloadEndPosition);
         }
 
         prefixBuffer
@@ -156,14 +146,13 @@ public class BinaryTuplePrefix extends BinaryTupleReader implements InternalTupl
     private static BinaryTuplePrefix truncateTuple(int numElements, BinaryTuple tuple) {
         assert numElements < tuple.elementCount();
 
-        int[] dataBeginOffsetHolder = new int[1];
-        int[] dataEndOffsetHolder = new int[1];
+        int[] dataOffsetHolder = new int[2];
 
-        tuple.fetch(0, (index, begin, end) -> dataBeginOffsetHolder[0] = begin);
-        tuple.fetch(numElements - 1, (index, begin, end) -> dataEndOffsetHolder[0] = end);
+        tuple.fetch(0, (index, begin, end) -> dataOffsetHolder[0] = begin);
+        tuple.fetch(numElements - 1, (index, begin, end) -> dataOffsetHolder[1] = end);
 
         BinaryTuplePrefixBuilder builder = new BinaryTuplePrefixBuilder(
-                numElements, numElements, dataEndOffsetHolder[0] - dataBeginOffsetHolder[0]
+                numElements, numElements, dataOffsetHolder[1] - dataOffsetHolder[0]
         );
 
         for (int i = 0; i < numElements; i++) {