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

jt2594838 pushed a commit to branch dev/1.3
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/dev/1.3 by this push:
     new 4690d2e3c2b [Performance] Reduce aligned MemTable bitmap memory usage 
(#18249) (#18256)
4690d2e3c2b is described below

commit 4690d2e3c2b8d4fc75b47c057c20c2518b91c804
Author: Caideyipi <[email protected]>
AuthorDate: Mon Jul 20 19:02:10 2026 +0800

    [Performance] Reduce aligned MemTable bitmap memory usage (#18249) (#18256)
---
 .../dataregion/memtable/TsFileProcessor.java       | 14 ++++---
 .../db/utils/datastructure/AlignedTVList.java      | 46 ++++++++++++++++++----
 .../dataregion/memtable/TsFileProcessorTest.java   | 28 ++++++-------
 .../db/utils/datastructure/AlignedTVListTest.java  | 42 ++++++++++++++++++++
 4 files changed, 103 insertions(+), 27 deletions(-)

diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessor.java
index da62e80252b..8c966231fb1 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessor.java
@@ -725,8 +725,10 @@ public class TsFileProcessor {
       }
       // Here currentChunkPointNum >= 1
       if ((alignedMemChunk.alignedListSize() % 
PrimitiveArrayManager.ARRAY_SIZE) == 0) {
-        
dataTypesInTVList.addAll(alignedMemChunk.getWorkingTVList().getTsDataTypes());
         memTableIncrement += 
alignedMemChunk.getWorkingTVList().alignedTvListArrayMemCost();
+        for (TSDataType dataType : dataTypesInTVList) {
+          memTableIncrement += AlignedTVList.valueListArrayMemCost(dataType);
+        }
       }
     }
     updateMemoryInfo(memTableIncrement, chunkMetadataIncrement, 
textDataIncrement);
@@ -810,13 +812,13 @@ public class TsFileProcessor {
         int addingPointNum = addingPointNumInfo.right;
         // Here currentChunkPointNum + addingPointNum >= 1
         if (((currentChunkPointNum + addingPointNum) % 
PrimitiveArrayManager.ARRAY_SIZE) == 0) {
-          if (alignedMemChunk != null) {
-            
dataTypesInTVList.addAll(alignedMemChunk.getWorkingTVList().getTsDataTypes());
-          }
           dataTypesInTVList.addAll(addingPointNumInfo.left.values());
           memTableIncrement +=
               alignedMemChunk != null
                   ? 
alignedMemChunk.getWorkingTVList().alignedTvListArrayMemCost()
+                      + dataTypesInTVList.stream()
+                          .mapToLong(AlignedTVList::valueListArrayMemCost)
+                          .sum()
                   : AlignedTVList.alignedTvListArrayMemCost(
                       dataTypesInTVList.toArray(new TSDataType[0]));
         }
@@ -983,9 +985,11 @@ public class TsFileProcessor {
       }
       if (acquireArray != 0) {
         // memory of extending the TVList
-        
dataTypesInTVList.addAll(alignedMemChunk.getWorkingTVList().getTsDataTypes());
         memIncrements[0] +=
             acquireArray * 
alignedMemChunk.getWorkingTVList().alignedTvListArrayMemCost();
+        for (TSDataType dataType : dataTypesInTVList) {
+          memIncrements[0] += acquireArray * 
AlignedTVList.valueListArrayMemCost(dataType);
+        }
       }
     }
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java
index b99e784faad..9e3a556710c 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/datastructure/AlignedTVList.java
@@ -39,6 +39,7 @@ import org.apache.tsfile.read.filter.basic.Filter;
 import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.BitMap;
 import org.apache.tsfile.utils.Pair;
+import org.apache.tsfile.utils.RamUsageEstimator;
 import org.apache.tsfile.utils.ReadWriteIOUtils;
 import org.apache.tsfile.utils.TsPrimitiveType;
 import org.apache.tsfile.write.UnSupportedDataTypeException;
@@ -64,6 +65,11 @@ import static 
org.apache.tsfile.utils.RamUsageEstimator.NUM_BYTES_OBJECT_REF;
 
 public abstract class AlignedTVList extends TVList {
 
+  private static final long BITMAP_RAM_COST_PER_BLOCK =
+      RamUsageEstimator.shallowSizeOfInstance(BitMap.class)
+          + RamUsageEstimator.sizeOfByteArray(ARRAY_SIZE / Byte.SIZE + 1)
+          + NUM_BYTES_OBJECT_REF;
+
   // Data types of this aligned tvList
   protected List<TSDataType> dataTypes;
 
@@ -755,12 +761,37 @@ public abstract class AlignedTVList extends TVList {
       }
 
       /* 2.mask the column bitmap */
-      if (bitMaps != null && bitMaps[j] != null) {
+      if (bitMaps != null && bitMaps[j] != null && 
containsMarkedBit(bitMaps[j], idx, len)) {
         getBitMap(j, arrayIndex).merge(bitMaps[j], idx, elementIdx, len);
       }
     }
   }
 
+  private static boolean containsMarkedBit(BitMap bitMap, int start, int 
length) {
+    if (length <= 0) {
+      return false;
+    }
+
+    byte[] bytes = bitMap.getByteArray();
+    int end = start + length - 1;
+    int firstByteIndex = start >>> 3;
+    int lastByteIndex = end >>> 3;
+    if (firstByteIndex == lastByteIndex) {
+      int mask = (0xFF << (start & 7)) & (0xFF >>> (7 - (end & 7)));
+      return (bytes[firstByteIndex] & mask) != 0;
+    }
+
+    if ((bytes[firstByteIndex] & (0xFF << (start & 7))) != 0) {
+      return true;
+    }
+    for (int i = firstByteIndex + 1; i < lastByteIndex; i++) {
+      if (bytes[i] != 0) {
+        return true;
+      }
+    }
+    return (bytes[lastByteIndex] & (0xFF >>> (7 - (end & 7)))) != 0;
+  }
+
   private void arrayCopy(Object[] value, int idx, int arrayIndex, int 
elementIndex, int remaining) {
     for (int i = 0; i < values.size(); i++) {
       if (value[i] == null) {
@@ -822,14 +853,14 @@ public abstract class AlignedTVList extends TVList {
     if (bitMaps.get(columnIndex) == null) {
       List<BitMap> columnBitMaps = new ArrayList<>();
       for (int i = 0; i < values.get(columnIndex).size(); i++) {
-        columnBitMaps.add(new BitMap(ARRAY_SIZE, new byte[ARRAY_SIZE]));
+        columnBitMaps.add(null);
       }
       bitMaps.set(columnIndex, columnBitMaps);
     }
 
     // if the bitmap in arrayIndex is null, init the bitmap
     if (bitMaps.get(columnIndex).get(arrayIndex) == null) {
-      bitMaps.get(columnIndex).set(arrayIndex, new BitMap(ARRAY_SIZE, new 
byte[ARRAY_SIZE]));
+      bitMaps.get(columnIndex).set(arrayIndex, new BitMap(ARRAY_SIZE));
     }
 
     return bitMaps.get(columnIndex).get(arrayIndex);
@@ -864,6 +895,7 @@ public abstract class AlignedTVList extends TVList {
     for (TSDataType type : types) {
       if (type != null) {
         size += (long) ARRAY_SIZE * (long) type.getDataTypeSize();
+        size += BITMAP_RAM_COST_PER_BLOCK;
         measurementColumnNum++;
       }
     }
@@ -892,9 +924,7 @@ public abstract class AlignedTVList extends TVList {
       TSDataType type = dataTypes.get(column);
       if (type != null) {
         size += (long) PrimitiveArrayManager.ARRAY_SIZE * (long) 
type.getDataTypeSize();
-        if (bitMaps != null && bitMaps.get(column) != null) {
-          size += (long) PrimitiveArrayManager.ARRAY_SIZE / 8 + 1;
-        }
+        size += BITMAP_RAM_COST_PER_BLOCK;
       }
     }
     // size is 0 when all types are null
@@ -922,8 +952,8 @@ public abstract class AlignedTVList extends TVList {
     long size = 0;
     // value array mem size
     size += (long) PrimitiveArrayManager.ARRAY_SIZE * (long) 
type.getDataTypeSize();
-    // bitmap array mem size
-    size += (long) PrimitiveArrayManager.ARRAY_SIZE / 8 + 1;
+    // bitmap object, byte array, and reference in the bitmap list
+    size += BITMAP_RAM_COST_PER_BLOCK;
     // array headers mem size
     size += NUM_BYTES_ARRAY_HEADER;
     // Object references size in ArrayList
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessorTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessorTest.java
index 9a40ee0b36c..658faa971a3 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessorTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/storageengine/dataregion/memtable/TsFileProcessorTest.java
@@ -686,11 +686,11 @@ public class TsFileProcessorTest {
     // Test Tablet
     processor.insertTablet(genInsertTableNode(0, true), 0, 10, new 
TSStatus[10]);
     IMemTable memTable = processor.getWorkMemTable();
-    Assert.assertEquals(1596552, memTable.getTVListsRamCost());
+    Assert.assertEquals(1776552, memTable.getTVListsRamCost());
     processor.insertTablet(genInsertTableNode(100, true), 0, 10, new 
TSStatus[10]);
-    Assert.assertEquals(1596552, memTable.getTVListsRamCost());
+    Assert.assertEquals(1776552, memTable.getTVListsRamCost());
     processor.insertTablet(genInsertTableNode(200, true), 0, 10, new 
TSStatus[10]);
-    Assert.assertEquals(1596552, memTable.getTVListsRamCost());
+    Assert.assertEquals(1776552, memTable.getTVListsRamCost());
     Assert.assertEquals(90000, memTable.getTotalPointsNum());
     Assert.assertEquals(720360, memTable.memSize());
     // Test records
@@ -699,7 +699,7 @@ public class TsFileProcessorTest {
       record.addTuple(DataPoint.getDataPoint(dataType, measurementId, 
String.valueOf(i)));
       processor.insert(buildInsertRowNodeByTSRecord(record), new long[4]);
     }
-    Assert.assertEquals(1598168, memTable.getTVListsRamCost());
+    Assert.assertEquals(1778168, memTable.getTVListsRamCost());
     Assert.assertEquals(90100, memTable.getTotalPointsNum());
     Assert.assertEquals(721560, memTable.memSize());
   }
@@ -722,21 +722,21 @@ public class TsFileProcessorTest {
     // Test Tablet
     processor.insertTablet(genInsertTableNode(0, true), 0, 10, new 
TSStatus[10]);
     IMemTable memTable = processor.getWorkMemTable();
-    Assert.assertEquals(1596552, memTable.getTVListsRamCost());
+    Assert.assertEquals(1776552, memTable.getTVListsRamCost());
     processor.insertTablet(genInsertTableNodeFors3000ToS6000(0, true), 0, 10, 
new TSStatus[10]);
-    Assert.assertEquals(3219552, memTable.getTVListsRamCost());
+    Assert.assertEquals(3552552, memTable.getTVListsRamCost());
     processor.insertTablet(genInsertTableNode(100, true), 0, 10, new 
TSStatus[10]);
-    Assert.assertEquals(3219552, memTable.getTVListsRamCost());
+    Assert.assertEquals(3552552, memTable.getTVListsRamCost());
     processor.insertTablet(genInsertTableNodeFors3000ToS6000(100, true), 0, 
10, new TSStatus[10]);
-    Assert.assertEquals(3219552, memTable.getTVListsRamCost());
+    Assert.assertEquals(3552552, memTable.getTVListsRamCost());
     processor.insertTablet(genInsertTableNode(200, true), 0, 10, new 
TSStatus[10]);
-    Assert.assertEquals(3219552, memTable.getTVListsRamCost());
+    Assert.assertEquals(3552552, memTable.getTVListsRamCost());
     processor.insertTablet(genInsertTableNodeFors3000ToS6000(200, true), 0, 
10, new TSStatus[10]);
-    Assert.assertEquals(3219552, memTable.getTVListsRamCost());
+    Assert.assertEquals(3552552, memTable.getTVListsRamCost());
     processor.insertTablet(genInsertTableNode(300, true), 0, 10, new 
TSStatus[10]);
-    Assert.assertEquals(6466104, memTable.getTVListsRamCost());
+    Assert.assertEquals(7105104, memTable.getTVListsRamCost());
     processor.insertTablet(genInsertTableNodeFors3000ToS6000(300, true), 0, 
10, new TSStatus[10]);
-    Assert.assertEquals(6466104, memTable.getTVListsRamCost());
+    Assert.assertEquals(7105104, memTable.getTVListsRamCost());
 
     Assert.assertEquals(240000, memTable.getTotalPointsNum());
     Assert.assertEquals(1920960, memTable.memSize());
@@ -746,14 +746,14 @@ public class TsFileProcessorTest {
       record.addTuple(DataPoint.getDataPoint(dataType, measurementId, 
String.valueOf(i)));
       processor.insert(buildInsertRowNodeByTSRecord(record), new long[4]);
     }
-    Assert.assertEquals(6467720, memTable.getTVListsRamCost());
+    Assert.assertEquals(7106720, memTable.getTVListsRamCost());
     // Test records
     for (int i = 1; i <= 100; i++) {
       TSRecord record = new TSRecord(i, deviceId);
       record.addTuple(DataPoint.getDataPoint(dataType, "s1", 
String.valueOf(i)));
       processor.insert(buildInsertRowNodeByTSRecord(record), new long[4]);
     }
-    Assert.assertEquals(6469336, memTable.getTVListsRamCost());
+    Assert.assertEquals(7108336, memTable.getTVListsRamCost());
     Assert.assertEquals(240200, memTable.getTotalPointsNum());
     Assert.assertEquals(1923360, memTable.memSize());
   }
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/datastructure/AlignedTVListTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/datastructure/AlignedTVListTest.java
index 392209e6581..6a7206d39c5 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/datastructure/AlignedTVListTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/datastructure/AlignedTVListTest.java
@@ -27,8 +27,11 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
+import static 
org.apache.iotdb.db.storageengine.rescon.memory.PrimitiveArrayManager.ARRAY_SIZE;
+
 public class AlignedTVListTest {
 
   @Test
@@ -142,6 +145,45 @@ public class AlignedTVListTest {
     }
   }
 
+  @Test
+  public void testBitmapIsAllocatedLazilyWithCompactBackingArray() {
+    AlignedTVList tvList =
+        AlignedTVList.newAlignedList(Arrays.asList(TSDataType.INT64, 
TSDataType.INT64));
+    Object[] values = new Object[] {1L, 1L};
+    for (int i = 0; i < ARRAY_SIZE * 2 + 1; i++) {
+      tvList.putAlignedValue(i, values);
+    }
+
+    Assert.assertNull(tvList.getBitMaps());
+    tvList.putAlignedValue(ARRAY_SIZE * 2 + 1L, new Object[] {null, 1L});
+
+    List<BitMap> firstColumnBitMaps = tvList.getBitMaps().get(0);
+    Assert.assertEquals(3, firstColumnBitMaps.size());
+    Assert.assertNull(firstColumnBitMaps.get(0));
+    Assert.assertNull(firstColumnBitMaps.get(1));
+    Assert.assertNotNull(firstColumnBitMaps.get(2));
+    Assert.assertEquals(
+        ARRAY_SIZE / Byte.SIZE + 1, 
firstColumnBitMaps.get(2).getByteArray().length);
+    Assert.assertTrue(tvList.isNullValue(ARRAY_SIZE * 2 + 1, 0));
+    Assert.assertFalse(tvList.isNullValue(ARRAY_SIZE * 2, 0));
+  }
+
+  @Test
+  public void testEmptyInputBitmapsDoNotMaterializeMemTableBitmaps() {
+    AlignedTVList tvList = 
AlignedTVList.newAlignedList(Arrays.asList(TSDataType.INT64));
+    long[] times = new long[ARRAY_SIZE];
+    long[][] values = new long[1][ARRAY_SIZE];
+    BitMap[] bitMaps = new BitMap[] {new BitMap(ARRAY_SIZE)};
+    for (int i = 0; i < ARRAY_SIZE; i++) {
+      times[i] = i;
+      values[0][i] = i;
+    }
+
+    tvList.putAlignedValues(times, values, bitMaps, 0, ARRAY_SIZE);
+
+    Assert.assertNull(tvList.getBitMaps());
+  }
+
   @Test
   public void testClone() {
     List<TSDataType> dataTypes = new ArrayList<>();

Reply via email to