You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by co...@apache.org on 2026/07/01 03:06:39 UTC
(iotdb) branch research/encoding-subcolumn updated: Add SubColumn SQL support for integer and float types (#18077)
This is an automated email from the ASF dual-hosted git repository.
ColinLeeo pushed a commit to branch research/encoding-subcolumn
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/research/encoding-subcolumn by this push:
new 3adac1eded7 Add SubColumn SQL support for integer and float types (#18077)
3adac1eded7 is described below
commit 3adac1eded72ffce417e3796de16fbcf4387b032
Author: xjz17 <67...@users.noreply.github.com>
AuthorDate: Wed Jul 1 11:06:34 2026 +0800
Add SubColumn SQL support for integer and float types (#18077)
This syncs the SubColumn implementation used by the paper artifact, including FLOAT scaling support and SQL/schema tests.
---
.../db/queryengine/plan/parser/ASTVisitor.java | 11 +-
.../org/apache/iotdb/db/utils/SchemaUtils.java | 6 +-
.../plan/parser/StatementGeneratorTest.java | 26 +
.../org/apache/iotdb/db/utils/SchemaUtilsTest.java | 12 +
iotdb-core/tsfile/pom.xml | 23 +-
.../iotdb/tsfile/encoding/decoder/Decoder.java | 12 +
.../encoding/decoder/FloatSubColumnDecoder.java | 91 +
.../encoding/decoder/IntSubColumnDecoder.java | 85 +
.../encoding/decoder/LongSubColumnDecoder.java | 65 +
.../encoding/encoder/FloatSubColumnEncoder.java | 152 +
.../encoding/encoder/IntSubColumnEncoder.java | 113 +
.../encoding/encoder/LongSubColumnEncoder.java | 68 +
.../tsfile/encoding/encoder/TSEncodingBuilder.java | 23 +
.../tsfile/file/metadata/enums/TSEncoding.java | 5 +-
.../iotdb/tsfile/compress/CSVCompressTest.java | 269 ++
.../org/apache/iotdb/tsfile/encoding/ALPTest.java | 695 +++++
... => ASubcolumnAddDictionaryAndHuffmanTest.java} | 702 +++--
.../encoding/{BPTest.java => BPLongTest.java} | 478 ++--
.../iotdb/tsfile/encoding/BPQueryGroupTest.java | 126 +
.../iotdb/tsfile/encoding/BPQuerySortTest.java | 142 +
.../org/apache/iotdb/tsfile/encoding/BPTest.java | 224 +-
.../iotdb/tsfile/encoding/BPUpdateAppendTest.java | 125 +
.../tsfile/encoding/BPUpdateDeleteSmallerTest.java | 72 +
.../tsfile/encoding/BPUpdateInsertLargerTest.java | 68 +
.../tsfile/encoding/BPUpdateInsertSmallerTest.java | 68 +
.../iotdb/tsfile/encoding/BPUpdateLargerTest.java | 68 +
.../iotdb/tsfile/encoding/BPUpdateSmallerTest.java | 68 +
.../iotdb/tsfile/encoding/BUFFDouble2Test.java | 553 ++++
.../{BUFFTest.java => BUFFDoubleTest.java} | 543 ++--
.../org/apache/iotdb/tsfile/encoding/BUFFTest.java | 256 +-
.../apache/iotdb/tsfile/encoding/BitWeaving.java | 182 ++
.../tsfile/encoding/BuffOfficialRatioTest.java | 321 +++
.../iotdb/tsfile/encoding/BuffQueryMain.java | 373 +++
.../DatasetEncoderCompressRoundtripBenchTest.java | 2910 ++++++++++++++++++++
.../encoding/{BPTest.java => DeltaLongTest.java} | 543 ++--
.../tsfile/encoding/DeltaSubcolumnLongTest.java | 728 +++++
.../encoding/DictionaryLongOnSortedTest.java | 827 ++++++
.../iotdb/tsfile/encoding/DictionaryLongTest.java | 700 +++++
.../tsfile/encoding/DictionaryMaterializeTest.java | 1017 +++++++
.../encoding/DictionarySubcolumnLongTest.java | 749 +++++
.../encoding/FastPFORCompressionBenchmarkTest.java | 228 ++
.../tsfile/encoding/FastPForSimple8bCodec.java | 440 +++
.../org/apache/iotdb/tsfile/encoding/HBPIndex.java | 261 ++
.../apache/iotdb/tsfile/encoding/HBPIndexLong.java | 194 ++
.../tsfile/encoding/HBPIndexLongQueryTest.java | 327 +++
.../iotdb/tsfile/encoding/HBPIndexLongTest.java | 325 +++
.../iotdb/tsfile/encoding/ParquetSelectEqual.java | 444 +++
.../tsfile/encoding/ParquetSelectGreater.java | 451 +++
.../tsfile/encoding/ParquetSelectGreaterLess.java | 433 +++
.../iotdb/tsfile/encoding/ParquetSelectLess.java | 342 +++
.../tsfile/encoding/ParquetSelectLessPart.java | 538 ++++
.../iotdb/tsfile/encoding/ParquetSelectMax.java | 806 ++++++
.../tsfile/encoding/RLEBPLongOnSortedTest.java | 863 ++++++
.../iotdb/tsfile/encoding/RLEBPLongTest.java | 726 +++++
.../apache/iotdb/tsfile/encoding/RLEBPTest.java | 203 +-
.../iotdb/tsfile/encoding/RLEMaterializeTest.java | 995 +++++++
.../iotdb/tsfile/encoding/SPRINTZBPLongTest.java | 739 +++++
.../iotdb/tsfile/encoding/SPRINTZBPTest.java | 294 +-
.../tsfile/encoding/SPRINTZSubcolumnLongTest.java | 387 +++
.../encoding/SPRINTZSubcolumnPruneNewTest.java | 398 +++
.../tsfile/encoding/SPRINTZSubcolumnTest.java | 158 +-
.../encoding/Simple8bCompressionBenchmarkTest.java | 193 ++
.../tsfile/encoding/SubColumnEncodingTest.java | 100 +
.../encoding/SubcolumnAblationCodecSupport.java | 723 +++++
.../encoding/SubcolumnAblationPruneNewEngine.java | 1860 +++++++++++++
.../SubcolumnAddDictPruneNewUpdateAppendTest.java | 118 +
...lumnAddDictPruneNewUpdateDeleteSmallerTest.java | 124 +
...olumnAddDictPruneNewUpdateInsertLargerTest.java | 122 +
...lumnAddDictPruneNewUpdateInsertSmallerTest.java | 122 +
.../SubcolumnAddDictPruneNewUpdateLargerTest.java | 122 +
.../SubcolumnAddDictPruneNewUpdateRatioTest.java | 656 +++++
.../SubcolumnAddDictPruneNewUpdateSmallerTest.java | 122 +
.../SubcolumnAddDictPruneNewUpdateTestUtil.java | 161 ++
...est.java => SubcolumnAddDictPruneTimeTest.java} | 622 +++--
.../encoding/SubcolumnAddDictQueryGreaterTest.java | 372 +++
.../encoding/SubcolumnAddDictQueryGroupTest.java | 523 ++++
.../iotdb/tsfile/encoding/SubcolumnBPBetaTest.java | 175 ++
.../{SubcolumnTest.java => SubcolumnBPTest.java} | 365 +--
.../iotdb/tsfile/encoding/SubcolumnBetaTest.java | 197 +-
.../tsfile/encoding/SubcolumnBlockSizeTest.java | 153 +-
...ntTest.java => SubcolumnCountWithNULLTest.java} | 275 +-
.../SubcolumnFullEncodingTypeRatioTest.java | 572 ++++
.../iotdb/tsfile/encoding/SubcolumnFullTest.java | 74 +
...SubcolumnTest.java => SubcolumnLongBPTest.java} | 661 ++---
.../tsfile/encoding/SubcolumnLongBetaTest.java | 340 +++
.../encoding/SubcolumnLongBlockSizeTest.java | 213 ++
.../encoding/SubcolumnLongMaterializeR1D3.java | 1110 ++++++++
.../encoding/SubcolumnLongMaterializeR1D8.java | 1068 +++++++
...umnTest.java => SubcolumnLongOnSortedTest.java} | 561 ++--
.../encoding/SubcolumnLongPSOBlockSizeTest.java | 214 ++
...ubcolumnTest.java => SubcolumnLongPSOTest.java} | 736 +++--
.../encoding/SubcolumnLongPointQueryBPTest.java | 1177 ++++++++
...st.java => SubcolumnLongPointQueryRLETest.java} | 807 +++---
.../encoding/SubcolumnLongPointQueryTest.java | 887 ++++++
.../encoding/SubcolumnLongQueryCountTest.java | 224 ++
.../encoding/SubcolumnLongQueryEqualTest.java | 226 ++
.../encoding/SubcolumnLongQuerySum2Test.java | 201 ++
...ubcolumnTest.java => SubcolumnLongRLETest.java} | 537 ++--
.../{SubcolumnTest.java => SubcolumnLongTest.java} | 483 ++--
...essPartsTest.java => SubcolumnMaterialize.java} | 491 +++-
.../tsfile/encoding/SubcolumnMaterializeR1D8.java | 1038 +++++++
...lumnTest.java => SubcolumnMaxWithNULLTest.java} | 802 +++---
...lumnTest.java => SubcolumnNoPruneBetaTest.java} | 563 ++--
...bcolumnTest.java => SubcolumnOnValuesTest.java} | 488 ++--
...PTest.java => SubcolumnOnlyDictionaryTest.java} | 524 ++--
...umnTest.java => SubcolumnPointQueryBPTest.java} | 713 ++---
...mnTest.java => SubcolumnPointQueryRLETest.java} | 536 ++--
.../tsfile/encoding/SubcolumnPointQueryTest.java | 445 +++
.../tsfile/encoding/SubcolumnPrune2NewTest.java | 773 ++++++
.../encoding/SubcolumnPrune2NewTimeTest.java | 917 ++++++
...SubcolumnTest.java => SubcolumnPrune2Test.java} | 783 +++---
.../tsfile/encoding/SubcolumnPruneFastTest.java | 556 ++++
.../encoding/SubcolumnPruneNewBenchmarkTest.java | 120 +
.../tsfile/encoding/SubcolumnPruneNewBetaTest.java | 1027 +++++++
.../SubcolumnPruneNewBlockSizeNoPruneTest.java | 756 +++++
.../encoding/SubcolumnPruneNewBlockSizeTest.java | 149 +
.../SubcolumnPruneNewEncodingTypeRatioTest.java | 1142 ++++++++
.../tsfile/encoding/SubcolumnPruneNewTest.java | 2009 ++++++++++++++
...{SubcolumnTest.java => SubcolumnPruneTest.java} | 590 ++--
.../tsfile/encoding/SubcolumnQueryCount2Test.java | 135 +
.../tsfile/encoding/SubcolumnQueryCount3Test.java | 41 +
.../encoding/SubcolumnQueryCountNewTest.java | 176 ++
.../tsfile/encoding/SubcolumnQueryCountTest.java | 324 +--
.../encoding/SubcolumnQueryEqualNewTest.java | 214 ++
.../tsfile/encoding/SubcolumnQueryEqualTest.java | 324 +--
.../encoding/SubcolumnQueryGreaterLessTest.java | 342 ---
.../encoding/SubcolumnQueryGreaterNewTest.java | 220 ++
.../tsfile/encoding/SubcolumnQueryGreaterTest.java | 316 +--
.../tsfile/encoding/SubcolumnQueryLessNewTest.java | 200 ++
.../encoding/SubcolumnQueryLessPartsNewTest.java | 461 ++++
.../encoding/SubcolumnQueryLessPartsTest.java | 368 ---
.../tsfile/encoding/SubcolumnQueryLessTest.java | 309 ---
.../iotdb/tsfile/encoding/SubcolumnQueryMain.java | 1307 +++++++++
.../tsfile/encoding/SubcolumnQueryMaxNewTest.java | 185 ++
.../tsfile/encoding/SubcolumnQueryMaxTest.java | 281 --
.../tsfile/encoding/SubcolumnQuerySortTest.java | 492 ++++
.../tsfile/encoding/SubcolumnQuerySum2Test.java | 313 +--
.../tsfile/encoding/SubcolumnQuerySumTest.java | 287 +-
.../{SubcolumnTest.java => SubcolumnRLETest.java} | 227 +-
...ubcolumnTest.java => SubcolumnSAPHANATest.java} | 506 ++--
...um2Test.java => SubcolumnSum2WithNULLTest.java} | 274 +-
...ySumTest.java => SubcolumnSumWithNULLTest.java} | 252 +-
.../iotdb/tsfile/encoding/SubcolumnTest.java | 194 +-
.../tsfile/encoding/SubcolumnVariableAlpha.java | 1399 ++++++++++
.../SubcolumnVariableAlphaSignExpMass.java | 1336 +++++++++
.../tsfile/encoding/SubcolumnWithoutBPETest.java | 16 +
.../tsfile/encoding/SubcolumnWithoutDETest.java | 16 +
.../tsfile/encoding/SubcolumnWithoutRLETest.java | 16 +
.../iotdb/tsfile/encoding/TSDIFFLongTest.java | 694 +++++
.../tsfile/encoding/TSDIFFSubcolumnLongTest.java | 366 +++
.../encoding/TSDIFFSubcolumnPruneNewTest.java | 396 +++
.../iotdb/tsfile/encoding/TSDIFFSubcolumnTest.java | 155 +-
.../apache/iotdb/tsfile/encoding/TSDIFFTest.java | 283 +-
.../{SubcolumnTest.java => UpdateAppendTest.java} | 334 +--
...olumnTest.java => UpdateDeleteSmallerTest.java} | 491 ++--
...columnTest.java => UpdateInsertLargerTest.java} | 492 ++--
...nTest.java => UpdateInsertOneCompressTest.java} | 406 ++-
...olumnTest.java => UpdateInsertSmallerTest.java} | 495 ++--
.../{SubcolumnTest.java => UpdateLargerTest.java} | 548 ++--
.../{SubcolumnTest.java => UpdateSmallerTest.java} | 654 +++--
.../apache/iotdb/tsfile/encoding/VBPIndexLong.java | 348 +++
.../iotdb/tsfile/encoding/VBPIndexLongTest.java | 325 +++
.../iotdb/tsfile/encoding/VBPMaterializeTest.java | 104 +
.../iotdb/tsfile/encoding/VBPQueryCount2Test.java | 91 +
.../iotdb/tsfile/encoding/VBPQueryCountTest.java | 95 +
.../iotdb/tsfile/encoding/VBPQueryEqualTest.java | 105 +
.../tsfile/encoding/VBPQueryGreaterLessTest.java | 120 +
.../iotdb/tsfile/encoding/VBPQueryGreaterTest.java | 105 +
.../iotdb/tsfile/encoding/VBPQueryGroupTest.java | 196 ++
.../apache/iotdb/tsfile/encoding/VBPQueryMain.java | 629 +++++
.../iotdb/tsfile/encoding/VBPQueryMaxTest.java | 104 +
.../tsfile/encoding/VBPQuerySmallerPartsTest.java | 129 +
.../iotdb/tsfile/encoding/VBPQuerySmallerTest.java | 105 +
.../iotdb/tsfile/encoding/VBPQuerySortTest.java | 152 +
.../iotdb/tsfile/encoding/VBPQuerySumTest.java | 100 +
.../iotdb/tsfile/encoding/VBPUpdateAppendTest.java | 131 +
.../encoding/VBPUpdateDeleteSmallerTest.java | 129 +
.../tsfile/encoding/VBPUpdateInsertLargerTest.java | 131 +
.../encoding/VBPUpdateInsertSmallerTest.java | 131 +
.../iotdb/tsfile/encoding/VBPUpdateLargerTest.java | 131 +
.../tsfile/encoding/VBPUpdateSmallerTest.java | 131 +
.../encoding/elf/ElfDoublePrecisionBenchCodec.java | 58 +
.../elf/ElfDoublePrecisionBenchCodecTest.java | 42 +
.../encoding/elf/chimp/ElfInputBitStream.java | 1594 +++++++++++
.../encoding/elf/chimp/ElfOutputBitStream.java | 1304 +++++++++
.../elf/compressor/AbstractElfCompressor.java | 54 +
.../encoding/elf/compressor/ElfCompressor.java | 42 +
.../encoding/elf/compressor/ICompressor.java | 34 +
.../elf/decompressor/AbstractElfDecompressor.java | 54 +
.../encoding/elf/decompressor/ElfDecompressor.java | 27 +
.../encoding/elf/decompressor/IDecompressor.java | 26 +
.../tsfile/encoding/elf/utils/Elf64Utils.java | 163 ++
.../elf/xorcompressor/ElfXORCompressor.java | 165 ++
.../elf/xordecompressor/ElfXORDecompressor.java | 129 +
194 files changed, 65045 insertions(+), 12653 deletions(-)
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
index c8299c50a68..8b5da9ae58a 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
@@ -362,7 +362,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
String datatypeString =
props.get(IoTDBConstant.COLUMN_TIMESERIES_DATATYPE.toLowerCase()).toUpperCase();
try {
- createTimeSeriesStatement.setDataType(TSDataType.valueOf(datatypeString));
+ createTimeSeriesStatement.setDataType(parseTimeSeriesDataType(datatypeString));
props.remove(IoTDBConstant.COLUMN_TIMESERIES_DATATYPE.toLowerCase());
} catch (Exception e) {
throw new SemanticException(String.format("Unsupported datatype: %s", datatypeString));
@@ -3428,7 +3428,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
}
String dataTypeString = ctx.dataType.getText().toUpperCase();
try {
- dataType = TSDataType.valueOf(dataTypeString);
+ dataType = parseTimeSeriesDataType(dataTypeString);
} catch (Exception e) {
throw new SemanticException(String.format("Unsupported datatype: %s", dataTypeString));
}
@@ -3436,6 +3436,13 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
return dataType;
}
+ private TSDataType parseTimeSeriesDataType(String dataTypeString) {
+ if ("INTEGER".equals(dataTypeString)) {
+ return TSDataType.INT32;
+ }
+ return TSDataType.valueOf(dataTypeString);
+ }
+
@Override
public Statement visitShowSchemaTemplates(IoTDBSqlParser.ShowSchemaTemplatesContext ctx) {
return new ShowSchemaTemplateStatement();
diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
index efae314ecaa..e489fa2cb61 100644
--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
+++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
@@ -56,6 +56,7 @@ public class SchemaUtils {
intSet.add(TSEncoding.CHIMP);
intSet.add(TSEncoding.SPRINTZ);
intSet.add(TSEncoding.RLBE);
+ intSet.add(TSEncoding.SUBCOLUMN);
schemaChecker.put(TSDataType.INT32, intSet);
schemaChecker.put(TSDataType.INT64, intSet);
@@ -69,9 +70,12 @@ public class SchemaUtils {
floatSet.add(TSEncoding.CHIMP);
floatSet.add(TSEncoding.SPRINTZ);
floatSet.add(TSEncoding.RLBE);
+ floatSet.add(TSEncoding.SUBCOLUMN);
schemaChecker.put(TSDataType.FLOAT, floatSet);
- schemaChecker.put(TSDataType.DOUBLE, floatSet);
+ Set<TSEncoding> doubleSet = new HashSet<>(floatSet);
+ doubleSet.remove(TSEncoding.SUBCOLUMN);
+ schemaChecker.put(TSDataType.DOUBLE, doubleSet);
Set<TSEncoding> textSet = new HashSet<>();
textSet.add(TSEncoding.PLAIN);
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java
index ff0ec603365..23bbc395db7 100644
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java
@@ -280,6 +280,32 @@ public class StatementGeneratorTest {
assertEquals(SNAPPY, statement.getCompressor());
}
+ @Test
+ public void testCreateTimeSeriesWithSubColumnEncodingSql() throws IllegalPathException {
+ Statement parsed =
+ StatementGenerator.createStatement(
+ "create timeseries root.temperature INTEGER encoding=SubColumn",
+ ZonedDateTime.now().getOffset());
+
+ CreateTimeSeriesStatement statement = (CreateTimeSeriesStatement) parsed;
+ assertEquals(new PartialPath("root.temperature"), statement.getPath());
+ assertEquals(TSDataType.INT32, statement.getDataType());
+ assertEquals(TSEncoding.SUBCOLUMN, statement.getEncoding());
+ }
+
+ @Test
+ public void testCreateFloatTimeSeriesWithSubColumnEncodingSql() throws IllegalPathException {
+ Statement parsed =
+ StatementGenerator.createStatement(
+ "create timeseries root.temperature FLOAT encoding=SubColumn",
+ ZonedDateTime.now().getOffset());
+
+ CreateTimeSeriesStatement statement = (CreateTimeSeriesStatement) parsed;
+ assertEquals(new PartialPath("root.temperature"), statement.getPath());
+ assertEquals(TSDataType.FLOAT, statement.getDataType());
+ assertEquals(TSEncoding.SUBCOLUMN, statement.getEncoding());
+ }
+
@Test
public void testCreateAlignedTimeSeries() throws IllegalPathException {
TSCreateAlignedTimeseriesReq req =
diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/SchemaUtilsTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/SchemaUtilsTest.java
index 7c76d5bafcf..b21d3a6b896 100644
--- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/SchemaUtilsTest.java
+++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/utils/SchemaUtilsTest.java
@@ -75,4 +75,16 @@ public class SchemaUtilsTest {
// do nothing
}
}
+
+ @Test
+ public void checkSubColumnEncodingForInteger() throws MetadataException {
+ SchemaUtils.checkDataTypeWithEncoding(TSDataType.INT32, TSEncoding.SUBCOLUMN);
+ SchemaUtils.checkDataTypeWithEncoding(TSDataType.FLOAT, TSEncoding.SUBCOLUMN);
+ try {
+ SchemaUtils.checkDataTypeWithEncoding(TSDataType.TEXT, TSEncoding.SUBCOLUMN);
+ Assert.fail("expect exception");
+ } catch (MetadataException e) {
+ // do nothing
+ }
+ }
}
diff --git a/iotdb-core/tsfile/pom.xml b/iotdb-core/tsfile/pom.xml
index 39f45ce86b9..21546c96234 100644
--- a/iotdb-core/tsfile/pom.xml
+++ b/iotdb-core/tsfile/pom.xml
@@ -191,28 +191,37 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
- <version>1.21</version> <!-- 请检查是否有更新的版本 -->
+ <version>1.21</version>
+ <!-- 请检查是否有更新的版本 -->
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
+ <dependency>
+ <groupId>net.java.dev.jna</groupId>
+ <artifactId>jna-platform</artifactId>
+ <version>${jna.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <!-- ELF bench: bit streams from elf@elf (dsiutils fastutil) -->
+ <dependency>
+ <groupId>it.unimi.dsi</groupId>
+ <artifactId>dsiutils</artifactId>
+ <version>2.7.0</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>24.0.1</version>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.apache.iotdb</groupId>
- <artifactId>iotdb-antlr</artifactId>
- <version>0.14.0-SNAPSHOT</version>
- </dependency>
<dependency>
<groupId>me.lemire.integercompression</groupId>
<artifactId>JavaFastPFOR</artifactId>
- <version>[0.1,)</version>
+ <version>0.3.13</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
diff --git a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/Decoder.java b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/Decoder.java
index 7a01964a733..1333bcbbb00 100644
--- a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/Decoder.java
+++ b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/Decoder.java
@@ -161,6 +161,18 @@ public abstract class Decoder {
default:
throw new TsFileDecodingException(String.format(ERROR_MSG, encoding, dataType));
}
+ case SUBCOLUMN:
+ switch (dataType) {
+ case INT32:
+ return new IntSubColumnDecoder();
+ case INT64:
+ case VECTOR:
+ return new LongSubColumnDecoder();
+ case FLOAT:
+ return new FloatSubColumnDecoder();
+ default:
+ throw new TsFileDecodingException(String.format(ERROR_MSG, encoding, dataType));
+ }
default:
throw new TsFileDecodingException(String.format(ERROR_MSG, encoding, dataType));
}
diff --git a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/FloatSubColumnDecoder.java b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/FloatSubColumnDecoder.java
new file mode 100644
index 00000000000..48a3718e22c
--- /dev/null
+++ b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/FloatSubColumnDecoder.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.tsfile.encoding.decoder;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+import org.apache.iotdb.tsfile.utils.ReadWriteForEncodingUtils;
+
+import java.nio.ByteBuffer;
+
+/** Decoder for FLOAT values encoded by FloatSubColumnEncoder. */
+public class FloatSubColumnDecoder extends Decoder {
+
+ private float[] values;
+ private int index;
+
+ public FloatSubColumnDecoder() {
+ super(TSEncoding.SUBCOLUMN);
+ }
+
+ @Override
+ public float readFloat(ByteBuffer buffer) {
+ ensureLoaded(buffer);
+ return values[index++];
+ }
+
+ @Override
+ public boolean hasNext(ByteBuffer buffer) {
+ ensureLoaded(buffer);
+ return values != null && index < values.length;
+ }
+
+ @Override
+ public void reset() {
+ values = null;
+ index = 0;
+ }
+
+ private void ensureLoaded(ByteBuffer buffer) {
+ if (values != null || !buffer.hasRemaining()) {
+ return;
+ }
+ int count = ReadWriteForEncodingUtils.readUnsignedVarInt(buffer);
+ int precision = ReadWriteForEncodingUtils.readUnsignedVarInt(buffer);
+ int blockSize = ReadWriteForEncodingUtils.readUnsignedVarInt(buffer);
+ double scale = Math.pow(10, precision);
+ int[] scaledValues = new int[count];
+ int offset = 0;
+ while (offset < count) {
+ int length = ReadWriteForEncodingUtils.readUnsignedVarInt(buffer);
+ int min = buffer.getInt();
+ int bitWidth = buffer.get() & 0xFF;
+ int bytesPerPlane = (length + Byte.SIZE - 1) / Byte.SIZE;
+ for (int bit = 0; bit < bitWidth; bit++) {
+ for (int byteIndex = 0; byteIndex < bytesPerPlane; byteIndex++) {
+ int mask = buffer.get() & 0xFF;
+ for (int j = 0; j < Byte.SIZE; j++) {
+ int localIndex = (byteIndex << 3) + j;
+ if (localIndex < length && ((mask >>> j) & 1) != 0) {
+ scaledValues[offset + localIndex] |= 1 << bit;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < length; i++) {
+ scaledValues[offset + i] += min;
+ }
+ offset += Math.min(blockSize, length);
+ }
+ values = new float[count];
+ for (int i = 0; i < count; i++) {
+ values[i] = (float) (scaledValues[i] / scale);
+ }
+ }
+}
diff --git a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/IntSubColumnDecoder.java b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/IntSubColumnDecoder.java
new file mode 100644
index 00000000000..6178f5bbe9f
--- /dev/null
+++ b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/IntSubColumnDecoder.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.tsfile.encoding.decoder;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+import org.apache.iotdb.tsfile.utils.ReadWriteForEncodingUtils;
+
+import java.nio.ByteBuffer;
+
+/** Decoder for INT32 values encoded by {@link org.apache.iotdb.tsfile.encoding.encoder.IntSubColumnEncoder}. */
+public class IntSubColumnDecoder extends Decoder {
+
+ private int[] values;
+ private int index;
+
+ public IntSubColumnDecoder() {
+ super(TSEncoding.SUBCOLUMN);
+ }
+
+ @Override
+ public int readInt(ByteBuffer buffer) {
+ ensureLoaded(buffer);
+ return values[index++];
+ }
+
+ @Override
+ public boolean hasNext(ByteBuffer buffer) {
+ ensureLoaded(buffer);
+ return values != null && index < values.length;
+ }
+
+ @Override
+ public void reset() {
+ values = null;
+ index = 0;
+ }
+
+ private void ensureLoaded(ByteBuffer buffer) {
+ if (values != null || !buffer.hasRemaining()) {
+ return;
+ }
+ int count = ReadWriteForEncodingUtils.readUnsignedVarInt(buffer);
+ int blockSize = ReadWriteForEncodingUtils.readUnsignedVarInt(buffer);
+ values = new int[count];
+ int offset = 0;
+ while (offset < count) {
+ int length = ReadWriteForEncodingUtils.readUnsignedVarInt(buffer);
+ int min = buffer.getInt();
+ int bitWidth = buffer.get() & 0xFF;
+ int bytesPerPlane = (length + Byte.SIZE - 1) / Byte.SIZE;
+ for (int bit = 0; bit < bitWidth; bit++) {
+ for (int byteIndex = 0; byteIndex < bytesPerPlane; byteIndex++) {
+ int mask = buffer.get() & 0xFF;
+ for (int j = 0; j < Byte.SIZE; j++) {
+ int localIndex = (byteIndex << 3) + j;
+ if (localIndex < length && ((mask >>> j) & 1) != 0) {
+ values[offset + localIndex] |= 1 << bit;
+ }
+ }
+ }
+ }
+ for (int i = 0; i < length; i++) {
+ values[offset + i] += min;
+ }
+ offset += Math.min(blockSize, length);
+ }
+ }
+}
diff --git a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/LongSubColumnDecoder.java b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/LongSubColumnDecoder.java
new file mode 100644
index 00000000000..1d553e16a23
--- /dev/null
+++ b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/decoder/LongSubColumnDecoder.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.tsfile.encoding.decoder;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+import org.apache.iotdb.tsfile.utils.ReadWriteForEncodingUtils;
+
+import java.nio.ByteBuffer;
+
+/** Decoder for INT64 values encoded by {@link org.apache.iotdb.tsfile.encoding.encoder.LongSubColumnEncoder}. */
+public class LongSubColumnDecoder extends Decoder {
+
+ private long[] values;
+ private int index;
+
+ public LongSubColumnDecoder() {
+ super(TSEncoding.SUBCOLUMN);
+ }
+
+ @Override
+ public long readLong(ByteBuffer buffer) {
+ ensureLoaded(buffer);
+ return values[index++];
+ }
+
+ @Override
+ public boolean hasNext(ByteBuffer buffer) {
+ ensureLoaded(buffer);
+ return values != null && index < values.length;
+ }
+
+ @Override
+ public void reset() {
+ values = null;
+ index = 0;
+ }
+
+ private void ensureLoaded(ByteBuffer buffer) {
+ if (values != null || !buffer.hasRemaining()) {
+ return;
+ }
+ int count = ReadWriteForEncodingUtils.readUnsignedVarInt(buffer);
+ values = new long[count];
+ for (int i = 0; i < count; i++) {
+ values[i] = buffer.getLong();
+ }
+ }
+}
diff --git a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/FloatSubColumnEncoder.java b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/FloatSubColumnEncoder.java
new file mode 100644
index 00000000000..f989b53a73f
--- /dev/null
+++ b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/FloatSubColumnEncoder.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.tsfile.encoding.encoder;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+import org.apache.iotdb.tsfile.utils.ReadWriteForEncodingUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Encoder for FLOAT values using decimal scaling and sub-column bit-plane packing. */
+public class FloatSubColumnEncoder extends Encoder {
+
+ private static final int BLOCK_SIZE = 128;
+ private static final int MAX_FLOAT_DECIMAL_PRECISION = 6;
+
+ private final List<Float> values = new ArrayList<>();
+
+ public FloatSubColumnEncoder() {
+ super(TSEncoding.SUBCOLUMN);
+ }
+
+ @Override
+ public void encode(float value, ByteArrayOutputStream out) {
+ values.add(value);
+ }
+
+ @Override
+ public void flush(ByteArrayOutputStream out) throws IOException {
+ int precision = choosePrecision();
+ double scale = Math.pow(10, precision);
+ int[] scaledValues = new int[values.size()];
+ for (int i = 0; i < values.size(); i++) {
+ scaledValues[i] = (int) Math.round(values.get(i) * scale);
+ }
+
+ ReadWriteForEncodingUtils.writeUnsignedVarInt(scaledValues.length, out);
+ ReadWriteForEncodingUtils.writeUnsignedVarInt(precision, out);
+ ReadWriteForEncodingUtils.writeUnsignedVarInt(BLOCK_SIZE, out);
+ for (int offset = 0; offset < scaledValues.length; offset += BLOCK_SIZE) {
+ int length = Math.min(BLOCK_SIZE, scaledValues.length - offset);
+ writeBlock(out, scaledValues, offset, length);
+ }
+ values.clear();
+ }
+
+ private int choosePrecision() {
+ int precision = 0;
+ for (float value : values) {
+ precision = Math.max(precision, getDecimalPrecision(value));
+ }
+ precision = Math.min(precision, MAX_FLOAT_DECIMAL_PRECISION);
+ while (precision > 0 && !fitsInInt(precision)) {
+ precision--;
+ }
+ return precision;
+ }
+
+ private boolean fitsInInt(int precision) {
+ double scale = Math.pow(10, precision);
+ for (float value : values) {
+ double scaled = Math.rint(value * scale);
+ if (scaled < Integer.MIN_VALUE || scaled > Integer.MAX_VALUE) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static int getDecimalPrecision(float value) {
+ BigDecimal decimal = new BigDecimal(Float.toString(value)).stripTrailingZeros();
+ return Math.max(0, decimal.scale());
+ }
+
+ private static void writeBlock(
+ ByteArrayOutputStream out, int[] scaledValues, int offset, int length) throws IOException {
+ int min = scaledValues[offset];
+ long maxDelta = 0;
+ for (int i = 0; i < length; i++) {
+ int value = scaledValues[offset + i];
+ if (value < min) {
+ min = value;
+ }
+ }
+ for (int i = 0; i < length; i++) {
+ long delta = (long) scaledValues[offset + i] - min;
+ if (delta > maxDelta) {
+ maxDelta = delta;
+ }
+ }
+
+ int bitWidth = maxDelta == 0 ? 0 : 64 - Long.numberOfLeadingZeros(maxDelta);
+ ReadWriteForEncodingUtils.writeUnsignedVarInt(length, out);
+ writeInt(out, min);
+ out.write(bitWidth);
+ if (bitWidth == 0) {
+ return;
+ }
+
+ int bytesPerPlane = (length + Byte.SIZE - 1) / Byte.SIZE;
+ byte[] plane = new byte[bytesPerPlane];
+ for (int bit = 0; bit < bitWidth; bit++) {
+ for (int i = 0; i < bytesPerPlane; i++) {
+ plane[i] = 0;
+ }
+ for (int i = 0; i < length; i++) {
+ long delta = (long) scaledValues[offset + i] - min;
+ if (((delta >>> bit) & 1L) != 0) {
+ plane[i >>> 3] |= (byte) (1 << (i & 7));
+ }
+ }
+ out.write(plane);
+ }
+ }
+
+ private static void writeInt(ByteArrayOutputStream out, int value) {
+ out.write((value >>> 24) & 0xFF);
+ out.write((value >>> 16) & 0xFF);
+ out.write((value >>> 8) & 0xFF);
+ out.write(value & 0xFF);
+ }
+
+ @Override
+ public int getOneItemMaxSize() {
+ return Integer.BYTES + 1;
+ }
+
+ @Override
+ public long getMaxByteSize() {
+ return (long) values.size() * (Integer.BYTES + 1) + 16;
+ }
+}
diff --git a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/IntSubColumnEncoder.java b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/IntSubColumnEncoder.java
new file mode 100644
index 00000000000..c4c6c8a9071
--- /dev/null
+++ b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/IntSubColumnEncoder.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.tsfile.encoding.encoder;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+import org.apache.iotdb.tsfile.utils.ReadWriteForEncodingUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Encoder for INT32 values using block-level sub-column bit-plane packing. */
+public class IntSubColumnEncoder extends Encoder {
+
+ private static final int BLOCK_SIZE = 128;
+
+ private final List<Integer> values = new ArrayList<>();
+
+ public IntSubColumnEncoder() {
+ super(TSEncoding.SUBCOLUMN);
+ }
+
+ @Override
+ public void encode(int value, ByteArrayOutputStream out) {
+ values.add(value);
+ }
+
+ @Override
+ public void flush(ByteArrayOutputStream out) throws IOException {
+ ReadWriteForEncodingUtils.writeUnsignedVarInt(values.size(), out);
+ ReadWriteForEncodingUtils.writeUnsignedVarInt(BLOCK_SIZE, out);
+ for (int offset = 0; offset < values.size(); offset += BLOCK_SIZE) {
+ int length = Math.min(BLOCK_SIZE, values.size() - offset);
+ writeBlock(out, offset, length);
+ }
+ values.clear();
+ }
+
+ private void writeBlock(ByteArrayOutputStream out, int offset, int length) throws IOException {
+ int min = values.get(offset);
+ long maxDelta = 0;
+ for (int i = 0; i < length; i++) {
+ int value = values.get(offset + i);
+ if (value < min) {
+ min = value;
+ }
+ }
+ for (int i = 0; i < length; i++) {
+ long delta = (long) values.get(offset + i) - min;
+ if (delta > maxDelta) {
+ maxDelta = delta;
+ }
+ }
+
+ int bitWidth = maxDelta == 0 ? 0 : 64 - Long.numberOfLeadingZeros(maxDelta);
+ ReadWriteForEncodingUtils.writeUnsignedVarInt(length, out);
+ writeInt(out, min);
+ out.write(bitWidth);
+ if (bitWidth == 0) {
+ return;
+ }
+
+ int bytesPerPlane = (length + Byte.SIZE - 1) / Byte.SIZE;
+ byte[] plane = new byte[bytesPerPlane];
+ for (int bit = 0; bit < bitWidth; bit++) {
+ for (int i = 0; i < bytesPerPlane; i++) {
+ plane[i] = 0;
+ }
+ for (int i = 0; i < length; i++) {
+ long delta = (long) values.get(offset + i) - min;
+ if (((delta >>> bit) & 1L) != 0) {
+ plane[i >>> 3] |= (byte) (1 << (i & 7));
+ }
+ }
+ out.write(plane);
+ }
+ }
+
+ private static void writeInt(ByteArrayOutputStream out, int value) {
+ out.write((value >>> 24) & 0xFF);
+ out.write((value >>> 16) & 0xFF);
+ out.write((value >>> 8) & 0xFF);
+ out.write(value & 0xFF);
+ }
+
+ @Override
+ public int getOneItemMaxSize() {
+ return Integer.BYTES + 1;
+ }
+
+ @Override
+ public long getMaxByteSize() {
+ return (long) values.size() * (Integer.BYTES + 1) + 16;
+ }
+}
diff --git a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/LongSubColumnEncoder.java b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/LongSubColumnEncoder.java
new file mode 100644
index 00000000000..f50294705d5
--- /dev/null
+++ b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/LongSubColumnEncoder.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.tsfile.encoding.encoder;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
+import org.apache.iotdb.tsfile.utils.ReadWriteForEncodingUtils;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Encoder for INT64 values. This keeps SUBCOLUMN schema support lossless for long series. */
+public class LongSubColumnEncoder extends Encoder {
+
+ private final List<Long> values = new ArrayList<>();
+
+ public LongSubColumnEncoder() {
+ super(TSEncoding.SUBCOLUMN);
+ }
+
+ @Override
+ public void encode(long value, ByteArrayOutputStream out) {
+ values.add(value);
+ }
+
+ @Override
+ public void flush(ByteArrayOutputStream out) throws IOException {
+ ReadWriteForEncodingUtils.writeUnsignedVarInt(values.size(), out);
+ for (long value : values) {
+ writeLong(out, value);
+ }
+ values.clear();
+ }
+
+ private static void writeLong(ByteArrayOutputStream out, long value) {
+ for (int shift = 56; shift >= 0; shift -= 8) {
+ out.write((int) ((value >>> shift) & 0xFF));
+ }
+ }
+
+ @Override
+ public int getOneItemMaxSize() {
+ return Long.BYTES;
+ }
+
+ @Override
+ public long getMaxByteSize() {
+ return (long) values.size() * Long.BYTES + 8;
+ }
+}
diff --git a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/TSEncodingBuilder.java b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/TSEncodingBuilder.java
index 453aadfed2d..368bab10500 100644
--- a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/TSEncodingBuilder.java
+++ b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/encoding/encoder/TSEncodingBuilder.java
@@ -78,6 +78,8 @@ public abstract class TSEncodingBuilder {
return new Sprintz();
case RLBE:
return new RLBE();
+ case SUBCOLUMN:
+ return new SubColumn();
default:
throw new UnsupportedOperationException(type.toString());
}
@@ -353,6 +355,27 @@ public abstract class TSEncodingBuilder {
}
}
+ public static class SubColumn extends TSEncodingBuilder {
+ @Override
+ public Encoder getEncoder(TSDataType type) {
+ switch (type) {
+ case INT32:
+ return new IntSubColumnEncoder();
+ case INT64:
+ return new LongSubColumnEncoder();
+ case FLOAT:
+ return new FloatSubColumnEncoder();
+ default:
+ throw new UnSupportedDataTypeException("SUBCOLUMN doesn't support data type: " + type);
+ }
+ }
+
+ @Override
+ public void initFromProps(Map<String, String> props) {
+ // do nothing
+ }
+ }
+
public static class Dictionary extends TSEncodingBuilder {
@Override
diff --git a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/enums/TSEncoding.java b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/enums/TSEncoding.java
index 56cecda02e9..7c2e547e26b 100644
--- a/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/enums/TSEncoding.java
+++ b/iotdb-core/tsfile/src/main/java/org/apache/iotdb/tsfile/file/metadata/enums/TSEncoding.java
@@ -34,7 +34,8 @@ public enum TSEncoding {
FREQ((byte) 10),
CHIMP((byte) 11),
SPRINTZ((byte) 12),
- RLBE((byte) 13);
+ RLBE((byte) 13),
+ SUBCOLUMN((byte) 14);
private final byte type;
TSEncoding(byte type) {
@@ -79,6 +80,8 @@ public enum TSEncoding {
return TSEncoding.SPRINTZ;
case 13:
return TSEncoding.RLBE;
+ case 14:
+ return TSEncoding.SUBCOLUMN;
default:
throw new IllegalArgumentException("Invalid input: " + encoding);
}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/CSVCompressTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/CSVCompressTest.java
new file mode 100644
index 00000000000..c66b177e862
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/compress/CSVCompressTest.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.iotdb.tsfile.compress;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Supplier;
+
+public class CSVCompressTest {
+
+ private static final String PARENT_DIR = "D://github/xjz17/subcolumn/";
+ private static final String INPUT_PARENT_DIR = PARENT_DIR + "dataset/";
+ private static final String OUTPUT_PARENT_DIR = PARENT_DIR + "result/";
+ private static final String[] HEADERS = {
+ "Dataset",
+ "Encoding Algorithm",
+ "Encoding Time",
+ "Decoding Time",
+ "Points",
+ "Original Size",
+ "Compressed Size",
+ "Compression Ratio"
+ };
+
+ private static class CompressionBenchmark {
+ private final String algorithm;
+ private final String outputFileName;
+ private final Supplier<ICompressor> compressorSupplier;
+ private final Supplier<IUnCompressor> unCompressorSupplier;
+ private CsvWriter writer;
+
+ private CompressionBenchmark(
+ String algorithm,
+ String outputFileName,
+ Supplier<ICompressor> compressorSupplier,
+ Supplier<IUnCompressor> unCompressorSupplier) {
+ this.algorithm = algorithm;
+ this.outputFileName = outputFileName;
+ this.compressorSupplier = compressorSupplier;
+ this.unCompressorSupplier = unCompressorSupplier;
+ }
+ }
+
+ private static class BenchmarkResult {
+ private final long encodeTime;
+ private final long decodeTime;
+ private final int compressedSize;
+ private final double compressionRatio;
+
+ private BenchmarkResult(
+ long encodeTime, long decodeTime, int compressedSize, double compressionRatio) {
+ this.encodeTime = encodeTime;
+ this.decodeTime = decodeTime;
+ this.compressedSize = compressedSize;
+ this.compressionRatio = compressionRatio;
+ }
+ }
+
+ public static int getDecimalPrecision(String str) {
+ int decimalIndex = str.indexOf(".");
+
+ if (decimalIndex == -1) {
+ return 0;
+ }
+
+ return str.substring(decimalIndex + 1).length();
+ }
+
+ public static String extractFileName(String path) {
+ if (path == null || path.isEmpty()) {
+ return "";
+ }
+
+ File file = new File(path);
+ String fileName = file.getName();
+
+ int dotIndex = fileName.lastIndexOf('.');
+
+ if (dotIndex == -1 || dotIndex == 0) {
+ return fileName;
+ }
+
+ return fileName.substring(0, dotIndex);
+ }
+
+ @Test
+ public void test0() throws IOException {
+
+ int REPEAT_TIME = 100;
+ // REPEAT_TIME = 500;
+ // REPEAT_TIME = 10;
+
+ Files.createDirectories(Paths.get(OUTPUT_PARENT_DIR));
+
+ File directory = new File(INPUT_PARENT_DIR);
+ File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null || csvFiles.length == 0) {
+ return;
+ }
+ Arrays.sort(csvFiles, Comparator.comparing(File::getName));
+
+ List<CompressionBenchmark> benchmarks = new ArrayList<>();
+ benchmarks.add(
+ new CompressionBenchmark(
+ "GZIP",
+ "compress_gzip.csv",
+ ICompressor.GZIPCompressor::new,
+ IUnCompressor.GZIPUnCompressor::new));
+ benchmarks.add(
+ new CompressionBenchmark(
+ "LZ4",
+ "compress_lz4.csv",
+ ICompressor.IOTDBLZ4Compressor::new,
+ IUnCompressor.LZ4UnCompressor::new));
+ benchmarks.add(
+ new CompressionBenchmark(
+ "SNAPPY",
+ "compress_snappy.csv",
+ ICompressor.SnappyCompressor::new,
+ IUnCompressor.SnappyUnCompressor::new));
+ benchmarks.add(
+ new CompressionBenchmark(
+ "LZMA2",
+ "compress_lzma2.csv",
+ ICompressor.LZMA2Compressor::new,
+ IUnCompressor.LZMA2UnCompressor::new));
+ benchmarks.add(
+ new CompressionBenchmark(
+ "ZSTD",
+ "compress_zstd.csv",
+ ICompressor.ZstdCompressor::new,
+ IUnCompressor.ZstdUnCompressor::new));
+
+ try {
+ for (CompressionBenchmark benchmark : benchmarks) {
+ benchmark.writer =
+ new CsvWriter(
+ OUTPUT_PARENT_DIR + benchmark.outputFileName, ',', StandardCharsets.UTF_8);
+ benchmark.writer.setRecordDelimiter('\n');
+ benchmark.writer.writeRecord(HEADERS);
+ }
+
+ for (File file : csvFiles) {
+ String datasetName = extractFileName(file.toString());
+ System.out.println(datasetName);
+
+ int[] data;
+ try (InputStream inputStream = Files.newInputStream(file.toPath())) {
+ CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ ArrayList<Float> floatData = new ArrayList<>();
+
+ int maxDecimal = 0;
+ while (loader.readRecord()) {
+ String value = loader.getValues()[0];
+ if (value.isEmpty()) {
+ continue;
+ }
+ int currentDecimal = getDecimalPrecision(value);
+ if (currentDecimal > maxDecimal) {
+ maxDecimal = currentDecimal;
+ }
+ floatData.add(Float.valueOf(value));
+ }
+ loader.close();
+
+ if (maxDecimal > 8) {
+ maxDecimal = 8;
+ }
+
+ data = new int[floatData.size()];
+ int maxMul = (int) Math.pow(10, maxDecimal);
+ for (int i = 0; i < floatData.size(); i++) {
+ data[i] = (int) (floatData.get(i) * maxMul);
+ }
+ }
+
+ if (data.length == 0) {
+ continue;
+ }
+
+ ByteBuffer inputBuffer = ByteBuffer.allocate(data.length * Integer.BYTES);
+ for (int value : data) {
+ inputBuffer.putInt(value);
+ }
+ byte[] inputBytes = inputBuffer.array();
+
+ for (CompressionBenchmark benchmark : benchmarks) {
+ ICompressor compressor = benchmark.compressorSupplier.get();
+ IUnCompressor unCompressor = benchmark.unCompressorSupplier.get();
+
+ byte[] compressed = new byte[0];
+ long start = System.nanoTime();
+ for (int repeat = 0; repeat < REPEAT_TIME; repeat++) {
+ compressed = compressor.compress(inputBytes);
+ }
+ long end = System.nanoTime();
+ long encodeTime = (end - start) / REPEAT_TIME;
+
+ byte[] restored = new byte[inputBytes.length];
+ start = System.nanoTime();
+ for (int repeat = 0; repeat < REPEAT_TIME; repeat++) {
+ restored = new byte[inputBytes.length];
+ int uncompressedSize =
+ unCompressor.uncompress(compressed, 0, compressed.length, restored, 0);
+ Assert.assertEquals(inputBytes.length, uncompressedSize);
+ }
+ end = System.nanoTime();
+ long decodeTime = (end - start) / REPEAT_TIME;
+
+ Assert.assertArrayEquals(inputBytes, restored);
+ BenchmarkResult result =
+ new BenchmarkResult(
+ encodeTime,
+ decodeTime,
+ compressed.length,
+ compressed.length / (double) inputBytes.length);
+
+ String[] record = {
+ datasetName,
+ benchmark.algorithm,
+ String.valueOf(result.encodeTime),
+ String.valueOf(result.decodeTime),
+ String.valueOf(data.length),
+ String.valueOf(inputBytes.length),
+ String.valueOf(result.compressedSize),
+ String.valueOf(result.compressionRatio)
+ };
+ benchmark.writer.writeRecord(record);
+ }
+ }
+ } finally {
+ for (CompressionBenchmark benchmark : benchmarks) {
+ if (benchmark.writer != null) {
+ benchmark.writer.close();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/ALPTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/ALPTest.java
new file mode 100644
index 00000000000..094fbf31565
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/ALPTest.java
@@ -0,0 +1,695 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+
+public class ALPTest {
+
+ public static int bitWidth(int value) {
+ return 32 - Integer.numberOfLeadingZeros(value);
+ }
+
+ public static int bitWidth(long value) {
+ return 64 - Long.numberOfLeadingZeros(value);
+ }
+
+ public static void intToBytes(int srcNum, byte[] result, int pos, int width) {
+ int cnt = pos & 0x07;
+ int index = pos >> 3;
+ while (width > 0) {
+ int m = width + cnt >= 8 ? 8 - cnt : width;
+ width -= m;
+ int mask = 1 << (8 - cnt);
+ cnt += m;
+ byte y = (byte) (srcNum >>> width);
+ y = (byte) (y << (8 - cnt));
+ mask = ~(mask - (1 << (8 - cnt)));
+ result[index] = (byte) (result[index] & mask | y);
+ srcNum = srcNum & ~(-1 << width);
+ if (cnt == 8) {
+ index++;
+ cnt = 0;
+ }
+ }
+ }
+
+ public static int bytesToInt(byte[] result, int pos, int width) {
+ int ret = 0;
+ int cnt = pos & 0x07;
+ int index = pos >> 3;
+ while (width > 0) {
+ int m = width + cnt >= 8 ? 8 - cnt : width;
+ width -= m;
+ ret = ret << m;
+ byte y = (byte) (result[index] & (0xff >> cnt));
+ y = (byte) ((y & 0xff) >>> (8 - cnt - m));
+ ret = ret | (y & 0xff);
+ cnt += m;
+ if (cnt == 8) {
+ cnt = 0;
+ index++;
+ }
+ }
+ return ret;
+ }
+
+ public static void boolToBytes(boolean value, byte[] result, int pos) {
+ int byteIndex = pos >> 3;
+ int bitOffset = pos & 0x07;
+
+ if (value) {
+ result[byteIndex] |= (1 << (7 - bitOffset));
+ } else {
+ result[byteIndex] &= ~(1 << (7 - bitOffset));
+ }
+ }
+
+ public static boolean bytesToBool(byte[] result, int pos) {
+ int byteIndex = pos >> 3;
+ int bitOffset = pos & 0x07;
+
+ return (result[byteIndex] & (1 << (7 - bitOffset))) != 0;
+ }
+
+ public static void longToBytes(long srcNum, byte[] result, int pos, int width) {
+ int cnt = pos & 0x07;
+ int index = pos >> 3;
+
+ while (width > 0) {
+ int m = width + cnt >= 8 ? 8 - cnt : width;
+ width -= m;
+ int mask = 1 << (8 - cnt);
+ cnt += m;
+ byte y = (byte) (srcNum >>> width);
+ y = (byte) (y << (8 - cnt));
+ mask = ~(mask - (1 << (8 - cnt)));
+ result[index] = (byte) (result[index] & mask | y);
+ srcNum = srcNum & ~(-1L << width);
+ if (cnt == 8) {
+ index++;
+ cnt = 0;
+ }
+ }
+ }
+
+ public static long bytesToLong(byte[] result, int pos, int width) {
+ long ret = 0;
+ int cnt = pos & 0x07;
+ int index = pos >> 3;
+ while (width > 0) {
+ int m = width + cnt >= 8 ? 8 - cnt : width;
+ width -= m;
+ ret = ret << m;
+ byte y = (byte) (result[index] & (0xff >> cnt));
+ y = (byte) ((y & 0xff) >>> (8 - cnt - m));
+ ret = ret | (y & 0xff);
+ cnt += m;
+ if (cnt == 8) {
+ cnt = 0;
+ index++;
+ }
+ }
+ return ret;
+ }
+
+ public static void pack8Values(int[] values, int offset, int width, int encode_pos,
+ byte[] encoded_result) {
+ int bufIdx = 0;
+ int valueIdx = offset;
+ int leftBit = 0;
+
+ while (valueIdx < 8 + offset) {
+ int buffer = 0;
+ int leftSize = 32;
+
+ if (leftBit > 0) {
+ buffer |= (values[valueIdx] << (32 - leftBit));
+ leftSize -= leftBit;
+ leftBit = 0;
+ valueIdx++;
+ }
+
+ while (leftSize >= width && valueIdx < 8 + offset) {
+ buffer |= (values[valueIdx] << (leftSize - width));
+ leftSize -= width;
+ valueIdx++;
+ }
+
+ if (leftSize > 0 && valueIdx < 8 + offset) {
+ buffer |= (values[valueIdx] >>> (width - leftSize));
+ leftBit = width - leftSize;
+ }
+
+ for (int j = 0; j < 4; j++) {
+ encoded_result[encode_pos] = (byte) ((buffer >>> ((3 - j) * 8)) & 0xFF);
+ encode_pos++;
+ bufIdx++;
+ if (bufIdx >= width) {
+ return;
+ }
+ }
+ }
+
+ }
+
+ public static void pack8Values(
+ long[] values, int offset, int width, int encode_pos, byte[] encoded_result) {
+ int bufIdx = 0;
+ int valueIdx = offset;
+ int leftBit = 0;
+
+ while (valueIdx < 8 + offset) {
+ long buffer = 0;
+ int leftSize = 64;
+
+ if (leftBit > 0) {
+ buffer |= (values[valueIdx] << (64 - leftBit));
+ leftSize -= leftBit;
+ leftBit = 0;
+ valueIdx++;
+ }
+
+ while (leftSize >= width && valueIdx < 8 + offset) {
+ buffer |= (values[valueIdx] << (leftSize - width));
+ leftSize -= width;
+ valueIdx++;
+ }
+ if (leftSize > 0 && valueIdx < 8 + offset) {
+ buffer |= (values[valueIdx] >>> (width - leftSize));
+ leftBit = width - leftSize;
+ }
+
+ for (int j = 0; j < 8; j++) {
+ encoded_result[encode_pos] = (byte) ((buffer >>> ((8 - j - 1) * 8)) & 0xFF);
+ encode_pos++;
+ bufIdx++;
+ if (bufIdx >= width * 8 / 8) {
+ return;
+ }
+ }
+ }
+ }
+
+ public static void unpack8Values(byte[] encoded, int offset, int width, int[] result_list, int result_offset) {
+ int byteIdx = offset;
+ long buffer = 0;
+ int totalBits = 0;
+ int valueIdx = 0;
+
+ while (valueIdx < 8) {
+ while (totalBits < width) {
+ buffer = (buffer << 8) | (encoded[byteIdx] & 0xFF);
+ byteIdx++;
+ totalBits += 8;
+ }
+
+ while (totalBits >= width && valueIdx < 8) {
+ result_list[result_offset + valueIdx] = (int) (buffer >>> (totalBits - width));
+ valueIdx++;
+ totalBits -= width;
+ buffer = buffer & ((1L << totalBits) - 1);
+ }
+ }
+ }
+
+ public static void unpack8Values(
+ byte[] encoded, int offset, int width, long[] result_list, int result_offset) {
+ int byteIdx = offset;
+ long buffer = 0;
+ int totalBits = 0;
+ int valueIdx = 0;
+
+ while (valueIdx < 8) {
+ while (totalBits < width) {
+ buffer = (buffer << 8) | (encoded[byteIdx] & 0xFF);
+ byteIdx++;
+ totalBits += 8;
+ }
+
+ while (totalBits >= width && valueIdx < 8) {
+ result_list[result_offset + valueIdx] = buffer >>> (totalBits - width);
+ valueIdx++;
+ totalBits -= width;
+ buffer = buffer & ((1L << totalBits) - 1);
+ }
+ }
+ }
+
+ public static int bitPacking(int[] numbers, int bit_width, int encode_pos,
+ byte[] encoded_result, int num_values) {
+ int block_num = num_values / 8;
+ int remainder = num_values % 8;
+
+ for (int i = 0; i < block_num; i++) {
+ pack8Values(numbers, i * 8, bit_width, encode_pos, encoded_result);
+ encode_pos += bit_width;
+ }
+
+ encode_pos *= 8;
+
+ for (int i = 0; i < remainder; i++) {
+ intToBytes(numbers[block_num * 8 + i], encoded_result, encode_pos, bit_width);
+ encode_pos += bit_width;
+ }
+
+ return (encode_pos + 7) / 8;
+ }
+
+ public static int bitPacking(long[] numbers, int bit_width, int encode_pos,
+ byte[] encoded_result, int num_values) {
+ int block_num = num_values / 8;
+ int remainder = num_values % 8;
+
+ for (int i = 0; i < block_num; i++) {
+ pack8Values(numbers, i * 8, bit_width, encode_pos, encoded_result);
+ encode_pos += bit_width;
+ }
+
+ encode_pos *= 8;
+
+ for (int i = 0; i < remainder; i++) {
+ longToBytes(numbers[block_num * 8 + i], encoded_result, encode_pos, bit_width);
+ encode_pos += bit_width;
+ }
+
+ return (encode_pos + 7) / 8;
+ }
+
+ public static int decodeBitPacking(
+ byte[] encoded, int decode_pos, int bit_width, int num_values, int[] result_list) {
+ int block_num = num_values / 8;
+ int remainder = num_values % 8;
+
+ for (int i = 0; i < block_num; i++) {
+ unpack8Values(encoded, decode_pos, bit_width, result_list, i * 8);
+ decode_pos += bit_width;
+ }
+
+ decode_pos *= 8;
+
+ for (int i = 0; i < remainder; i++) {
+ result_list[block_num * 8 + i] = bytesToInt(encoded, decode_pos, bit_width);
+ decode_pos += bit_width;
+ }
+
+ return (decode_pos + 7) / 8;
+ }
+
+ public static int decodeBitPacking(
+ byte[] encoded, int decode_pos, int bit_width, int num_values, long[] result_list) {
+ int block_num = num_values / 8;
+ int remainder = num_values % 8;
+
+ for (int i = 0; i < block_num; i++) {
+ unpack8Values(encoded, decode_pos, bit_width, result_list, i * 8);
+ decode_pos += bit_width;
+ }
+
+ decode_pos *= 8;
+
+ for (int i = 0; i < remainder; i++) {
+ result_list[block_num * 8 + i] = bytesToLong(encoded, decode_pos, bit_width);
+ decode_pos += bit_width;
+ }
+
+ return (decode_pos + 7) / 8;
+ }
+
+ public static void int2Bytes(int integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer >> 24);
+ cur_byte[encode_pos + 1] = (byte) (integer >> 16);
+ cur_byte[encode_pos + 2] = (byte) (integer >> 8);
+ cur_byte[encode_pos + 3] = (byte) (integer);
+ }
+
+ public static void intByte2Bytes(int integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer);
+ }
+
+ public static void long2Bytes(long integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer >> 56);
+ cur_byte[encode_pos + 1] = (byte) (integer >> 48);
+ cur_byte[encode_pos + 2] = (byte) (integer >> 40);
+ cur_byte[encode_pos + 3] = (byte) (integer >> 32);
+ cur_byte[encode_pos + 4] = (byte) (integer >> 24);
+ cur_byte[encode_pos + 5] = (byte) (integer >> 16);
+ cur_byte[encode_pos + 6] = (byte) (integer >> 8);
+ cur_byte[encode_pos + 7] = (byte) (integer);
+ }
+
+ public static int bytes2Integer(byte[] encoded, int start, int num) {
+ int value = 0;
+
+ for (int i = 0; i < num; i++) {
+ value <<= 8;
+ int b = encoded[i + start] & 0xFF;
+ value |= b;
+ }
+ return value;
+ }
+
+ public static long bytes2Long(byte[] encoded, int start, int num) {
+ long value = 0;
+
+ for (int i = 0; i < num; i++) {
+ value <<= 8;
+ int b = encoded[i + start] & 0xFF;
+ value |= b;
+ }
+ return value;
+ }
+
+ public static int BlockEncoder(double[] data, int block_index, int block_size, int remainder, int encode_pos,
+ int max_decimal,
+ byte[] encoded_result) {
+ int e = 0;
+ int f = 0;
+
+ int base = block_index * block_size;
+
+ e = 17;
+ f = 17 - max_decimal;
+
+ double[] testValues = new double[remainder];
+ for (int i = 0; i < testValues.length; i++) {
+ testValues[i] = data[base + (i * remainder / testValues.length)];
+ }
+
+ outer_loop: for (int i = 0; i < 19; i++) {
+ for (int j = i; j >= 0; j--) {
+ boolean valid = true;
+
+ for (double value : testValues) {
+ // double encodedValue = n * Math.pow(10, i) * Math.pow(10, -j);
+ // double encodedValue = n * power(10, i) / power(10, j);
+ long encodedValue = Math.round(value * power(10, i) / power(10, j));
+
+ double decodedValue = encodedValue / power(10, i) * power(10, j);
+ // if (Math.abs(n - decodedValue) > 0.00001) {
+ if (Math.abs(value - decodedValue) > Math.pow(10, -max_decimal)) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid) {
+ e = i;
+ f = j;
+
+ break outer_loop;
+ }
+ }
+ }
+
+ intByte2Bytes(e, encode_pos, encoded_result);
+ encode_pos += 1;
+
+ intByte2Bytes(f, encode_pos, encoded_result);
+ encode_pos += 1;
+
+ long[] data_long = new long[remainder];
+
+ long min_data = Long.MAX_VALUE;
+ long max_data = Long.MIN_VALUE;
+
+ for (int i = 0; i < remainder; i++) {
+ data_long[i] = Math.round(data[base + i] * power(10, e) / power(10, f));
+ if (data_long[i] < min_data) {
+ min_data = data_long[i];
+ }
+ if (data_long[i] > max_data) {
+ max_data = data_long[i];
+ }
+ }
+
+ long2Bytes(min_data, encode_pos, encoded_result);
+ encode_pos += 8;
+
+ for (int i = 0; i < remainder; i++) {
+ data_long[i] -= min_data;
+ }
+ long max_diff = max_data - min_data;
+ int bit_width = bitWidth(max_diff);
+
+ intByte2Bytes(bit_width, encode_pos, encoded_result);
+ encode_pos += 1;
+
+ encode_pos = bitPacking(data_long, bit_width, encode_pos, encoded_result, remainder);
+ return encode_pos;
+
+ }
+
+ public static int BlockDecoder(byte[] encoded_result, int block_index, int block_size, int remainder,
+ int encode_pos, double[] data) {
+ int e = bytes2Integer(encoded_result, encode_pos, 1);
+ encode_pos += 1;
+
+ int f = bytes2Integer(encoded_result, encode_pos, 1);
+ encode_pos += 1;
+
+ long min_data = bytes2Long(encoded_result, encode_pos, 8);
+ encode_pos += 8;
+
+ // System.out.println("min_data: " + min_data);
+
+ int bit_width = bytes2Integer(encoded_result, encode_pos, 1);
+ encode_pos += 1;
+
+ // System.out.println("bit_width: " + bit_width);
+
+ long[] data_long = new long[remainder];
+
+ encode_pos = decodeBitPacking(encoded_result, encode_pos, bit_width, remainder, data_long);
+
+ for (int i = 0; i < remainder; i++) {
+ data_long[i] += min_data;
+ // data[block_index * block_size + i] = data_long[i] * Math.pow(10, f) *
+ // Math.pow(10, -e);
+ data[block_index * block_size + i] = data_long[i] * power(10, f) / power(10, e);
+ }
+
+ return encode_pos;
+ }
+
+ public static int Encoder(double[] data, int block_size, int max_decimal, byte[] encoded_result) {
+ int data_length = data.length;
+ int encode_pos = 0;
+
+ int2Bytes(data_length, encode_pos, encoded_result);
+ encode_pos += 4;
+
+ int2Bytes(block_size, encode_pos, encoded_result);
+ encode_pos += 4;
+
+ int num_blocks = data_length / block_size;
+
+ int remainder = data_length % block_size;
+
+ for (int i = 0; i < num_blocks; i++) {
+ encode_pos = BlockEncoder(data, i, block_size, block_size, encode_pos, max_decimal, encoded_result);
+ }
+
+ if (remainder > 0) {
+ encode_pos = BlockEncoder(data, num_blocks, block_size, remainder, encode_pos,
+ max_decimal, encoded_result);
+ }
+
+ return encode_pos;
+ }
+
+ public static double[] Decoder(byte[] encoded_result) {
+ int encode_pos = 0;
+
+ int data_length = bytes2Integer(encoded_result, encode_pos, 4);
+ encode_pos += 4;
+
+ int block_size = bytes2Integer(encoded_result, encode_pos, 4);
+ encode_pos += 4;
+
+ int num_blocks = data_length / block_size;
+ int remainder = data_length % block_size;
+
+ double[] data = new double[data_length];
+
+ for (int i = 0; i < num_blocks; i++) {
+ encode_pos = BlockDecoder(encoded_result, i, block_size, block_size, encode_pos, data);
+ }
+
+ if (remainder > 0) {
+ encode_pos = BlockDecoder(encoded_result, num_blocks, block_size, remainder, encode_pos, data);
+ }
+
+ return data;
+ }
+
+ public static double power(int base, int exponent) {
+ double result = 1;
+ for (int i = 0; i < exponent; i++) {
+ result *= base;
+ }
+ return result;
+ }
+
+ public static int getDecimalPrecision(String str) {
+ int decimalIndex = str.indexOf(".");
+
+ if (decimalIndex == -1) {
+ return 0;
+ }
+
+ return str.substring(decimalIndex + 1).length();
+ }
+
+ public static String extractFileName(String path) {
+ if (path == null || path.isEmpty()) {
+ return "";
+ }
+
+ File file = new File(path);
+ String fileName = file.getName();
+
+ int dotIndex = fileName.lastIndexOf('.');
+
+ if (dotIndex == -1 || dotIndex == 0) {
+ return fileName;
+ }
+
+ return fileName.substring(0, dotIndex);
+ }
+
+ @Test
+ public void test0() throws IOException {
+ String parent_dir = "path/to/your/directory/";
+
+ String input_parent_dir = parent_dir + "dataset/";
+
+ String output_parent_dir = parent_dir + "result/";
+
+ String outputPath = output_parent_dir + "alp.csv";
+
+ int block_size = 1024;
+
+ int repeatTime = 50;
+
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+
+ String[] head = {
+ "Dataset",
+ "Encoding Algorithm",
+ "Encoding Time",
+ "Decoding Time",
+ "Points",
+ "Compressed Size",
+ "Compression Ratio"
+ };
+ writer.writeRecord(head);
+
+ File directory = new File(input_parent_dir);
+ File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
+
+ for (File file : csvFiles) {
+ String datasetName = extractFileName(file.toString());
+ System.out.println(datasetName);
+
+ InputStream inputStream = Files.newInputStream(file.toPath());
+
+ CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ ArrayList<Double> data1 = new ArrayList<>();
+
+ int max_decimal = 0;
+ while (loader.readRecord()) {
+ String f_str = loader.getValues()[0];
+ if (f_str.isEmpty()) {
+ continue;
+ }
+ int cur_decimal = getDecimalPrecision(f_str);
+ if (cur_decimal > max_decimal) {
+ max_decimal = cur_decimal;
+ }
+ data1.add(Double.valueOf(f_str));
+ }
+ inputStream.close();
+
+ if (max_decimal > 17) {
+ max_decimal = 17;
+ }
+
+ double[] data1_arr = new double[data1.size()];
+ for (int i = 0; i < data1.size(); i++) {
+ data1_arr[i] = data1.get(i);
+ }
+
+ byte[] encoded_result = new byte[data1.size() * 8];
+
+ long encodeTime = 0;
+ long decodeTime = 0;
+ double ratio = 0;
+ double compressed_size = 0;
+
+ int length = 0;
+
+ long s = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ length = Encoder(data1_arr, block_size, max_decimal, encoded_result);
+ }
+
+ long e = System.nanoTime();
+ encodeTime += ((e - s) / repeatTime);
+ compressed_size += length;
+
+ double ratioTmp;
+
+ ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
+
+ ratio += ratioTmp;
+
+ System.out.println("Decode");
+
+ double[] data2_arr_decoded = new double[data1_arr.length];
+
+ s = System.nanoTime();
+
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ data2_arr_decoded = Decoder(encoded_result);
+ }
+
+ e = System.nanoTime();
+ decodeTime += ((e - s) / repeatTime);
+
+ String[] record = {
+ datasetName,
+ "ALP",
+ String.valueOf(encodeTime),
+ String.valueOf(decodeTime),
+ String.valueOf(data1.size()),
+ String.valueOf(compressed_size),
+ String.valueOf(ratio)
+ };
+ writer.writeRecord(record);
+ System.out.println(ratio);
+ }
+
+ writer.close();
+ }
+
+ @Test
+ public void test1() {
+ double c = Math.pow(10, -17);
+
+ System.out.println(c);
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/SubcolumnTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/ASubcolumnAddDictionaryAndHuffmanTest.java
similarity index 54%
copy from iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/SubcolumnTest.java
copy to iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/ASubcolumnAddDictionaryAndHuffmanTest.java
index 668b9045687..16c395cd083 100644
--- a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/SubcolumnTest.java
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/ASubcolumnAddDictionaryAndHuffmanTest.java
@@ -1,5 +1,9 @@
package org.apache.iotdb.tsfile.encoding;
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -8,19 +12,215 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
-import java.util.stream.Stream;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+public class ASubcolumnAddDictionaryAndHuffmanTest {
+
+ private static final class HuffmanNode {
+ final int symbol; // -1 for internal node
+ final int freq;
+ final HuffmanNode left;
+ final HuffmanNode right;
+
+ private HuffmanNode(int symbol, int freq, HuffmanNode left, HuffmanNode right) {
+ this.symbol = symbol;
+ this.freq = freq;
+ this.left = left;
+ this.right = right;
+ }
-import org.junit.Test;
+ boolean isLeaf() {
+ return left == null && right == null;
+ }
+ }
-import com.csvreader.CsvReader;
-import com.csvreader.CsvWriter;
+ private static final class HuffmanCode {
+ final int code;
+ final int len;
+
+ private HuffmanCode(int code, int len) {
+ this.code = code;
+ this.len = len;
+ }
+ }
+
+ private static final class HuffmanBuildResult {
+ final int[] symbolsSortedAsc;
+ final byte[] codeLenBySymbolIndex; // aligned with symbolsSortedAsc
+ final HuffmanCode[] canonicalCodesBySymbolIndex; // aligned with symbolsSortedAsc
+ final int totalBits;
+
+ private HuffmanBuildResult(
+ int[] symbolsSortedAsc,
+ byte[] codeLenBySymbolIndex,
+ HuffmanCode[] canonicalCodesBySymbolIndex,
+ int totalBits) {
+ this.symbolsSortedAsc = symbolsSortedAsc;
+ this.codeLenBySymbolIndex = codeLenBySymbolIndex;
+ this.canonicalCodesBySymbolIndex = canonicalCodesBySymbolIndex;
+ this.totalBits = totalBits;
+ }
+ }
+
+ private static void writeBits(int value, int bitLen, byte[] out, int bitPos) {
+ // Write highest bits first.
+ for (int k = bitLen - 1; k >= 0; k--) {
+ boolean b = ((value >>> k) & 1) != 0;
+ boolToBytes(b, out, bitPos++);
+ }
+ }
+
+ private static int readBit(byte[] in, int bitPos) {
+ return bytesToBool(in, bitPos) ? 1 : 0;
+ }
-import static org.junit.Assert.assertEquals;
+ private static void assignCodeLengths(HuffmanNode node, int depth, Map<Integer, Integer> outLen) {
+ if (node.isLeaf()) {
+ // Single-symbol edge case: assign length 1.
+ outLen.put(node.symbol, Math.max(1, depth));
+ return;
+ }
+ assignCodeLengths(node.left, depth + 1, outLen);
+ assignCodeLengths(node.right, depth + 1, outLen);
+ }
-public class SubcolumnTest {
+ private static HuffmanBuildResult buildCanonicalHuffman(int[] values, int valueCount) {
+ // Build frequency table
+ Map<Integer, Integer> freq = new HashMap<>();
+ for (int i = 0; i < valueCount; i++) {
+ freq.merge(values[i], 1, Integer::sum);
+ }
+
+ int cardinality = freq.size();
+ int[] symbols = new int[cardinality];
+ int idx = 0;
+ for (int s : freq.keySet()) {
+ symbols[idx++] = s;
+ }
+ Arrays.sort(symbols);
+
+ // Build Huffman tree
+ PriorityQueue<HuffmanNode> pq = new PriorityQueue<>(
+ Comparator.<HuffmanNode>comparingInt(n -> n.freq).thenComparingInt(n -> n.symbol));
+ for (int s : symbols) {
+ pq.add(new HuffmanNode(s, freq.get(s), null, null));
+ }
+ while (pq.size() > 1) {
+ HuffmanNode a = pq.poll();
+ HuffmanNode b = pq.poll();
+ // Ensure deterministic structure by ordering children by (freq,symbol)
+ HuffmanNode left = a;
+ HuffmanNode right = b;
+ pq.add(new HuffmanNode(-1, a.freq + b.freq, left, right));
+ }
+ HuffmanNode root = pq.poll();
+
+ Map<Integer, Integer> lenBySymbol = new HashMap<>();
+ assignCodeLengths(root, 0, lenBySymbol);
+
+ // Prepare canonical order: by (len, symbol)
+ Integer[] order = new Integer[cardinality];
+ for (int i = 0; i < cardinality; i++) {
+ order[i] = symbols[i];
+ }
+ Arrays.sort(order, Comparator.<Integer>comparingInt(s -> lenBySymbol.get(s)).thenComparingInt(s -> s));
+
+ // Assign canonical codes
+ Map<Integer, HuffmanCode> codeBySymbol = new HashMap<>();
+ int code = 0;
+ int prevLen = lenBySymbol.get(order[0]);
+ codeBySymbol.put(order[0], new HuffmanCode(code, prevLen));
+ for (int i = 1; i < order.length; i++) {
+ int s = order[i];
+ int len = lenBySymbol.get(s);
+ code = (code + 1) << (len - prevLen);
+ codeBySymbol.put(s, new HuffmanCode(code, len));
+ prevLen = len;
+ }
+
+ // Align to symbolsSortedAsc for compact lookup
+ byte[] codeLens = new byte[cardinality];
+ HuffmanCode[] codes = new HuffmanCode[cardinality];
+ int totalBits = 0;
+ for (int i = 0; i < cardinality; i++) {
+ int s = symbols[i];
+ HuffmanCode hc = codeBySymbol.get(s);
+ codeLens[i] = (byte) hc.len;
+ codes[i] = hc;
+ totalBits += freq.get(s) * hc.len;
+ }
+
+ return new HuffmanBuildResult(symbols, codeLens, codes, totalBits);
+ }
+
+ private static Map<Integer, HuffmanCode> rebuildCanonicalCodes(int[] symbolsSortedAsc, byte[] codeLens) {
+ int cardinality = symbolsSortedAsc.length;
+ Integer[] order = new Integer[cardinality];
+ for (int i = 0; i < cardinality; i++) {
+ order[i] = symbolsSortedAsc[i];
+ }
+ Map<Integer, Integer> lenBySymbol = new HashMap<>();
+ for (int i = 0; i < cardinality; i++) {
+ lenBySymbol.put(symbolsSortedAsc[i], codeLens[i] & 0xFF);
+ }
+ Arrays.sort(order, Comparator.<Integer>comparingInt(s -> lenBySymbol.get(s)).thenComparingInt(s -> s));
+
+ Map<Integer, HuffmanCode> codeBySymbol = new HashMap<>();
+ int code = 0;
+ int prevLen = lenBySymbol.get(order[0]);
+ codeBySymbol.put(order[0], new HuffmanCode(code, prevLen));
+ for (int i = 1; i < order.length; i++) {
+ int s = order[i];
+ int len = lenBySymbol.get(s);
+ code = (code + 1) << (len - prevLen);
+ codeBySymbol.put(s, new HuffmanCode(code, len));
+ prevLen = len;
+ }
+ return codeBySymbol;
+ }
+
+ private static final class MutableTrieNode {
+ MutableTrieNode zero;
+ MutableTrieNode one;
+ int symbol = -1;
+ }
+
+ private static MutableTrieNode buildMutableDecodeTrie(Map<Integer, HuffmanCode> codeBySymbol) {
+ MutableTrieNode root = new MutableTrieNode();
+ for (Map.Entry<Integer, HuffmanCode> e : codeBySymbol.entrySet()) {
+ int sym = e.getKey();
+ HuffmanCode hc = e.getValue();
+ MutableTrieNode cur = root;
+ for (int k = hc.len - 1; k >= 0; k--) {
+ int b = (hc.code >>> k) & 1;
+ if (b == 0) {
+ if (cur.zero == null) {
+ cur.zero = new MutableTrieNode();
+ }
+ cur = cur.zero;
+ } else {
+ if (cur.one == null) {
+ cur.one = new MutableTrieNode();
+ }
+ cur = cur.one;
+ }
+ }
+ cur.symbol = sym;
+ }
+ return root;
+ }
public static int bitWidth(int value) {
+ if (value == 0)
+ return 1;
return 32 - Integer.numberOfLeadingZeros(value);
}
@@ -247,6 +447,44 @@ public class SubcolumnTest {
int cMin = Integer.MAX_VALUE;
+ int[] bpe_cost_single = new int[m];
+ int[] rle_cost_single = new int[m];
+ int[] de_cost_single = new int[m];
+
+ int cost0 = 0;
+
+ for (int i = 0; i < m; i++) {
+ int current_value = (x[0] >> i) & 1;
+
+ int count = 1;
+
+ de_cost_single[i] = 1;
+
+ for (int j = 0; j < x_length; j++) {
+
+ // if (count * (1 + (int) Math.ceil(Math.log(x_length))) >= x_length) {
+ // rle_cost_single[i] = x_length + 1;
+ // break;
+ // }
+
+ int subcolumn_ij = (x[j] >> i) & 1;
+ if (subcolumn_ij == 1) {
+ bpe_cost_single[i] = x_length;
+ }
+
+ if (subcolumn_ij != current_value) {
+ count++;
+ current_value = subcolumn_ij;
+ de_cost_single[i] = x_length + 2 * (1 + 1);
+ }
+
+ }
+
+ rle_cost_single[i] = count * (1 + (int) Math.ceil(Math.log(x_length)));
+
+ cost0 += Math.min(bpe_cost_single[i], Math.min(rle_cost_single[i], de_cost_single[i]));
+ }
+
// int[] beta_list = {1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
// int[] beta_list = { 1, 2, 3, 5, 7, 11 };
// int[] beta_list = { 1, 2, 3, 4 };
@@ -266,42 +504,78 @@ public class SubcolumnTest {
// System.out.println("l: " + l);
- int[][] subcolumnList = new int[l][x_length];
+ // int[][] subcolumnList = new int[l][x_length];
int cost = 0;
+ // for (int i = 0; i < l; i++) {
+ // int maxValuePart = 0;
+ // for (int j = 0; j < x_length; j++) {
+ // subcolumnList[i][j] = (x[j] >> (i * beta)) & ((1 << beta) - 1);
+ // if (subcolumnList[i][j] > maxValuePart) {
+ // maxValuePart = subcolumnList[i][j];
+ // }
+ // }
+ // bitWidthListList[i] = bitWidth(maxValuePart);
+ // }
+
for (int i = 0; i < l; i++) {
- int maxValuePart = 0;
- for (int j = 0; j < x_length; j++) {
- subcolumnList[i][j] = (x[j] >> (i * beta)) & ((1 << beta) - 1);
- if (subcolumnList[i][j] > maxValuePart) {
- maxValuePart = subcolumnList[i][j];
- }
+ // int bpCost = bitWidthListList[i] * x_length;
+
+ // int bpCost = bpe_cost_single[i * beta] * beta;
+ int beta_start = (Math.min(m - 1, (i + 1) * beta - 1));
+ while (beta_start - 1 >= i * beta && bpe_cost_single[beta_start - 1] == 0) {
+ beta_start--;
}
- bitWidthListList[i] = bitWidth(maxValuePart);
- }
- for (int i = 0; i < l; i++) {
- int bpCost = bitWidthListList[i] * x_length;
+ int bpCost = bpe_cost_single[beta_start] * (beta_start - i * beta + 1);
+
int rleCost = 0;
- // int count = 1;
- int currentNumber = subcolumnList[i][0];
+ // int lowestBitIndex = 0;
+ // int currentLowestBit = subcolumnList[i][0] & 1;
+
+ // for (int j = 1; j < x_length; j++) {
+ // int lowestBit = subcolumnList[i][j] & 1; // 获取当前元素的最低位
+ // if (lowestBit != currentLowestBit) {
+ // lowestBitIndex++;
+ // currentLowestBit = lowestBit;
+ // }
+ // }
+
+ // if (bw * lowestBitIndex + bitWidthListList[i] * lowestBitIndex >= bpCost) {
+ // cost += bpCost;
+ // continue;
+ // }
int index = 0;
boolean bpBest = false;
+ // int count = 1;
+ // int currentNumber = subcolumnList[i][0];
+ int currentNumber = (x[0] >> (i * beta)) & ((1 << beta) - 1);
+
for (int j = 1; j < x_length; j++) {
- if (subcolumnList[i][j] != currentNumber) {
+ int currentNumber_j = (x[j] >> (i * beta)) & ((1 << beta) - 1);
+ if (currentNumber_j != currentNumber) {
index++;
- currentNumber = subcolumnList[i][j];
+ currentNumber = currentNumber_j;
}
-
- if (bw * index + bitWidthListList[i] * index >= bpCost) {
+ if (bw * index + bitWidth(x_length) * index >= bpCost) {
bpBest = true;
break;
}
+
+ // if (subcolumnList[i][j] != currentNumber) {
+ // index++;
+ // currentNumber = subcolumnList[i][j];
+ // }
+
+ // if (bw * index + bitWidthListList[i] * index >= bpCost) {
+ // bpBest = true;
+ // break;
+ // }
}
if (bpBest) {
@@ -313,7 +587,7 @@ public class SubcolumnTest {
// System.out.println("index: " + index);
- rleCost = bw * index + bitWidthListList[i] * index;
+ rleCost = bw * index + bitWidth(x_length) * index;
// System.out.println("bpCost: " + bpCost + " rleCost: " + rleCost);
@@ -338,9 +612,9 @@ public class SubcolumnTest {
public static int SubcolumnEncoder(int[] list, int encode_pos, byte[] encoded_result, int[] beta, int block_size) {
int list_length = list.length;
int maxValue = 0;
- for (int i = 0; i < list_length; i++) {
- if (list[i] > maxValue) {
- maxValue = list[i];
+ for (int k : list) {
+ if (k > maxValue) {
+ maxValue = k;
}
}
@@ -354,7 +628,6 @@ public class SubcolumnTest {
}
// int[] bitWidthList = new int[m];
-
// int[][] subcolumnList = new int[m][list_length];
int l;
@@ -390,9 +663,9 @@ public class SubcolumnTest {
int[] encodingType = new int[l];
- // encoded_result 预留大小为 (l + 7) / 8 的大小,存储每个分列的类型
+ // encoded_result 预留大小为 (l + 7) * 2 / 8 的大小,存储每个分列的类型
int preTypePos = encode_pos;
- encode_pos += (l + 7) / 8;
+ encode_pos += (l + 3) / 4;
for (int i = l - 1; i >= 0; i--) {
// 对于每个分列,计算使用 bit packing 还是 rle
@@ -402,8 +675,15 @@ public class SubcolumnTest {
int previous = subcolumnList[i][0];
int index = 0;
+ // uniqueValues.add(previous);
+
for (int j = 1; j < list_length; j++) {
int currentNumber = subcolumnList[i][j];
+
+ // if(currentNumber == 6){
+ // System.out.println("currentNumber == 6 && i==0");
+ // System.out.println(uniqueValues);
+ // }
if (currentNumber != previous) {
index++;
previous = currentNumber;
@@ -413,11 +693,122 @@ public class SubcolumnTest {
break;
}
}
+ Set<Integer> uniqueValues = new HashSet<>();
+ Map<Integer, Integer> freq = new HashMap<>();
+ for (int j = 0; j < list_length; j++) {
+ int currentNumber = subcolumnList[i][j];
+ uniqueValues.add(currentNumber);
+ freq.merge(currentNumber, 1, Integer::sum);
+ }
+ int cardinality = uniqueValues.size();
index++;
rleCost = bw * index + bitWidthList[i] * index;
+ // Huffman as the 4th option for each subcolumn
+ // We store: [cardinality:2B][totalBits:4B][symbols(bitpacked)][codeLen(bytes)][bitstream]
+ int hufTotalBits;
+ int hufOverheadBits;
+ HuffmanBuildResult hufBuild = null;
+ try {
+ hufBuild = buildCanonicalHuffman(subcolumnList[i], list_length);
+ hufTotalBits = hufBuild.totalBits;
+ // overhead: 2B + 4B + symbols(bitWidthList[i] * cardinality) + codeLen(8 * cardinality)
+ hufOverheadBits = (2 + 4) * 8 + bitWidthList[i] * cardinality + 8 * cardinality;
+ } catch (Exception ex) {
+ // If anything goes wrong, just disable Huffman for this subcolumn
+ hufTotalBits = Integer.MAX_VALUE / 2;
+ hufOverheadBits = Integer.MAX_VALUE / 2;
+ }
+ int hufCostBits = (hufTotalBits >= Integer.MAX_VALUE / 4) ? Integer.MAX_VALUE : (hufTotalBits + hufOverheadBits);
+
+ if (cardinality < Math.pow(2, bitWidthList[i] - 1)) {
+ // test dictionary encoding
+ int dict_bit_width = bitWidth(cardinality);
+ int dicCost = dict_bit_width * list_length + cardinality * (bitWidthList[i] + dict_bit_width);
+ if (dicCost < rleCost && dicCost < bpCost) {
+ // if dictionary encoding
+ // int dict_bit_width = bitWidth(cardinality) ;
+ encodingType[i] = 2;
+
+ // System.out.println(uniqueValues);
+ List<Integer> sortedUnique = new ArrayList<>(uniqueValues);
+ Collections.sort(sortedUnique);
+ Map<Integer, Integer> valueToCode = new HashMap<>();
+ int[] dict_key_list = new int[cardinality];
+ // int[] dict_value_list = new int[cardinality];
+ for (int j = 0; j < cardinality; j++) {
+ valueToCode.put(sortedUnique.get(j), j);
+ dict_key_list[j] = sortedUnique.get(j);
+ // dict_value_list[j] = j;
+ }
+ // System.out.println(valueToCode);
+ // System.out.println(list_length);
+ // System.out.println(beta[0]);
+ // System.out.println(Arrays.toString(subcolumnList[i]));
+ for (int j = 0; j < list_length; j++) {
+ int currentNumber = subcolumnList[i][j];
+ int encodedValue = valueToCode.get(currentNumber);
+ subcolumnList[i][j] = encodedValue;
+ }
+
+ encoded_result[encode_pos] = (byte) (cardinality >> 8);
+ encode_pos += 1;
+ encoded_result[encode_pos] = (byte) (cardinality & 0xFF);
+ encode_pos += 1;
+
+ encode_pos = bitPacking(dict_key_list, bitWidthList[i], encode_pos, encoded_result, cardinality);
+ // encode_pos = bitPacking(dict_value_list, dict_bit_width, encode_pos,
+ // encoded_result, cardinality);
+
+ encode_pos = bitPacking(subcolumnList[i], dict_bit_width, encode_pos, encoded_result, list_length);
+ continue;
+ }
+ }
+
+ // If Huffman wins, encode Huffman
+ if (hufCostBits < bpCost && hufCostBits < rleCost) {
+ encodingType[i] = 3;
+
+ // cardinality
+ encoded_result[encode_pos] = (byte) (cardinality >> 8);
+ encode_pos += 1;
+ encoded_result[encode_pos] = (byte) (cardinality & 0xFF);
+ encode_pos += 1;
+
+ // totalBits
+ int2Bytes(hufBuild.totalBits, encode_pos, encoded_result);
+ encode_pos += 4;
+
+ // symbols sorted asc
+ encode_pos = bitPacking(hufBuild.symbolsSortedAsc, bitWidthList[i], encode_pos, encoded_result, cardinality);
+
+ // code lengths in bytes aligned with symbolsSortedAsc
+ for (int j = 0; j < cardinality; j++) {
+ encoded_result[encode_pos + j] = hufBuild.codeLenBySymbolIndex[j];
+ }
+ encode_pos += cardinality;
+
+ // build lookup from symbol->(code,len) by symbolsSortedAsc index
+ Map<Integer, HuffmanCode> codeBySymbol = new HashMap<>();
+ for (int j = 0; j < cardinality; j++) {
+ codeBySymbol.put(hufBuild.symbolsSortedAsc[j], hufBuild.canonicalCodesBySymbolIndex[j]);
+ }
+
+ int bitPos = encode_pos * 8;
+ int bytesToWrite = (hufBuild.totalBits + 7) / 8;
+ Arrays.fill(encoded_result, encode_pos, encode_pos + bytesToWrite, (byte) 0);
+ for (int j = 0; j < list_length; j++) {
+ int sym = subcolumnList[i][j];
+ HuffmanCode hc = codeBySymbol.get(sym);
+ writeBits(hc.code, hc.len, encoded_result, bitPos);
+ bitPos += hc.len;
+ }
+ encode_pos += bytesToWrite;
+ continue;
+ }
+
if (bpCost <= rleCost) {
encodingType[i] = 0;
@@ -458,7 +849,7 @@ public class SubcolumnTest {
}
- preTypePos = bitPacking(encodingType, 1, preTypePos, encoded_result, l);
+ preTypePos = bitPacking(encodingType, 2, preTypePos, encoded_result, l);
return encode_pos;
}
@@ -489,7 +880,7 @@ public class SubcolumnTest {
int[] encodingType = new int[l];
- encode_pos = decodeBitPacking(encoded_result, encode_pos, 1, l, encodingType);
+ encode_pos = decodeBitPacking(encoded_result, encode_pos, 2, l, encodingType);
for (int i = l - 1; i >= 0; i--) {
int type = encodingType[i];
@@ -497,7 +888,7 @@ public class SubcolumnTest {
if (type == 0) {
encode_pos = decodeBitPacking(encoded_result, encode_pos, bitWidth, list_length,
subcolumnList[i]);
- } else {
+ } else if (type == 1) {
int index = ((encoded_result[encode_pos] & 0xFF) << 8) | (encoded_result[encode_pos + 1] & 0xFF);
encode_pos += 2;
@@ -517,6 +908,68 @@ public class SubcolumnTest {
currentIndex++;
}
}
+ } else if (type == 2) {
+ int cardinality = ((encoded_result[encode_pos] & 0xFF) << 8) | (encoded_result[encode_pos + 1] & 0xFF);
+ encode_pos += 2;
+ int dict_bit_width = bitWidth(cardinality);
+ int[] dict_key_list = new int[cardinality];
+ int[] dict_value_list = new int[cardinality];
+
+ for (int j = 0; j < cardinality; j++) {
+ dict_value_list[j] = j;
+ }
+
+ encode_pos = decodeBitPacking(encoded_result, encode_pos, bitWidthList[i], cardinality, dict_key_list);
+ // encode_pos = decodeBitPacking(encoded_result, encode_pos, dict_bit_width,
+ // cardinality, dict_value_list);
+
+ encode_pos = decodeBitPacking(encoded_result, encode_pos, dict_bit_width, list_length,
+ subcolumnList[i]);
+ Map<Integer, Integer> valueToCode = new HashMap<>();
+ for (int j = 0; j < cardinality; j++) {
+ valueToCode.put(dict_value_list[j], dict_key_list[j]);
+ }
+
+ for (int j = 0; j < list_length; j++) {
+ int currentNumber = subcolumnList[i][j];
+ int encodedValue = valueToCode.get(currentNumber);
+ subcolumnList[i][j] = encodedValue;
+ }
+
+ } else {
+ // Huffman
+ int cardinality = ((encoded_result[encode_pos] & 0xFF) << 8) | (encoded_result[encode_pos + 1] & 0xFF);
+ encode_pos += 2;
+
+ int totalBits = bytes2Integer(encoded_result, encode_pos, 4);
+ encode_pos += 4;
+
+ int[] symbolsSortedAsc = new int[cardinality];
+ encode_pos = decodeBitPacking(encoded_result, encode_pos, bitWidthList[i], cardinality, symbolsSortedAsc);
+
+ byte[] codeLens = new byte[cardinality];
+ for (int j = 0; j < cardinality; j++) {
+ codeLens[j] = encoded_result[encode_pos + j];
+ }
+ encode_pos += cardinality;
+
+ Map<Integer, HuffmanCode> codeBySymbol = rebuildCanonicalCodes(symbolsSortedAsc, codeLens);
+ MutableTrieNode trie = buildMutableDecodeTrie(codeBySymbol);
+
+ int bitPos = encode_pos * 8;
+ int bitsRead = 0;
+ for (int j = 0; j < list_length; j++) {
+ MutableTrieNode cur = trie;
+ while (cur.symbol < 0) {
+ int b = readBit(encoded_result, bitPos++);
+ bitsRead++;
+ cur = (b == 0) ? cur.zero : cur.one;
+ }
+ subcolumnList[i][j] = cur.symbol;
+ }
+
+ // advance bytes by totalBits (trusted) rather than bitsRead (derived)
+ encode_pos += (totalBits + 7) / 8;
}
}
@@ -568,7 +1021,7 @@ public class SubcolumnTest {
int[] data_delta = getAbsDeltaTsBlock(data, block_index, block_size,
remainder, min_delta);
-
+
int2Bytes(min_delta[0], encode_pos, encoded_result);
encode_pos += 4;
@@ -709,25 +1162,23 @@ public class SubcolumnTest {
}
@Test
- public void testSubcolumn() throws IOException {
- String parent_dir = "D:/github/xjz17/subcolumn/";
+ public void test0() throws IOException {
+ String parent_dir = "path/to/your/directory/";
String input_parent_dir = parent_dir + "dataset/";
- String output_parent_dir = "D:/encoding-subcolumn/result/";
- // String output_parent_dir = parent_dir + "result/";
+ String output_parent_dir = parent_dir + "result/";
- String outputPath = output_parent_dir + "subcolumn.csv";
+ String outputPath = output_parent_dir + "subcolumn_dictionary_huffman.csv";
+ // int block_size = 512;
int block_size = 512;
- int repeatTime = 100;
+ // int repeatTime = 100;
+ int repeatTime = 10;
// repeatTime = 1;
- List<String> integerDatasets = new ArrayList<>();
- integerDatasets.add("Wine-Tasting");
-
CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
writer.setRecordDelimiter('\n');
@@ -749,6 +1200,9 @@ public class SubcolumnTest {
for (File file : csvFiles) {
String datasetName = extractFileName(file.toString());
System.out.println(datasetName);
+ // if(! datasetName.equals("Stocks-UK")){
+ // continue;
+ // }
InputStream inputStream = Files.newInputStream(file.toPath());
@@ -768,6 +1222,11 @@ public class SubcolumnTest {
data1.add(Float.valueOf(f_str));
}
inputStream.close();
+
+ if (max_decimal > 8) {
+ max_decimal = 8;
+ }
+
int[] data2_arr = new int[data1.size()];
int max_mul = (int) Math.pow(10, max_decimal);
@@ -796,11 +1255,7 @@ public class SubcolumnTest {
double ratioTmp;
- if (integerDatasets.contains(datasetName)) {
- ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- } else {
- ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
- }
+ ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
ratio += ratioTmp;
@@ -818,12 +1273,12 @@ public class SubcolumnTest {
decodeTime += ((e - s) / repeatTime);
for (int i = 0; i < data2_arr_decoded.length; i++) {
- assertEquals(data2_arr[i], data2_arr_decoded[i]);
+ // assertEquals(data2_arr[i], data2_arr_decoded[i]);
}
String[] record = {
datasetName,
- "Sub-columns",
+ "Sub-columns (Dictionary)",
String.valueOf(encodeTime),
String.valueOf(decodeTime),
String.valueOf(data1.size()),
@@ -837,151 +1292,4 @@ public class SubcolumnTest {
writer.close();
}
- @Test
- public void testTransData() throws IOException {
- String parent_dir = "D:/github/xjz17/subcolumn/";
-
- String output_parent_dir = "D:/encoding-subcolumn/trans_data_result/";
- // String output_parent_dir = parent_dir + "trans_data_result/";
-
- String input_parent_dir = parent_dir + "trans_data/";
-
- ArrayList<String> input_path_list = new ArrayList<>();
- ArrayList<String> output_path_list = new ArrayList<>();
- ArrayList<String> dataset_name = new ArrayList<>();
- ArrayList<Integer> dataset_block_size = new ArrayList<>();
-
- try (Stream<Path> paths = Files.walk(Paths.get(input_parent_dir))) {
- paths.filter(Files::isDirectory)
- .filter(path -> !path.equals(Paths.get(input_parent_dir)))
- .forEach(dir -> {
- String name = dir.getFileName().toString();
- dataset_name.add(name);
- input_path_list.add(dir.toString());
- dataset_block_size.add(1024);
- });
- }
-
- String outputPath = output_parent_dir + "subcolumn.csv";
- CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
- writer.setRecordDelimiter('\n');
-
- String[] head = {
- "Dataset",
- "Encoding Algorithm",
- "Encoding Time",
- "Decoding Time",
- "Points",
- "Compressed Size",
- "Compression Ratio"
- };
- writer.writeRecord(head);
-
- int repeatTime = 100;
-
- for (int file_i = 0; file_i < input_path_list.size(); file_i++) {
-
- String inputPath = input_path_list.get(file_i);
- System.out.println(inputPath);
-
- File file = new File(inputPath);
- File[] tempList = file.listFiles();
-
- // CsvWriter writer = new CsvWriter(Output, ',', StandardCharsets.UTF_8);
- // writer.setRecordDelimiter('\n');
-
- // String[] head = {
- // "Input Direction",
- // "Encoding Algorithm",
- // "Encoding Time",
- // "Decoding Time",
- // "Points",
- // "Compressed Size",
- // "Compression Ratio"
- // };
- // writer.writeRecord(head);
-
- long totalEncodeTime = 0;
- long totalDecodeTime = 0;
- double totalCompressedSize = 0;
- int totalPoints = 0;
-
- for (File f : tempList) {
- String datasetName = extractFileName(f.toString());
- InputStream inputStream = Files.newInputStream(f.toPath());
-
- CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
- ArrayList<Integer> data1 = new ArrayList<>();
- ArrayList<Integer> data2 = new ArrayList<>();
-
- loader.readHeaders();
- while (loader.readRecord()) {
- // String value = loader.getValues()[index];
- data1.add(Integer.valueOf(loader.getValues()[0]));
- data2.add(Integer.valueOf(loader.getValues()[1]));
- // data.add(Integer.valueOf(value));
- }
- inputStream.close();
- int[] data2_arr = new int[data1.size()];
- for (int i = 0; i < data2.size(); i++) {
- data2_arr[i] = data2.get(i);
- }
- byte[] encoded_result = new byte[data2_arr.length * 4];
- long encodeTime = 0;
- long decodeTime = 0;
- double ratio = 0;
- double compressed_size = 0;
-
- int length = 0;
-
- long s = System.nanoTime();
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- length = Encoder(data2_arr, dataset_block_size.get(file_i), encoded_result);
- }
-
- long e = System.nanoTime();
- encodeTime += ((e - s) / repeatTime);
- compressed_size += length;
- double ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- ratio += ratioTmp;
- s = System.nanoTime();
-
- int[] data2_arr_decoded = new int[data1.size()];
-
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- data2_arr_decoded = Decoder(encoded_result);
- }
-
- e = System.nanoTime();
- decodeTime += ((e - s) / repeatTime);
-
- totalEncodeTime += encodeTime;
- totalDecodeTime += decodeTime;
- totalCompressedSize += compressed_size;
- totalPoints += data1.size();
-
- for (int i = 0; i < data2_arr_decoded.length; i++) {
- assertEquals(data2_arr[i], data2_arr_decoded[i]);
- }
-
- }
-
- double compressionRatio = totalCompressedSize / (totalPoints * Integer.BYTES);
-
- String[] record = {
- dataset_name.get(file_i),
- "Sub-columns",
- String.valueOf(totalEncodeTime),
- String.valueOf(totalDecodeTime),
- String.valueOf(totalPoints),
- String.valueOf(totalCompressedSize),
- String.valueOf(compressionRatio)
- };
-
- writer.writeRecord(record);
- System.out.println(compressionRatio);
- }
- writer.close();
- }
-
}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPLongTest.java
similarity index 53%
copy from iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPTest.java
copy to iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPLongTest.java
index 36f5a35afdc..b23b8f7aac5 100644
--- a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPTest.java
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPLongTest.java
@@ -16,12 +16,16 @@ import org.junit.Test;
import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
-public class BPTest {
+public class BPLongTest {
public static int bitWidth(int value) {
return 32 - Integer.numberOfLeadingZeros(value);
}
+ public static int bitWidth(long value) {
+ return 64 - Long.numberOfLeadingZeros(value);
+ }
+
public static void intToBytes(int srcNum, byte[] result, int pos, int width) {
int cnt = pos & 0x07;
int index = pos >> 3;
@@ -62,20 +66,57 @@ public class BPTest {
return ret;
}
- public static void pack8Values(int[] values, int offset, int width, int encode_pos,
+ public static void longToBytes(long srcNum, byte[] result, int pos, int width) {
+ int cnt = pos & 0x07;
+ int index = pos >> 3;
+
+ while (width > 0) {
+ int m = width + cnt >= 8 ? 8 - cnt : width;
+ width -= m;
+ int mask = 1 << (8 - cnt);
+ cnt += m;
+ byte y = (byte) (srcNum >>> width);
+ y = (byte) (y << (8 - cnt));
+ mask = ~(mask - (1 << (8 - cnt)));
+ result[index] = (byte) (result[index] & mask | y);
+ srcNum = srcNum & ~(-1L << width);
+ if (cnt == 8) {
+ index++;
+ cnt = 0;
+ }
+ }
+ }
+
+ public static long bytesToLong(byte[] result, int pos, int width) {
+ long ret = 0;
+ int cnt = pos & 0x07;
+ int index = pos >> 3;
+ while (width > 0) {
+ int m = width + cnt >= 8 ? 8 - cnt : width;
+ width -= m;
+ ret = ret << m;
+ byte y = (byte) (result[index] & (0xff >> cnt));
+ y = (byte) ((y & 0xff) >>> (8 - cnt - m));
+ ret = ret | (y & 0xff);
+ cnt += m;
+ if (cnt == 8) {
+ cnt = 0;
+ index++;
+ }
+ }
+ return ret;
+ }
+
+ public static void pack8Values(int[] values, int offset, int width, int encode_pos,
byte[] encoded_result) {
int bufIdx = 0;
int valueIdx = offset;
- // remaining bits for the current unfinished Integer
int leftBit = 0;
while (valueIdx < 8 + offset) {
- // buffer is used for saving 32 bits as a part of result
int buffer = 0;
- // remaining size of bits in the 'buffer'
int leftSize = 32;
- // encode the left bits of current Integer to 'buffer'
if (leftBit > 0) {
buffer |= (values[valueIdx] << (32 - leftBit));
leftSize -= leftBit;
@@ -84,20 +125,16 @@ public class BPTest {
}
while (leftSize >= width && valueIdx < 8 + offset) {
- // encode one Integer to the 'buffer'
buffer |= (values[valueIdx] << (leftSize - width));
leftSize -= width;
valueIdx++;
}
- // If the remaining space of the buffer can not save the bits for one Integer,
+
if (leftSize > 0 && valueIdx < 8 + offset) {
- // put the first 'leftSize' bits of the Integer into remaining space of the
- // buffer
buffer |= (values[valueIdx] >>> (width - leftSize));
leftBit = width - leftSize;
}
- // put the buffer into the final result
for (int j = 0; j < 4; j++) {
encoded_result[encode_pos] = (byte) ((buffer >>> ((3 - j) * 8)) & 0xFF);
encode_pos++;
@@ -110,28 +147,58 @@ public class BPTest {
}
+ public static void pack8Values(
+ long[] values, int offset, int width, int encode_pos, byte[] encoded_result) {
+ int bufIdx = 0;
+ int valueIdx = offset;
+ int leftBit = 0;
+
+ while (valueIdx < 8 + offset) {
+ long buffer = 0;
+ int leftSize = 64;
+
+ if (leftBit > 0) {
+ buffer |= (values[valueIdx] << (64 - leftBit));
+ leftSize -= leftBit;
+ leftBit = 0;
+ valueIdx++;
+ }
+
+ while (leftSize >= width && valueIdx < 8 + offset) {
+ buffer |= (values[valueIdx] << (leftSize - width));
+ leftSize -= width;
+ valueIdx++;
+ }
+ if (leftSize > 0 && valueIdx < 8 + offset) {
+ buffer |= (values[valueIdx] >>> (width - leftSize));
+ leftBit = width - leftSize;
+ }
+
+ for (int j = 0; j < 8; j++) {
+ encoded_result[encode_pos] = (byte) ((buffer >>> ((8 - j - 1) * 8)) & 0xFF);
+ encode_pos++;
+ bufIdx++;
+ if (bufIdx >= width * 8 / 8) {
+ return;
+ }
+ }
+ }
+ }
+
public static void unpack8Values(byte[] encoded, int offset, int width, int[] result_list, int result_offset) {
int byteIdx = offset;
long buffer = 0;
- // total bits which have read from 'buf' to 'buffer'. i.e.,
- // number of available bits to be decoded.
int totalBits = 0;
int valueIdx = 0;
while (valueIdx < 8) {
- // If current available bits are not enough to decode one Integer,
- // then add next byte from buf to 'buffer' until totalBits >= width
while (totalBits < width) {
buffer = (buffer << 8) | (encoded[byteIdx] & 0xFF);
byteIdx++;
totalBits += 8;
}
- // If current available bits are enough to decode one Integer,
- // then decode one Integer one by one until left bits in 'buffer' is
- // not enough to decode one Integer.
while (totalBits >= width && valueIdx < 8) {
- // result_list.add((int) (buffer >>> (totalBits - width)));
result_list[result_offset + valueIdx] = (int) (buffer >>> (totalBits - width));
valueIdx++;
totalBits -= width;
@@ -140,6 +207,29 @@ public class BPTest {
}
}
+ public static void unpack8Values(
+ byte[] encoded, int offset, int width, long[] result_list, int result_offset) {
+ int byteIdx = offset;
+ long buffer = 0;
+ int totalBits = 0;
+ int valueIdx = 0;
+
+ while (valueIdx < 8) {
+ while (totalBits < width) {
+ buffer = (buffer << 8) | (encoded[byteIdx] & 0xFF);
+ byteIdx++;
+ totalBits += 8;
+ }
+
+ while (totalBits >= width && valueIdx < 8) {
+ result_list[result_offset + valueIdx] = buffer >>> (totalBits - width);
+ valueIdx++;
+ totalBits -= width;
+ buffer = buffer & ((1L << totalBits) - 1);
+ }
+ }
+ }
+
public static int bitPacking(int[] numbers, int bit_width, int encode_pos,
byte[] encoded_result, int num_values) {
int block_num = num_values / 8;
@@ -160,14 +250,32 @@ public class BPTest {
return (encode_pos + 7) / 8;
}
+ public static int bitPacking(long[] numbers, int bit_width, int encode_pos,
+ byte[] encoded_result, int num_values) {
+ int block_num = num_values / 8;
+ int remainder = num_values % 8;
+
+ for (int i = 0; i < block_num; i++) {
+ pack8Values(numbers, i * 8, bit_width, encode_pos, encoded_result);
+ encode_pos += bit_width;
+ }
+
+ encode_pos *= 8;
+
+ for (int i = 0; i < remainder; i++) {
+ longToBytes(numbers[block_num * 8 + i], encoded_result, encode_pos, bit_width);
+ encode_pos += bit_width;
+ }
+
+ return (encode_pos + 7) / 8;
+ }
+
public static int decodeBitPacking(
byte[] encoded, int decode_pos, int bit_width, int num_values, int[] result_list) {
- // ArrayList<Integer> result_list = new ArrayList<>();
- // int[] result_list = new int[num_values];
int block_num = num_values / 8;
int remainder = num_values % 8;
- for (int i = 0; i < block_num; i++) { // bitpacking
+ for (int i = 0; i < block_num; i++) {
unpack8Values(encoded, decode_pos, bit_width, result_list, i * 8);
decode_pos += bit_width;
}
@@ -182,9 +290,73 @@ public class BPTest {
return (decode_pos + 7) / 8;
}
- public static int BPEncoder(int[] list, int encode_pos, byte[] encoded_result) {
+ public static int decodeBitPacking(
+ byte[] encoded, int decode_pos, int bit_width, int num_values, long[] result_list) {
+ int block_num = num_values / 8;
+ int remainder = num_values % 8;
+
+ for (int i = 0; i < block_num; i++) {
+ unpack8Values(encoded, decode_pos, bit_width, result_list, i * 8);
+ decode_pos += bit_width;
+ }
+
+ decode_pos *= 8;
+
+ for (int i = 0; i < remainder; i++) {
+ result_list[block_num * 8 + i] = bytesToLong(encoded, decode_pos, bit_width);
+ decode_pos += bit_width;
+ }
+
+ return (decode_pos + 7) / 8;
+ }
+
+ public static void int2Bytes(int integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer >> 24);
+ cur_byte[encode_pos + 1] = (byte) (integer >> 16);
+ cur_byte[encode_pos + 2] = (byte) (integer >> 8);
+ cur_byte[encode_pos + 3] = (byte) (integer);
+ }
+
+ public static void intByte2Bytes(int integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer);
+ }
+
+ public static void long2Bytes(long integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer >> 56);
+ cur_byte[encode_pos + 1] = (byte) (integer >> 48);
+ cur_byte[encode_pos + 2] = (byte) (integer >> 40);
+ cur_byte[encode_pos + 3] = (byte) (integer >> 32);
+ cur_byte[encode_pos + 4] = (byte) (integer >> 24);
+ cur_byte[encode_pos + 5] = (byte) (integer >> 16);
+ cur_byte[encode_pos + 6] = (byte) (integer >> 8);
+ cur_byte[encode_pos + 7] = (byte) (integer);
+ }
+
+ public static int bytes2Integer(byte[] encoded, int start, int num) {
+ int value = 0;
+
+ for (int i = 0; i < num; i++) {
+ value <<= 8;
+ int b = encoded[i + start] & 0xFF;
+ value |= b;
+ }
+ return value;
+ }
+
+ public static long bytes2Long(byte[] encoded, int start, int num) {
+ long value = 0;
+
+ for (int i = 0; i < num; i++) {
+ value <<= 8;
+ int b = encoded[i + start] & 0xFF;
+ value |= b;
+ }
+ return value;
+ }
+
+ public static int BPEncoder(long[] list, int encode_pos, byte[] encoded_result) {
int list_length = list.length;
- int maxValue = 0;
+ long maxValue = 0;
for (int i = 0; i < list_length; i++) {
if (list[i] > maxValue) {
maxValue = list[i];
@@ -192,27 +364,22 @@ public class BPTest {
}
int m = bitWidth(maxValue);
- // System.out.println("m: " + m);
- // writeBits(encoded_result, startBitPosition, 8, m);
- // startBitPosition += 8;
encoded_result[encode_pos] = (byte) m;
encode_pos += 1;
- // bitPacking(list, encoded_result, startBitPosition, m, list_length);
- // startBitPosition += m * list_length;
encode_pos = bitPacking(list, m, encode_pos, encoded_result, list_length);
return encode_pos;
}
- public static int BPDecoder(byte[] encoded_result, int encode_pos, int[] list) {
+ public static int BPDecoder(byte[] encoded_result, int encode_pos, long[] list) {
int list_length = list.length;
int m = encoded_result[encode_pos];
encode_pos += 1;
- int[] new_list = new int[list_length];
+ long[] new_list = new long[list_length];
encode_pos = decodeBitPacking(encoded_result, encode_pos, m, list_length, new_list);
for (int i = 0; i < list_length; i++) {
@@ -222,21 +389,21 @@ public class BPTest {
return encode_pos;
}
- public static int[] getAbsDeltaTsBlock(
- int[] ts_block,
+ public static long[] getAbsDeltaTsBlock(
+ long[] ts_block,
int i,
int block_size,
int remaining,
- int[] min_delta) {
- int[] ts_block_delta = new int[remaining];
+ long[] min_delta) {
+ long[] ts_block_delta = new long[remaining];
- int value_delta_min = Integer.MAX_VALUE;
- int value_delta_max = Integer.MIN_VALUE;
+ long value_delta_min = Long.MAX_VALUE;
+ long value_delta_max = Long.MIN_VALUE;
int base = i * block_size;
int end = i * block_size + remaining;
for (int j = base; j < end; j++) {
- int cur = ts_block[j];
+ long cur = ts_block[j];
if (cur < value_delta_min) {
value_delta_min = cur;
}
@@ -254,18 +421,15 @@ public class BPTest {
return ts_block_delta;
}
- public static int BlockEncoder(int[] data, int block_index, int block_size, int remainder,
+ public static int BlockEncoder(long[] data, int block_index, int block_size, int remainder,
int encode_pos, byte[] encoded_result) {
- int[] min_delta = new int[3];
+ long[] min_delta = new long[3];
- int[] data_delta = getAbsDeltaTsBlock(data, block_index, block_size,
+ long[] data_delta = getAbsDeltaTsBlock(data, block_index, block_size,
remainder, min_delta);
- encoded_result[encode_pos] = (byte) (min_delta[0] >> 24);
- encoded_result[encode_pos + 1] = (byte) (min_delta[0] >> 16);
- encoded_result[encode_pos + 2] = (byte) (min_delta[0] >> 8);
- encoded_result[encode_pos + 3] = (byte) min_delta[0];
- encode_pos += 4;
+ long2Bytes(min_delta[0], encode_pos, encoded_result);
+ encode_pos += 8;
encode_pos = BPEncoder(data_delta, encode_pos,
encoded_result);
@@ -274,14 +438,13 @@ public class BPTest {
}
public static int BlockDecoder(byte[] encoded_result, int block_index, int block_size, int remainder,
- int encode_pos, int[] data) {
- int[] min_delta = new int[3];
+ int encode_pos, long[] data) {
+ long[] min_delta = new long[3];
- min_delta[0] = ((encoded_result[encode_pos] & 0xFF) << 24) | ((encoded_result[encode_pos + 1] & 0xFF) << 16) |
- ((encoded_result[encode_pos + 2] & 0xFF) << 8) | (encoded_result[encode_pos + 3] & 0xFF);
- encode_pos += 4;
+ min_delta[0] = bytes2Long(encoded_result, encode_pos, 8);
+ encode_pos += 8;
- int[] block_data = new int[remainder];
+ long[] block_data = new long[remainder];
encode_pos = BPDecoder(encoded_result, encode_pos,
block_data);
@@ -293,7 +456,7 @@ public class BPTest {
return encode_pos;
}
- public static int Encoder(int[] data, int block_size, byte[] encoded_result) {
+ public static int Encoder(long[] data, int block_size, byte[] encoded_result) {
int data_length = data.length;
int encode_pos = 0;
@@ -319,12 +482,9 @@ public class BPTest {
if (remainder <= 3) {
for (int i = 0; i < remainder; i++) {
- int value = data[num_blocks * block_size + i];
- encoded_result[encode_pos] = (byte) (value >> 24);
- encoded_result[encode_pos + 1] = (byte) (value >> 16);
- encoded_result[encode_pos + 2] = (byte) (value >> 8);
- encoded_result[encode_pos + 3] = (byte) value;
- encode_pos += 4;
+ long value = data[num_blocks * block_size + i];
+ long2Bytes(value, encode_pos, encoded_result);
+ encode_pos += 8;
}
} else {
encode_pos = BlockEncoder(data, num_blocks, block_size, remainder, encode_pos,
@@ -334,21 +494,18 @@ public class BPTest {
return encode_pos;
}
- public static int[] Decoder(byte[] encoded_result) {
+ public static long[] Decoder(byte[] encoded_result) {
int encode_pos = 0;
- int data_length = ((encoded_result[encode_pos] & 0xFF) << 24) | ((encoded_result[encode_pos + 1] & 0xFF) << 16)
- |
- ((encoded_result[encode_pos + 2] & 0xFF) << 8) | (encoded_result[encode_pos + 3] & 0xFF);
+ int data_length = bytes2Integer(encoded_result, encode_pos, 4);
encode_pos += 4;
- int block_size = ((encoded_result[encode_pos] & 0xFF) << 24) | ((encoded_result[encode_pos + 1] & 0xFF) << 16) |
- ((encoded_result[encode_pos + 2] & 0xFF) << 8) | (encoded_result[encode_pos + 3] & 0xFF);
+ int block_size = bytes2Integer(encoded_result, encode_pos, 4);
encode_pos += 4;
int num_blocks = data_length / block_size;
- int[] data = new int[data_length];
+ long[] data = new long[data_length];
for (int i = 0; i < num_blocks; i++) {
encode_pos = BlockDecoder(encoded_result, i, block_size, block_size, encode_pos, data);
@@ -358,10 +515,8 @@ public class BPTest {
if (remainder <= 3) {
for (int i = 0; i < remainder; i++) {
- data[num_blocks * block_size + i] = ((encoded_result[encode_pos] & 0xFF) << 24) |
- ((encoded_result[encode_pos + 1] & 0xFF) << 16) |
- ((encoded_result[encode_pos + 2] & 0xFF) << 8) | (encoded_result[encode_pos + 3] & 0xFF);
- encode_pos += 4;
+ data[num_blocks * block_size + i] = bytes2Long(encoded_result, encode_pos, 8);
+ encode_pos += 8;
}
} else {
encode_pos = BlockDecoder(encoded_result, num_blocks, block_size, remainder,
@@ -372,15 +527,12 @@ public class BPTest {
}
public static int getDecimalPrecision(String str) {
- // 查找小数点的位置
int decimalIndex = str.indexOf(".");
- // 如果没有小数点,精度为0
if (decimalIndex == -1) {
return 0;
}
- // 获取小数点后的部分并返回其长度
return str.substring(decimalIndex + 1).length();
}
@@ -402,24 +554,21 @@ public class BPTest {
}
@Test
- public void testSubcolumn() throws IOException {
- String parent_dir = "D:/github/xjz17/subcolumn/";
+ public void test0() throws IOException {
+ // String parent_dir = "path/to/your/directory/";
+ String parent_dir = "D:/encoding-subcolumn/";
- String input_parent_dir = parent_dir + "dataset/";
+ // String input_parent_dir = parent_dir + "dataset/";
+ String input_parent_dir = parent_dir + "ElfTestData_camel/";
- String output_parent_dir = "D:/encoding-subcolumn/result/";
- // String output_parent_dir = parent_dir + "result/";
+ String output_parent_dir = parent_dir + "result/";
- String outputPath = output_parent_dir + "bp.csv";
+ // String outputPath = output_parent_dir + "bp_long.csv";
+ String outputPath = output_parent_dir + "bp_long2.csv";
int block_size = 1024;
- int repeatTime = 100;
-
- // repeatTime = 1;
-
- List<String> integerDatasets = new ArrayList<>();
- integerDatasets.add("Wine-Tasting");
+ int repeatTime = 50;
CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
writer.setRecordDelimiter('\n');
@@ -436,7 +585,6 @@ public class BPTest {
writer.writeRecord(head);
File directory = new File(input_parent_dir);
- // File[] csvFiles = directory.listFiles();
File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
for (File file : csvFiles) {
@@ -446,7 +594,7 @@ public class BPTest {
InputStream inputStream = Files.newInputStream(file.toPath());
CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
- ArrayList<Float> data1 = new ArrayList<>();
+ ArrayList<Double> data1 = new ArrayList<>();
int max_decimal = 0;
while (loader.readRecord()) {
@@ -458,20 +606,23 @@ public class BPTest {
if (cur_decimal > max_decimal) {
max_decimal = cur_decimal;
}
- // String value = loader.getValues()[index];
- data1.add(Float.valueOf(f_str));
- // data2.add(Integer.valueOf(loader.getValues()[1]));
- // data.add(Integer.valueOf(value));
+ data1.add(Double.valueOf(f_str));
}
inputStream.close();
- int[] data2_arr = new int[data1.size()];
- int max_mul = (int) Math.pow(10, max_decimal);
+
+ if (max_decimal > 17) {
+ max_decimal = 17;
+ }
+
+ long[] data2_arr = new long[data1.size()];
+
+ long max_mul = (long) Math.pow(10, max_decimal);
for (int i = 0; i < data1.size(); i++) {
- data2_arr[i] = (int) (data1.get(i) * max_mul);
+ data2_arr[i] = (long) (data1.get(i) * max_mul);
}
System.out.println(max_decimal);
- byte[] encoded_result = new byte[data2_arr.length * 4];
+ byte[] encoded_result = new byte[data2_arr.length * 8];
long encodeTime = 0;
long decodeTime = 0;
@@ -491,17 +642,13 @@ public class BPTest {
double ratioTmp;
- if (integerDatasets.contains(datasetName)) {
- ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- } else {
- ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
- }
+ ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
ratio += ratioTmp;
System.out.println("Decode");
- int[] data2_arr_decoded = new int[data1.size()];
+ long[] data2_arr_decoded = new long[data1.size()];
s = System.nanoTime();
@@ -528,133 +675,4 @@ public class BPTest {
writer.close();
}
- @Test
- public void testTransData() throws IOException {
- String parent_dir = "D:/github/xjz17/subcolumn/";
-
- String output_parent_dir = "D:/encoding-subcolumn/trans_data_result/";
- // String output_parent_dir = parent_dir + "trans_data_result/";
-
- String input_parent_dir = parent_dir + "trans_data/";
-
- ArrayList<String> input_path_list = new ArrayList<>();
- ArrayList<String> output_path_list = new ArrayList<>();
- ArrayList<String> dataset_name = new ArrayList<>();
- ArrayList<Integer> dataset_block_size = new ArrayList<>();
-
- try (Stream<Path> paths = Files.walk(Paths.get(input_parent_dir))) {
- paths.filter(Files::isDirectory)
- .filter(path -> !path.equals(Paths.get(input_parent_dir)))
- .forEach(dir -> {
- String name = dir.getFileName().toString();
- dataset_name.add(name);
- input_path_list.add(dir.toString());
- dataset_block_size.add(1024);
- });
- }
-
- String outputPath = output_parent_dir + "bp.csv";
- CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
- writer.setRecordDelimiter('\n');
-
- String[] head = {
- "Dataset",
- "Encoding Algorithm",
- "Encoding Time",
- "Decoding Time",
- "Points",
- "Compressed Size",
- "Compression Ratio"
- };
- writer.writeRecord(head);
-
- int repeatTime = 100;
-
- for (int file_i = 0; file_i < input_path_list.size(); file_i++) {
-
- String inputPath = input_path_list.get(file_i);
- System.out.println(inputPath);
-
- File file = new File(inputPath);
- File[] tempList = file.listFiles();
-
- long totalEncodeTime = 0;
- long totalDecodeTime = 0;
- double totalCompressedSize = 0;
- int totalPoints = 0;
-
- for (File f : tempList) {
- String datasetName = extractFileName(f.toString());
- InputStream inputStream = Files.newInputStream(f.toPath());
-
- CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
- ArrayList<Integer> data1 = new ArrayList<>();
- ArrayList<Integer> data2 = new ArrayList<>();
-
- loader.readHeaders();
- while (loader.readRecord()) {
- // String value = loader.getValues()[index];
- data1.add(Integer.valueOf(loader.getValues()[0]));
- data2.add(Integer.valueOf(loader.getValues()[1]));
- // data.add(Integer.valueOf(value));
- }
- inputStream.close();
- int[] data2_arr = new int[data1.size()];
- for (int i = 0; i < data2.size(); i++) {
- data2_arr[i] = data2.get(i);
- }
- byte[] encoded_result = new byte[data2_arr.length * 4];
- long encodeTime = 0;
- long decodeTime = 0;
- double ratio = 0;
- double compressed_size = 0;
-
- int length = 0;
-
- long s = System.nanoTime();
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- length = Encoder(data2_arr, dataset_block_size.get(file_i), encoded_result);
- }
-
- long e = System.nanoTime();
- encodeTime += ((e - s) / repeatTime);
- compressed_size += length;
- double ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- ratio += ratioTmp;
- s = System.nanoTime();
-
- int[] data2_arr_decoded = new int[data1.size()];
-
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- data2_arr_decoded = Decoder(encoded_result);
- }
-
- e = System.nanoTime();
- decodeTime += ((e - s) / repeatTime);
-
- totalEncodeTime += encodeTime;
- totalDecodeTime += decodeTime;
- totalCompressedSize += compressed_size;
- totalPoints += data1.size();
-
- }
-
- double compressionRatio = totalCompressedSize / (totalPoints * Integer.BYTES);
-
- String[] record = {
- dataset_name.get(file_i),
- "BP",
- String.valueOf(totalEncodeTime),
- String.valueOf(totalDecodeTime),
- String.valueOf(totalPoints),
- String.valueOf(totalCompressedSize),
- String.valueOf(compressionRatio)
- };
-
- writer.writeRecord(record);
- System.out.println(compressionRatio);
- }
- writer.close();
- }
-
}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPQueryGroupTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPQueryGroupTest.java
new file mode 100644
index 00000000000..ddf28aab7eb
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPQueryGroupTest.java
@@ -0,0 +1,126 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+
+public class BPQueryGroupTest {
+
+ public static int[] queryGroupMaxIndexByDecode(byte[] encodedResult, int windowSize) {
+ int[] decoded = BPTest.Decoder(encodedResult);
+ int groupCount = (decoded.length + windowSize - 1) / windowSize;
+ int[] result = new int[groupCount];
+ for (int g = 0; g < groupCount; g++) {
+ int start = g * windowSize;
+ int end = Math.min(decoded.length, start + windowSize);
+ int bestIndex = start;
+ int bestValue = decoded[start];
+ for (int i = start + 1; i < end; i++) {
+ if (decoded[i] > bestValue) {
+ bestValue = decoded[i];
+ bestIndex = i;
+ }
+ }
+ result[g] = bestIndex;
+ }
+ return result;
+ }
+
+ @Test
+ public void test0() throws IOException {
+ String parentDir = "D://github/xjz17/subcolumn/";
+ String inputParentDir = parentDir + "dataset/";
+ String outputParentDir = parentDir + "result/";
+ String outputPath = outputParentDir + "bp_query_group_max.csv";
+
+ int blockSize = 512;
+ int repeatTime = 100;
+ int windowSize = 30;
+
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+ writer.writeRecord(
+ new String[] {
+ "Dataset",
+ "Encoding Algorithm",
+ "Encoding Time",
+ "Decoding Time",
+ "Points",
+ "Compressed Size",
+ "Compression Ratio"
+ });
+
+ File directory = new File(inputParentDir);
+ File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null) {
+ writer.close();
+ return;
+ }
+
+ for (File file : csvFiles) {
+ String datasetName = BPTest.extractFileName(file.toString());
+ System.out.println(datasetName);
+ InputStream inputStream = Files.newInputStream(file.toPath());
+ CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ ArrayList<Float> data = new ArrayList<>();
+
+ int maxDecimal = 0;
+ while (loader.readRecord()) {
+ String fStr = loader.getValues()[0];
+ if (fStr.isEmpty()) {
+ continue;
+ }
+ int curDecimal = BPTest.getDecimalPrecision(fStr);
+ if (curDecimal > maxDecimal) {
+ maxDecimal = curDecimal;
+ }
+ data.add(Float.valueOf(fStr));
+ }
+ inputStream.close();
+
+ int maxMul = (int) Math.pow(10, Math.min(maxDecimal, 8));
+ int[] dataArr = new int[data.size()];
+ for (int i = 0; i < data.size(); i++) {
+ dataArr[i] = (int) (data.get(i) * maxMul);
+ }
+
+ byte[] encodedResult = new byte[Math.max(16, dataArr.length * 8)];
+ int length = 0;
+ long start = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ length = BPTest.Encoder(dataArr, blockSize, encodedResult);
+ }
+ long end = System.nanoTime();
+ long encodeTime = (end - start) / repeatTime;
+
+ int[] groupResult = null;
+ start = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ groupResult = queryGroupMaxIndexByDecode(encodedResult, windowSize);
+ }
+ end = System.nanoTime();
+ long queryTime = (end - start) / repeatTime;
+ System.out.println("groupCount: " + (groupResult == null ? 0 : groupResult.length));
+
+ double compressionRatio = length / (double) (Math.max(1, data.size()) * Long.BYTES);
+ writer.writeRecord(
+ new String[] {
+ datasetName,
+ "BP",
+ String.valueOf(encodeTime),
+ String.valueOf(queryTime),
+ String.valueOf(data.size()),
+ String.valueOf(length),
+ String.valueOf(compressionRatio)
+ });
+ }
+ writer.close();
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPQuerySortTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPQuerySortTest.java
new file mode 100644
index 00000000000..753e49dcda1
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPQuerySortTest.java
@@ -0,0 +1,142 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+
+public class BPQuerySortTest {
+
+ public static int[] querySortSingleBlockByDecode(byte[] encodedResult, int targetBlockId) {
+ int[] decoded = BPTest.Decoder(encodedResult);
+ int blockSize = ((encodedResult[4] & 0xFF) << 24)
+ | ((encodedResult[5] & 0xFF) << 16)
+ | ((encodedResult[6] & 0xFF) << 8)
+ | (encodedResult[7] & 0xFF);
+ int dataLength = decoded.length;
+ int numBlocks = dataLength / blockSize;
+ int remainder = dataLength % blockSize;
+ int totalBlocks = numBlocks + (remainder > 0 ? 1 : 0);
+ if (targetBlockId < 0 || targetBlockId >= totalBlocks) {
+ return new int[0];
+ }
+
+ int count = targetBlockId < numBlocks ? blockSize : remainder;
+ int start = targetBlockId * blockSize;
+ int[] indices = new int[count];
+ for (int i = 0; i < count; i++) {
+ indices[i] = start + i;
+ }
+
+ for (int i = 0; i < count - 1; i++) {
+ int min = i;
+ for (int j = i + 1; j < count; j++) {
+ int v1 = decoded[indices[j]];
+ int v2 = decoded[indices[min]];
+ if (v1 < v2 || (v1 == v2 && indices[j] < indices[min])) {
+ min = j;
+ }
+ }
+ int t = indices[i];
+ indices[i] = indices[min];
+ indices[min] = t;
+ }
+ return indices;
+ }
+
+ @Test
+ public void testDecodeSortSingleBlock() throws IOException {
+ String parentDir = "D://github/xjz17/subcolumn/";
+ String inputParentDir = parentDir + "dataset/";
+ String outputParentDir = parentDir + "result/";
+ String outputPath = outputParentDir + "bp_query_sort_decode.csv";
+
+ int blockSize = 512;
+ int repeatTime = 100;
+ int targetBlockId = 0;
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+ writer.writeRecord(
+ new String[] {
+ "Dataset",
+ "Encoding Algorithm",
+ "Encoding Time",
+ "Decoding Time",
+ "Points",
+ "Compressed Size",
+ "Compression Ratio"
+ });
+
+ File directory = new File(inputParentDir);
+ File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null) {
+ writer.close();
+ return;
+ }
+
+ for (File file : csvFiles) {
+ String datasetName = BPTest.extractFileName(file.toString());
+ System.out.println(datasetName);
+ InputStream inputStream = Files.newInputStream(file.toPath());
+ CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ ArrayList<Float> data = new ArrayList<>();
+
+ int maxDecimal = 0;
+ while (loader.readRecord()) {
+ String fStr = loader.getValues()[0];
+ if (fStr.isEmpty()) {
+ continue;
+ }
+ int curDecimal = BPTest.getDecimalPrecision(fStr);
+ if (curDecimal > maxDecimal) {
+ maxDecimal = curDecimal;
+ }
+ data.add(Float.valueOf(fStr));
+ }
+ inputStream.close();
+
+ int maxMul = (int) Math.pow(10, Math.min(maxDecimal, 8));
+ int[] dataArr = new int[data.size()];
+ for (int i = 0; i < data.size(); i++) {
+ dataArr[i] = (int) (data.get(i) * maxMul);
+ }
+
+ byte[] encodedResult = new byte[Math.max(16, dataArr.length * 8)];
+ int length = 0;
+ long start = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ length = BPTest.Encoder(dataArr, blockSize, encodedResult);
+ }
+ long end = System.nanoTime();
+ long encodeTime = (end - start) / repeatTime;
+
+ int[] sortedIndices = null;
+ start = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ sortedIndices = querySortSingleBlockByDecode(encodedResult, targetBlockId);
+ }
+ end = System.nanoTime();
+ long queryTime = (end - start) / repeatTime;
+ System.out.println("sortedCount: " + (sortedIndices == null ? 0 : sortedIndices.length));
+
+ double compressionRatio = length / (double) (Math.max(1, data.size()) * Long.BYTES);
+ writer.writeRecord(
+ new String[] {
+ datasetName,
+ "BP",
+ String.valueOf(encodeTime),
+ String.valueOf(queryTime),
+ String.valueOf(data.size()),
+ String.valueOf(length),
+ String.valueOf(compressionRatio)
+ });
+ }
+ writer.close();
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPTest.java
index 36f5a35afdc..b7552ad1719 100644
--- a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPTest.java
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPTest.java
@@ -18,23 +18,43 @@ import com.csvreader.CsvWriter;
public class BPTest {
+ private static final int PACK_BIT_STEP = 4;
+ private static final int BIT_IO_STEP = 4;
+
public static int bitWidth(int value) {
return 32 - Integer.numberOfLeadingZeros(value);
}
+ private static void storeByteBits(byte[] result, int index, int byteVal) {
+ storeByteBits(result, index, byteVal, PACK_BIT_STEP);
+ }
+
+ private static void storeByteBits(byte[] result, int index, int byteVal, int step) {
+ int bitIndex = 8;
+ int remaining = 8;
+ while (remaining > 0) {
+ int bitsToWrite = Math.min(step, Math.min(bitIndex, remaining));
+ bitIndex -= bitsToWrite;
+ int mask = (1 << bitsToWrite) - 1;
+ int bits = (byteVal >> (remaining - bitsToWrite)) & mask;
+ result[index] &= (byte) ~(mask << bitIndex);
+ result[index] |= (byte) (bits << bitIndex);
+ remaining -= bitsToWrite;
+ }
+ }
+
public static void intToBytes(int srcNum, byte[] result, int pos, int width) {
int cnt = pos & 0x07;
int index = pos >> 3;
while (width > 0) {
- int m = width + cnt >= 8 ? 8 - cnt : width;
+ int available = 8 - cnt;
+ int m = Math.min(BIT_IO_STEP, Math.min(available, width));
width -= m;
- int mask = 1 << (8 - cnt);
+ int mask = (1 << m) - 1;
+ int bits = (srcNum >> width) & mask;
+ int byteMask = mask << (8 - cnt - m);
+ result[index] = (byte) (result[index] & ~byteMask | (bits << (8 - cnt - m)));
cnt += m;
- byte y = (byte) (srcNum >>> width);
- y = (byte) (y << (8 - cnt));
- mask = ~(mask - (1 << (8 - cnt)));
- result[index] = (byte) (result[index] & mask | y);
- srcNum = srcNum & ~(-1 << width);
if (cnt == 8) {
index++;
cnt = 0;
@@ -66,16 +86,12 @@ public class BPTest {
byte[] encoded_result) {
int bufIdx = 0;
int valueIdx = offset;
- // remaining bits for the current unfinished Integer
int leftBit = 0;
while (valueIdx < 8 + offset) {
- // buffer is used for saving 32 bits as a part of result
int buffer = 0;
- // remaining size of bits in the 'buffer'
int leftSize = 32;
- // encode the left bits of current Integer to 'buffer'
if (leftBit > 0) {
buffer |= (values[valueIdx] << (32 - leftBit));
leftSize -= leftBit;
@@ -84,22 +100,23 @@ public class BPTest {
}
while (leftSize >= width && valueIdx < 8 + offset) {
- // encode one Integer to the 'buffer'
buffer |= (values[valueIdx] << (leftSize - width));
leftSize -= width;
valueIdx++;
}
- // If the remaining space of the buffer can not save the bits for one Integer,
+
if (leftSize > 0 && valueIdx < 8 + offset) {
- // put the first 'leftSize' bits of the Integer into remaining space of the
- // buffer
buffer |= (values[valueIdx] >>> (width - leftSize));
leftBit = width - leftSize;
}
- // put the buffer into the final result
for (int j = 0; j < 4; j++) {
- encoded_result[encode_pos] = (byte) ((buffer >>> ((3 - j) * 8)) & 0xFF);
+ int outByte = (buffer >>> ((3 - j) * 8)) & 0xFF;
+ if (j == 3) {
+ storeByteBits(encoded_result, encode_pos, outByte, 3);
+ } else {
+ storeByteBits(encoded_result, encode_pos, outByte);
+ }
encode_pos++;
bufIdx++;
if (bufIdx >= width) {
@@ -107,31 +124,22 @@ public class BPTest {
}
}
}
-
}
public static void unpack8Values(byte[] encoded, int offset, int width, int[] result_list, int result_offset) {
int byteIdx = offset;
long buffer = 0;
- // total bits which have read from 'buf' to 'buffer'. i.e.,
- // number of available bits to be decoded.
int totalBits = 0;
int valueIdx = 0;
while (valueIdx < 8) {
- // If current available bits are not enough to decode one Integer,
- // then add next byte from buf to 'buffer' until totalBits >= width
while (totalBits < width) {
buffer = (buffer << 8) | (encoded[byteIdx] & 0xFF);
byteIdx++;
totalBits += 8;
}
- // If current available bits are enough to decode one Integer,
- // then decode one Integer one by one until left bits in 'buffer' is
- // not enough to decode one Integer.
while (totalBits >= width && valueIdx < 8) {
- // result_list.add((int) (buffer >>> (totalBits - width)));
result_list[result_offset + valueIdx] = (int) (buffer >>> (totalBits - width));
valueIdx++;
totalBits -= width;
@@ -162,12 +170,10 @@ public class BPTest {
public static int decodeBitPacking(
byte[] encoded, int decode_pos, int bit_width, int num_values, int[] result_list) {
- // ArrayList<Integer> result_list = new ArrayList<>();
- // int[] result_list = new int[num_values];
int block_num = num_values / 8;
int remainder = num_values % 8;
- for (int i = 0; i < block_num; i++) { // bitpacking
+ for (int i = 0; i < block_num; i++) {
unpack8Values(encoded, decode_pos, bit_width, result_list, i * 8);
decode_pos += bit_width;
}
@@ -192,15 +198,10 @@ public class BPTest {
}
int m = bitWidth(maxValue);
- // System.out.println("m: " + m);
- // writeBits(encoded_result, startBitPosition, 8, m);
- // startBitPosition += 8;
encoded_result[encode_pos] = (byte) m;
encode_pos += 1;
- // bitPacking(list, encoded_result, startBitPosition, m, list_length);
- // startBitPosition += m * list_length;
encode_pos = bitPacking(list, m, encode_pos, encoded_result, list_length);
return encode_pos;
@@ -372,15 +373,12 @@ public class BPTest {
}
public static int getDecimalPrecision(String str) {
- // 查找小数点的位置
int decimalIndex = str.indexOf(".");
- // 如果没有小数点,精度为0
if (decimalIndex == -1) {
return 0;
}
- // 获取小数点后的部分并返回其长度
return str.substring(decimalIndex + 1).length();
}
@@ -402,24 +400,19 @@ public class BPTest {
}
@Test
- public void testSubcolumn() throws IOException {
+ public void test0() throws IOException {
+ // String parent_dir = "/Users/xiaojinzhao/Documents/GitHub/subcolumn/";
String parent_dir = "D:/github/xjz17/subcolumn/";
String input_parent_dir = parent_dir + "dataset/";
- String output_parent_dir = "D:/encoding-subcolumn/result/";
- // String output_parent_dir = parent_dir + "result/";
+ String output_parent_dir = parent_dir + "result/";
String outputPath = output_parent_dir + "bp.csv";
int block_size = 1024;
- int repeatTime = 100;
-
- // repeatTime = 1;
-
- List<String> integerDatasets = new ArrayList<>();
- integerDatasets.add("Wine-Tasting");
+ int repeatTime = 500;
CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
writer.setRecordDelimiter('\n');
@@ -436,7 +429,6 @@ public class BPTest {
writer.writeRecord(head);
File directory = new File(input_parent_dir);
- // File[] csvFiles = directory.listFiles();
File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
for (File file : csvFiles) {
@@ -458,10 +450,7 @@ public class BPTest {
if (cur_decimal > max_decimal) {
max_decimal = cur_decimal;
}
- // String value = loader.getValues()[index];
data1.add(Float.valueOf(f_str));
- // data2.add(Integer.valueOf(loader.getValues()[1]));
- // data.add(Integer.valueOf(value));
}
inputStream.close();
int[] data2_arr = new int[data1.size()];
@@ -491,11 +480,7 @@ public class BPTest {
double ratioTmp;
- if (integerDatasets.contains(datasetName)) {
- ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- } else {
- ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
- }
+ ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
ratio += ratioTmp;
@@ -528,133 +513,4 @@ public class BPTest {
writer.close();
}
- @Test
- public void testTransData() throws IOException {
- String parent_dir = "D:/github/xjz17/subcolumn/";
-
- String output_parent_dir = "D:/encoding-subcolumn/trans_data_result/";
- // String output_parent_dir = parent_dir + "trans_data_result/";
-
- String input_parent_dir = parent_dir + "trans_data/";
-
- ArrayList<String> input_path_list = new ArrayList<>();
- ArrayList<String> output_path_list = new ArrayList<>();
- ArrayList<String> dataset_name = new ArrayList<>();
- ArrayList<Integer> dataset_block_size = new ArrayList<>();
-
- try (Stream<Path> paths = Files.walk(Paths.get(input_parent_dir))) {
- paths.filter(Files::isDirectory)
- .filter(path -> !path.equals(Paths.get(input_parent_dir)))
- .forEach(dir -> {
- String name = dir.getFileName().toString();
- dataset_name.add(name);
- input_path_list.add(dir.toString());
- dataset_block_size.add(1024);
- });
- }
-
- String outputPath = output_parent_dir + "bp.csv";
- CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
- writer.setRecordDelimiter('\n');
-
- String[] head = {
- "Dataset",
- "Encoding Algorithm",
- "Encoding Time",
- "Decoding Time",
- "Points",
- "Compressed Size",
- "Compression Ratio"
- };
- writer.writeRecord(head);
-
- int repeatTime = 100;
-
- for (int file_i = 0; file_i < input_path_list.size(); file_i++) {
-
- String inputPath = input_path_list.get(file_i);
- System.out.println(inputPath);
-
- File file = new File(inputPath);
- File[] tempList = file.listFiles();
-
- long totalEncodeTime = 0;
- long totalDecodeTime = 0;
- double totalCompressedSize = 0;
- int totalPoints = 0;
-
- for (File f : tempList) {
- String datasetName = extractFileName(f.toString());
- InputStream inputStream = Files.newInputStream(f.toPath());
-
- CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
- ArrayList<Integer> data1 = new ArrayList<>();
- ArrayList<Integer> data2 = new ArrayList<>();
-
- loader.readHeaders();
- while (loader.readRecord()) {
- // String value = loader.getValues()[index];
- data1.add(Integer.valueOf(loader.getValues()[0]));
- data2.add(Integer.valueOf(loader.getValues()[1]));
- // data.add(Integer.valueOf(value));
- }
- inputStream.close();
- int[] data2_arr = new int[data1.size()];
- for (int i = 0; i < data2.size(); i++) {
- data2_arr[i] = data2.get(i);
- }
- byte[] encoded_result = new byte[data2_arr.length * 4];
- long encodeTime = 0;
- long decodeTime = 0;
- double ratio = 0;
- double compressed_size = 0;
-
- int length = 0;
-
- long s = System.nanoTime();
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- length = Encoder(data2_arr, dataset_block_size.get(file_i), encoded_result);
- }
-
- long e = System.nanoTime();
- encodeTime += ((e - s) / repeatTime);
- compressed_size += length;
- double ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- ratio += ratioTmp;
- s = System.nanoTime();
-
- int[] data2_arr_decoded = new int[data1.size()];
-
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- data2_arr_decoded = Decoder(encoded_result);
- }
-
- e = System.nanoTime();
- decodeTime += ((e - s) / repeatTime);
-
- totalEncodeTime += encodeTime;
- totalDecodeTime += decodeTime;
- totalCompressedSize += compressed_size;
- totalPoints += data1.size();
-
- }
-
- double compressionRatio = totalCompressedSize / (totalPoints * Integer.BYTES);
-
- String[] record = {
- dataset_name.get(file_i),
- "BP",
- String.valueOf(totalEncodeTime),
- String.valueOf(totalDecodeTime),
- String.valueOf(totalPoints),
- String.valueOf(totalCompressedSize),
- String.valueOf(compressionRatio)
- };
-
- writer.writeRecord(record);
- System.out.println(compressionRatio);
- }
- writer.close();
- }
-
}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateAppendTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateAppendTest.java
new file mode 100644
index 00000000000..6b1714dad5d
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateAppendTest.java
@@ -0,0 +1,125 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+
+public class BPUpdateAppendTest {
+
+ private static void int2Bytes(int value, int pos, byte[] out) {
+ out[pos] = (byte) (value >> 24);
+ out[pos + 1] = (byte) (value >> 16);
+ out[pos + 2] = (byte) (value >> 8);
+ out[pos + 3] = (byte) value;
+ }
+
+ private static int skipBlock(byte[] encodedResult, int encodePos, int rowCount) {
+ encodePos += 4;
+ int[] tmp = new int[rowCount];
+ return BPTest.BPDecoder(encodedResult, encodePos, tmp);
+ }
+
+ @Test
+ public void test0() throws IOException {
+ String parentDir = "D://github/xjz17/subcolumn/";
+ String inputParentDir = parentDir + "dataset/";
+ String outputParentDir = parentDir + "result/update/";
+ String outputPath = outputParentDir + "bp_update_append.csv";
+ int blockSize = 512;
+ int repeatTime = 200;
+
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+ writer.writeRecord(
+ new String[] {
+ "Dataset",
+ "Encoding Algorithm",
+ "Encoding Time",
+ "Append-only Time with Sub-column",
+ "Points",
+ "Remaining Points",
+ "Compressed Size",
+ "Compression Ratio"
+ });
+
+ File directory = new File(inputParentDir);
+ File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null) {
+ writer.close();
+ return;
+ }
+ for (File file : csvFiles) {
+ String datasetName = BPTest.extractFileName(file.toString());
+ if (datasetName.equals("POI-lon") || datasetName.equals("POI-lat")) {
+ continue;
+ }
+ InputStream inputStream = Files.newInputStream(file.toPath());
+ CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ ArrayList<Float> data = new ArrayList<>();
+ int maxDecimal = 0;
+ while (loader.readRecord()) {
+ String fStr = loader.getValues()[0];
+ if (fStr.isEmpty()) continue;
+ int d = BPTest.getDecimalPrecision(fStr);
+ if (d > maxDecimal) maxDecimal = d;
+ data.add(Float.valueOf(fStr));
+ }
+ inputStream.close();
+ int mul = (int) Math.pow(10, Math.min(maxDecimal, 8));
+ int[] origin = new int[data.size()];
+ int maxValue = Integer.MIN_VALUE;
+ for (int i = 0; i < data.size(); i++) {
+ origin[i] = (int) (data.get(i) * mul);
+ if (origin[i] > maxValue) maxValue = origin[i];
+ }
+ if (origin.length == 0) continue;
+
+ int[] appended = new int[origin.length + 1];
+ System.arraycopy(origin, 0, appended, 0, origin.length);
+ appended[origin.length] = (maxValue == Integer.MAX_VALUE) ? maxValue : maxValue + 1;
+
+ byte[] encodedResult = new byte[Math.max(16, appended.length * 8)];
+ int length = 0;
+ long s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) length = BPTest.Encoder(origin, blockSize, encodedResult);
+ long e = System.nanoTime();
+ long encodeTime = (e - s) / repeatTime;
+
+ int numBlocks = origin.length / blockSize;
+ int remainder = origin.length % blockSize;
+ if (remainder <= 0) continue;
+ int tailStart = 8;
+ for (int i = 0; i < numBlocks; i++) tailStart = skipBlock(encodedResult, tailStart, blockSize);
+ int newRemainder = remainder + 1;
+ int updatedLength = length;
+ s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) {
+ int pos = tailStart;
+ if (newRemainder <= 3) {
+ int base = numBlocks * blockSize;
+ for (int i = 0; i < newRemainder; i++) {
+ int2Bytes(appended[base + i], pos, encodedResult);
+ pos += 4;
+ }
+ } else {
+ pos = BPTest.BlockEncoder(appended, numBlocks, blockSize, newRemainder, pos, encodedResult);
+ }
+ updatedLength = pos;
+ }
+ e = System.nanoTime();
+ long appendTime = (e - s) / repeatTime;
+
+ double ratio = length / (double) (Math.max(1, origin.length) * Long.BYTES);
+ writer.writeRecord(new String[] {datasetName, "BP", String.valueOf(encodeTime), String.valueOf(appendTime),
+ String.valueOf(origin.length), String.valueOf(remainder), String.valueOf(updatedLength), String.valueOf(ratio)});
+ }
+ writer.close();
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateDeleteSmallerTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateDeleteSmallerTest.java
new file mode 100644
index 00000000000..113b04e292d
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateDeleteSmallerTest.java
@@ -0,0 +1,72 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+
+public class BPUpdateDeleteSmallerTest {
+ @Test
+ public void test0() throws IOException {
+ String parentDir = "D://github/xjz17/subcolumn/";
+ String inputParentDir = parentDir + "dataset/";
+ String outputPath = parentDir + "result/update/bp_update_delete_smaller.csv";
+ int blockSize = 512, repeatTime = 200;
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+ writer.writeRecord(new String[]{"Dataset","Encoding Algorithm","Encoding Time","Delete Time with Sub-column","Points","Remaining Points","Compressed Size","Compression Ratio"});
+ File[] csvFiles = new File(inputParentDir).listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null) { writer.close(); return; }
+ for (File file : csvFiles) {
+ String name = BPTest.extractFileName(file.toString());
+ if (name.equals("POI-lon") || name.equals("POI-lat")) continue;
+ InputStream is = Files.newInputStream(file.toPath());
+ CsvReader loader = new CsvReader(is, StandardCharsets.UTF_8);
+ ArrayList<Float> data = new ArrayList<>();
+ int dec = 0;
+ while (loader.readRecord()) { String s = loader.getValues()[0]; if (s.isEmpty()) continue; dec = Math.max(dec, BPTest.getDecimalPrecision(s)); data.add(Float.valueOf(s)); }
+ is.close();
+ int mul = (int) Math.pow(10, Math.min(dec, 8));
+ int[] origin = new int[data.size()];
+ for (int i = 0; i < data.size(); i++) origin[i] = (int) (data.get(i) * mul);
+ if (origin.length <= 1) continue;
+ byte[] encoded = new byte[Math.max(16, origin.length * 8)];
+ int len = 0;
+ long s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) len = BPTest.Encoder(origin, blockSize, encoded);
+ long e = System.nanoTime();
+ long encodeTime = (e - s) / repeatTime;
+ int num = origin.length / blockSize, rem = origin.length % blockSize;
+ if (rem <= 0) continue;
+ int[] deleted = new int[origin.length - 1];
+ System.arraycopy(origin, 0, deleted, 0, deleted.length);
+ int tailStart = 8;
+ for (int i = 0; i < num; i++) {
+ tailStart += 4;
+ int[] tmp = new int[blockSize];
+ tailStart = BPTest.BPDecoder(encoded, tailStart, tmp);
+ }
+ int newRem = rem - 1;
+ int updatedLen = len;
+ s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) {
+ if (newRem == 0) {
+ updatedLen = tailStart;
+ } else {
+ updatedLen = BPTest.BlockEncoder(deleted, num, blockSize, newRem, tailStart, encoded);
+ }
+ }
+ e = System.nanoTime();
+ long t = (e - s) / repeatTime;
+ double ratio = len / (double)(Math.max(1,origin.length)*Long.BYTES);
+ writer.writeRecord(new String[]{name,"BP",String.valueOf(encodeTime),String.valueOf(t),String.valueOf(origin.length),String.valueOf(rem),String.valueOf(updatedLen),String.valueOf(ratio)});
+ }
+ writer.close();
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateInsertLargerTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateInsertLargerTest.java
new file mode 100644
index 00000000000..8e2dea117f4
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateInsertLargerTest.java
@@ -0,0 +1,68 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+
+public class BPUpdateInsertLargerTest {
+ private static int skipBlock(byte[] encodedResult, int encodePos, int rowCount) {
+ encodePos += 4;
+ int[] tmp = new int[rowCount];
+ return BPTest.BPDecoder(encodedResult, encodePos, tmp);
+ }
+
+ @Test
+ public void test0() throws IOException {
+ String parentDir = "D://github/xjz17/subcolumn/";
+ String inputParentDir = parentDir + "dataset/";
+ String outputPath = parentDir + "result/update/bp_update_insert_larger.csv";
+ int blockSize = 512, repeatTime = 200;
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+ writer.writeRecord(new String[]{"Dataset","Encoding Algorithm","Encoding Time","Insert Time with Sub-column","Points","Remaining Points","Compressed Size","Compression Ratio"});
+ File[] csvFiles = new File(inputParentDir).listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null) { writer.close(); return; }
+ for (File file : csvFiles) {
+ String name = BPTest.extractFileName(file.toString());
+ if (name.equals("POI-lon") || name.equals("POI-lat")) continue;
+ InputStream is = Files.newInputStream(file.toPath());
+ CsvReader loader = new CsvReader(is, StandardCharsets.UTF_8);
+ ArrayList<Float> data = new ArrayList<>();
+ int dec = 0;
+ while (loader.readRecord()) { String s = loader.getValues()[0]; if (s.isEmpty()) continue; dec = Math.max(dec, BPTest.getDecimalPrecision(s)); data.add(Float.valueOf(s)); }
+ is.close();
+ int mul = (int) Math.pow(10, Math.min(dec, 8));
+ int[] origin = new int[data.size()];
+ int max = Integer.MIN_VALUE;
+ for (int i = 0; i < data.size(); i++) { origin[i] = (int) (data.get(i) * mul); max = Math.max(max, origin[i]); }
+ if (origin.length == 0) continue;
+ int[] inserted = new int[origin.length + 1];
+ System.arraycopy(origin, 0, inserted, 0, origin.length);
+ inserted[origin.length] = max == Integer.MAX_VALUE ? max : max + 1;
+ byte[] encoded = new byte[Math.max(16, inserted.length * 8)];
+ int len = 0;
+ long s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) len = BPTest.Encoder(origin, blockSize, encoded);
+ long e = System.nanoTime();
+ long encodeTime = (e - s) / repeatTime;
+ int num = origin.length / blockSize, rem = origin.length % blockSize;
+ if (rem <= 0) continue;
+ int tail = 8; for (int i=0;i<num;i++) tail = skipBlock(encoded, tail, blockSize);
+ int newRem = rem + 1, updatedLen = len;
+ s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) updatedLen = BPTest.BlockEncoder(inserted, num, blockSize, newRem, tail, encoded);
+ e = System.nanoTime();
+ long t = (e - s) / repeatTime;
+ double ratio = len / (double)(Math.max(1,origin.length)*Long.BYTES);
+ writer.writeRecord(new String[]{name,"BP",String.valueOf(encodeTime),String.valueOf(t),String.valueOf(origin.length),String.valueOf(rem),String.valueOf(updatedLen),String.valueOf(ratio)});
+ }
+ writer.close();
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateInsertSmallerTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateInsertSmallerTest.java
new file mode 100644
index 00000000000..e9bb6056049
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateInsertSmallerTest.java
@@ -0,0 +1,68 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+
+public class BPUpdateInsertSmallerTest {
+ private static int skipBlock(byte[] encodedResult, int encodePos, int rowCount) {
+ encodePos += 4;
+ int[] tmp = new int[rowCount];
+ return BPTest.BPDecoder(encodedResult, encodePos, tmp);
+ }
+
+ @Test
+ public void test0() throws IOException {
+ String parentDir = "D://github/xjz17/subcolumn/";
+ String inputParentDir = parentDir + "dataset/";
+ String outputPath = parentDir + "result/update/bp_update_insert_smaller.csv";
+ int blockSize = 512, repeatTime = 200;
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+ writer.writeRecord(new String[]{"Dataset","Encoding Algorithm","Encoding Time","Insert Time with Sub-column","Points","Remaining Points","Compressed Size","Compression Ratio"});
+ File[] csvFiles = new File(inputParentDir).listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null) { writer.close(); return; }
+ for (File file : csvFiles) {
+ String name = BPTest.extractFileName(file.toString());
+ if (name.equals("POI-lon") || name.equals("POI-lat")) continue;
+ InputStream is = Files.newInputStream(file.toPath());
+ CsvReader loader = new CsvReader(is, StandardCharsets.UTF_8);
+ ArrayList<Float> data = new ArrayList<>();
+ int dec = 0;
+ while (loader.readRecord()) { String s = loader.getValues()[0]; if (s.isEmpty()) continue; dec = Math.max(dec, BPTest.getDecimalPrecision(s)); data.add(Float.valueOf(s)); }
+ is.close();
+ int mul = (int) Math.pow(10, Math.min(dec, 8));
+ int[] origin = new int[data.size()];
+ int min = Integer.MAX_VALUE;
+ for (int i = 0; i < data.size(); i++) { origin[i] = (int) (data.get(i) * mul); min = Math.min(min, origin[i]); }
+ if (origin.length == 0) continue;
+ int[] inserted = new int[origin.length + 1];
+ System.arraycopy(origin, 0, inserted, 0, origin.length);
+ inserted[origin.length] = min == Integer.MIN_VALUE ? min : min - 1;
+ byte[] encoded = new byte[Math.max(16, inserted.length * 8)];
+ int len = 0;
+ long s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) len = BPTest.Encoder(origin, blockSize, encoded);
+ long e = System.nanoTime();
+ long encodeTime = (e - s) / repeatTime;
+ int num = origin.length / blockSize, rem = origin.length % blockSize;
+ if (rem <= 0) continue;
+ int tail = 8; for (int i=0;i<num;i++) tail = skipBlock(encoded, tail, blockSize);
+ int newRem = rem + 1, updatedLen = len;
+ s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) updatedLen = BPTest.BlockEncoder(inserted, num, blockSize, newRem, tail, encoded);
+ e = System.nanoTime();
+ long t = (e - s) / repeatTime;
+ double ratio = len / (double)(Math.max(1,origin.length)*Long.BYTES);
+ writer.writeRecord(new String[]{name,"BP",String.valueOf(encodeTime),String.valueOf(t),String.valueOf(origin.length),String.valueOf(rem),String.valueOf(updatedLen),String.valueOf(ratio)});
+ }
+ writer.close();
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateLargerTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateLargerTest.java
new file mode 100644
index 00000000000..66e5fc7c986
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateLargerTest.java
@@ -0,0 +1,68 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+
+public class BPUpdateLargerTest {
+ private static int skipBlock(byte[] encodedResult, int encodePos, int rowCount) {
+ encodePos += 4;
+ int[] tmp = new int[rowCount];
+ return BPTest.BPDecoder(encodedResult, encodePos, tmp);
+ }
+
+ @Test
+ public void test0() throws IOException {
+ String parentDir = "D://github/xjz17/subcolumn/";
+ String inputParentDir = parentDir + "dataset/";
+ String outputPath = parentDir + "result/update/bp_update_larger.csv";
+ int blockSize = 512, repeatTime = 200;
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+ writer.writeRecord(new String[]{"Dataset","Encoding Algorithm","Encoding Time","Insert Time with Sub-column","Points","Remaining Points","Compressed Size","Compression Ratio"});
+ File[] csvFiles = new File(inputParentDir).listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null) { writer.close(); return; }
+ for (File file : csvFiles) {
+ String name = BPTest.extractFileName(file.toString());
+ if (name.equals("POI-lon") || name.equals("POI-lat")) continue;
+ InputStream is = Files.newInputStream(file.toPath());
+ CsvReader loader = new CsvReader(is, StandardCharsets.UTF_8);
+ ArrayList<Float> data = new ArrayList<>();
+ int dec = 0;
+ while (loader.readRecord()) { String s = loader.getValues()[0]; if (s.isEmpty()) continue; dec = Math.max(dec, BPTest.getDecimalPrecision(s)); data.add(Float.valueOf(s)); }
+ is.close();
+ int mul = (int) Math.pow(10, Math.min(dec, 8));
+ int[] origin = new int[data.size()];
+ int max = Integer.MIN_VALUE;
+ for (int i = 0; i < data.size(); i++) { origin[i] = (int) (data.get(i) * mul); max = Math.max(max, origin[i]); }
+ if (origin.length == 0) continue;
+ byte[] encoded = new byte[Math.max(16, origin.length * 8)];
+ int len = 0;
+ long s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) len = BPTest.Encoder(origin, blockSize, encoded);
+ long e = System.nanoTime();
+ long encodeTime = (e - s) / repeatTime;
+ int num = origin.length / blockSize, rem = origin.length % blockSize;
+ if (rem <= 0) continue;
+ int[] updated = new int[origin.length];
+ System.arraycopy(origin, 0, updated, 0, origin.length);
+ updated[num * blockSize + rem - 1] = max == Integer.MAX_VALUE ? max : max + 1;
+ int tail = 8; for (int i=0;i<num;i++) tail = skipBlock(encoded, tail, blockSize);
+ int updatedLen = len;
+ s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) updatedLen = BPTest.BlockEncoder(updated, num, blockSize, rem, tail, encoded);
+ e = System.nanoTime();
+ long t = (e - s) / repeatTime;
+ double ratio = len / (double)(Math.max(1,origin.length)*Long.BYTES);
+ writer.writeRecord(new String[]{name,"BP",String.valueOf(encodeTime),String.valueOf(t),String.valueOf(origin.length),String.valueOf(rem),String.valueOf(updatedLen),String.valueOf(ratio)});
+ }
+ writer.close();
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateSmallerTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateSmallerTest.java
new file mode 100644
index 00000000000..ea9f4d798a6
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BPUpdateSmallerTest.java
@@ -0,0 +1,68 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+
+public class BPUpdateSmallerTest {
+ private static int skipBlock(byte[] encodedResult, int encodePos, int rowCount) {
+ encodePos += 4;
+ int[] tmp = new int[rowCount];
+ return BPTest.BPDecoder(encodedResult, encodePos, tmp);
+ }
+
+ @Test
+ public void test0() throws IOException {
+ String parentDir = "D://github/xjz17/subcolumn/";
+ String inputParentDir = parentDir + "dataset/";
+ String outputPath = parentDir + "result/update/bp_update_smaller.csv";
+ int blockSize = 512, repeatTime = 200;
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+ writer.writeRecord(new String[]{"Dataset","Encoding Algorithm","Encoding Time","Insert Time with Sub-column","Points","Remaining Points","Compressed Size","Compression Ratio"});
+ File[] csvFiles = new File(inputParentDir).listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null) { writer.close(); return; }
+ for (File file : csvFiles) {
+ String name = BPTest.extractFileName(file.toString());
+ if (name.equals("POI-lon") || name.equals("POI-lat")) continue;
+ InputStream is = Files.newInputStream(file.toPath());
+ CsvReader loader = new CsvReader(is, StandardCharsets.UTF_8);
+ ArrayList<Float> data = new ArrayList<>();
+ int dec = 0;
+ while (loader.readRecord()) { String s = loader.getValues()[0]; if (s.isEmpty()) continue; dec = Math.max(dec, BPTest.getDecimalPrecision(s)); data.add(Float.valueOf(s)); }
+ is.close();
+ int mul = (int) Math.pow(10, Math.min(dec, 8));
+ int[] origin = new int[data.size()];
+ int min = Integer.MAX_VALUE;
+ for (int i = 0; i < data.size(); i++) { origin[i] = (int) (data.get(i) * mul); min = Math.min(min, origin[i]); }
+ if (origin.length == 0) continue;
+ byte[] encoded = new byte[Math.max(16, origin.length * 8)];
+ int len = 0;
+ long s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) len = BPTest.Encoder(origin, blockSize, encoded);
+ long e = System.nanoTime();
+ long encodeTime = (e - s) / repeatTime;
+ int num = origin.length / blockSize, rem = origin.length % blockSize;
+ if (rem <= 0) continue;
+ int[] updated = new int[origin.length];
+ System.arraycopy(origin, 0, updated, 0, origin.length);
+ updated[num * blockSize + rem - 1] = min == Integer.MIN_VALUE ? min : min - 1;
+ int tail = 8; for (int i=0;i<num;i++) tail = skipBlock(encoded, tail, blockSize);
+ int updatedLen = len;
+ s = System.nanoTime();
+ for (int r = 0; r < repeatTime; r++) updatedLen = BPTest.BlockEncoder(updated, num, blockSize, rem, tail, encoded);
+ e = System.nanoTime();
+ long t = (e - s) / repeatTime;
+ double ratio = len / (double)(Math.max(1,origin.length)*Long.BYTES);
+ writer.writeRecord(new String[]{name,"BP",String.valueOf(encodeTime),String.valueOf(t),String.valueOf(origin.length),String.valueOf(rem),String.valueOf(updatedLen),String.valueOf(ratio)});
+ }
+ writer.close();
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFDouble2Test.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFDouble2Test.java
new file mode 100644
index 00000000000..3cafe8abf3e
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFDouble2Test.java
@@ -0,0 +1,553 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.Test;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+
+public class BUFFDouble2Test {
+
+ public static int bitWidth(int value) {
+ return 32 - Integer.numberOfLeadingZeros(value);
+ }
+
+ public static int bitWidth(long value) {
+ return 64 - Long.numberOfLeadingZeros(value);
+ }
+
+ // 逐位写入整数到字节数组
+ public static void writeBits(int srcNum, byte[] result, int bitPos, int width) {
+ for (int i = 0; i < width; i++) {
+ int bit = (srcNum >> (width - 1 - i)) & 1;
+ int byteIndex = bitPos / 8;
+ int bitOffset = 7 - (bitPos % 8); // 从最高位开始
+
+ if (bit == 1) {
+ result[byteIndex] |= (1 << bitOffset);
+ } else {
+ result[byteIndex] &= ~(1 << bitOffset);
+ }
+ bitPos++;
+ }
+ }
+
+ // 从字节数组逐位读取整数
+ public static int readBits(byte[] result, int bitPos, int width) {
+ int ret = 0;
+ for (int i = 0; i < width; i++) {
+ int byteIndex = bitPos / 8;
+ int bitOffset = 7 - (bitPos % 8);
+ int bit = (result[byteIndex] >> bitOffset) & 1;
+ ret = (ret << 1) | bit;
+ bitPos++;
+ }
+ return ret;
+ }
+
+ // 逐位写入长整数到字节数组
+ public static void writeBits(long srcNum, byte[] result, int bitPos, int width) {
+ for (int i = 0; i < width; i++) {
+ long bit = (srcNum >> (width - 1 - i)) & 1L;
+ int byteIndex = bitPos / 8;
+ int bitOffset = 7 - (bitPos % 8); // 从最高位开始
+
+ if (bit == 1) {
+ result[byteIndex] |= (1 << bitOffset);
+ } else {
+ result[byteIndex] &= ~(1 << bitOffset);
+ }
+ bitPos++;
+ }
+ }
+
+ // 从字节数组逐位读取长整数
+ public static long readBitsLong(byte[] result, int bitPos, int width) {
+ long ret = 0;
+ for (int i = 0; i < width; i++) {
+ int byteIndex = bitPos / 8;
+ int bitOffset = 7 - (bitPos % 8);
+ long bit = (result[byteIndex] >> bitOffset) & 1L;
+ ret = (ret << 1) | bit;
+ bitPos++;
+ }
+ return ret;
+ }
+
+ // 逐位打包整数数组
+ public static int bitPacking(int[] numbers, int bit_width, int startBitPos,
+ byte[] encoded_result, int num_values) {
+ int currentBitPos = startBitPos;
+
+ for (int i = 0; i < num_values; i++) {
+ writeBits(numbers[i], encoded_result, currentBitPos, bit_width);
+ currentBitPos += bit_width;
+ }
+
+ return (currentBitPos + 7) / 8; // 返回字节位置
+ }
+
+ // 逐位打包长整数数组
+ public static int bitPacking(long[] numbers, int bit_width, int startBitPos,
+ byte[] encoded_result, int num_values) {
+ int currentBitPos = startBitPos;
+
+ for (int i = 0; i < num_values; i++) {
+ writeBits(numbers[i], encoded_result, currentBitPos, bit_width);
+ currentBitPos += bit_width;
+ }
+
+ return (currentBitPos + 7) / 8; // 返回字节位置
+ }
+
+ // 逐位解包整数数组
+ public static int decodeBitPacking(
+ byte[] encoded, int startBitPos, int bit_width, int num_values, int[] result_list) {
+ int currentBitPos = startBitPos;
+
+ for (int i = 0; i < num_values; i++) {
+ result_list[i] = readBits(encoded, currentBitPos, bit_width);
+ currentBitPos += bit_width;
+ }
+
+ return (currentBitPos + 7) / 8; // 返回字节位置
+ }
+
+ // 逐位解包长整数数组
+ public static int decodeBitPacking(
+ byte[] encoded, int startBitPos, int bit_width, int num_values, long[] result_list) {
+ int currentBitPos = startBitPos;
+
+ for (int i = 0; i < num_values; i++) {
+ result_list[i] = readBitsLong(encoded, currentBitPos, bit_width);
+ currentBitPos += bit_width;
+ }
+
+ return (currentBitPos + 7) / 8; // 返回字节位置
+ }
+
+ // 辅助函数:将整数写入字节数组(字节对齐)
+ public static void int2Bytes(int integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer >> 24);
+ cur_byte[encode_pos + 1] = (byte) (integer >> 16);
+ cur_byte[encode_pos + 2] = (byte) (integer >> 8);
+ cur_byte[encode_pos + 3] = (byte) (integer);
+ }
+
+ // 辅助函数:将长整数写入字节数组(字节对齐)
+ public static void long2Bytes(long integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer >> 56);
+ cur_byte[encode_pos + 1] = (byte) (integer >> 48);
+ cur_byte[encode_pos + 2] = (byte) (integer >> 40);
+ cur_byte[encode_pos + 3] = (byte) (integer >> 32);
+ cur_byte[encode_pos + 4] = (byte) (integer >> 24);
+ cur_byte[encode_pos + 5] = (byte) (integer >> 16);
+ cur_byte[encode_pos + 6] = (byte) (integer >> 8);
+ cur_byte[encode_pos + 7] = (byte) (integer);
+ }
+
+ // 从字节数组读取整数
+ public static int bytes2Integer(byte[] encoded, int start, int num) {
+ int value = 0;
+ for (int i = 0; i < num; i++) {
+ value <<= 8;
+ int b = encoded[i + start] & 0xFF;
+ value |= b;
+ }
+ return value;
+ }
+
+ // 从字节数组读取长整数
+ public static long bytes2Long(byte[] encoded, int start, int num) {
+ long value = 0;
+ for (int i = 0; i < num; i++) {
+ value <<= 8;
+ int b = encoded[i + start] & 0xFF;
+ value |= b;
+ }
+ return value;
+ }
+
+ public static int[] bits_needed = { 0, 5, 8, 11, 15, 18, 21, 25, 28, 31, 35,
+ 38, 41, 45, 48, 51, 55, 58
+ };
+
+ public static int BlockEncoder(double[] data, int block_index, int block_size, int remainder, int max_decimal,
+ int encode_pos, byte[] encoded_result) {
+
+ long[] sign_bits = new long[remainder];
+ long[] integer_parts = new long[remainder];
+ long[] decimal_parts = new long[remainder];
+
+ long min_integer_part = Long.MAX_VALUE;
+ long max_integer_part = Long.MIN_VALUE;
+
+ // 提取每个双精度值的符号、整数部分和小数部分
+ for (int i = 0; i < remainder; i++) {
+ double value = data[block_index * block_size + i];
+
+ // 符号位
+ if (value < 0) {
+ sign_bits[i] = 1;
+ }
+
+ // 整数部分
+ long currentInt = (long) Math.abs(value);
+ integer_parts[i] = currentInt;
+
+ // 更新最小和最大整数部分
+ if (currentInt < min_integer_part) {
+ min_integer_part = currentInt;
+ }
+ if (currentInt > max_integer_part) {
+ max_integer_part = currentInt;
+ }
+
+ // 提取小数部分
+ long bits = Double.doubleToLongBits(value);
+ long exponent = (bits >> 52) & 0x7FF;
+ long mantissa = bits & (long) ((1L << 52) - 1);
+ long actualExponent = exponent - 1023;
+
+ if (actualExponent >= 0) {
+ long mask = (1L << (52 - actualExponent)) - 1;
+ mantissa &= mask;
+ } else {
+ mantissa += 1L << 52;
+ }
+
+ long shift = 52 - actualExponent - bits_needed[max_decimal];
+ if (shift < 0) {
+ mantissa <<= -shift;
+ } else {
+ mantissa >>= shift;
+ }
+
+ if (exponent == 0) {
+ mantissa = 0;
+ }
+
+ decimal_parts[i] = mantissa;
+ }
+
+ // 写入最小整数部分(8字节)
+ long2Bytes(min_integer_part, encode_pos, encoded_result);
+ encode_pos += 8;
+
+ // 计算整数部分所需的位数并写入
+ int bw = bitWidth(max_integer_part - min_integer_part);
+ encoded_result[encode_pos] = (byte) bw;
+ encode_pos += 1;
+
+ // 对整数部分进行差分编码
+ for (int i = 0; i < remainder; i++) {
+ integer_parts[i] -= min_integer_part;
+ }
+
+ // 计算总位宽
+ int totalBitWidth = 1 + bw + bits_needed[max_decimal];
+
+ // 为每个值分配一个长整数数组来存储组合位
+ long[] combinedValues = new long[remainder];
+
+ // 将符号位、整数部分和小数部分组合成一个长整数
+ for (int i = 0; i < remainder; i++) {
+ long combined = (sign_bits[i] << (bw + bits_needed[max_decimal]))
+ | (integer_parts[i] << bits_needed[max_decimal]) | decimal_parts[i];
+ combinedValues[i] = combined;
+ }
+
+ // 使用逐位打包方式编码组合值
+ int bitPos = encode_pos * 8; // 转换为位位置
+ encode_pos = bitPacking(combinedValues, totalBitWidth, bitPos, encoded_result, remainder);
+
+ return encode_pos;
+ }
+
+ // 块解码器
+ public static int BlockDecoder(byte[] encoded_result, int block_index, int block_size, int remainder,
+ int max_decimal, int encode_pos, double[] data) {
+
+ // 读取最小整数部分
+ long min_integer_part = bytes2Long(encoded_result, encode_pos, 8);
+ encode_pos += 8;
+
+ // 读取整数部分位宽
+ int bw = encoded_result[encode_pos] & 0xFF;
+ encode_pos += 1;
+
+ // 计算总位宽
+ int totalBitWidth = 1 + bw + bits_needed[max_decimal];
+
+ // 解码组合值
+ long[] combinedValues = new long[remainder];
+ int bitPos = encode_pos * 8; // 转换为位位置
+ encode_pos = decodeBitPacking(encoded_result, bitPos, totalBitWidth, remainder, combinedValues);
+
+ // 解码每个值
+ for (int i = 0; i < remainder; i++) {
+ long combined = combinedValues[i];
+
+ // 提取符号位、整数部分和小数部分
+ long sign_bit = (combined >> (bw + bits_needed[max_decimal])) & 1L;
+ long integer_part = (combined >> bits_needed[max_decimal]) & ((1L << bw) - 1);
+ long decimal_part = combined & ((1L << bits_needed[max_decimal]) - 1);
+
+ // 恢复原始整数部分
+ integer_part += min_integer_part;
+
+ // 将小数部分转换为double
+ double decimal = decimal_part;
+ for (int j = 0; j < bits_needed[max_decimal]; j++) {
+ decimal /= 2;
+ }
+
+ // 组合成最终的双精度值
+ double value = integer_part + decimal;
+ value = sign_bit == 1 ? -value : value;
+ data[block_index * block_size + i] = value;
+ }
+
+ return encode_pos;
+ }
+
+ // 主编码器
+ public static int Encoder(double[] data, int block_size, int max_decimal, byte[] encoded_result) {
+ int data_length = data.length;
+ int encode_pos = 0;
+
+ // 写入数据长度(4字节)
+ encoded_result[0] = (byte) (data_length >> 24);
+ encoded_result[1] = (byte) (data_length >> 16);
+ encoded_result[2] = (byte) (data_length >> 8);
+ encoded_result[3] = (byte) data_length;
+ encode_pos += 4;
+
+ // 写入块大小(4字节)
+ encoded_result[4] = (byte) (block_size >> 24);
+ encoded_result[5] = (byte) (block_size >> 16);
+ encoded_result[6] = (byte) (block_size >> 8);
+ encoded_result[7] = (byte) block_size;
+ encode_pos += 4;
+
+ // 写入最大小数位数(1字节)
+ encoded_result[8] = (byte) max_decimal;
+ encode_pos += 1;
+
+ // 计算块数
+ int num_blocks = data_length / block_size;
+ int remainder = data_length % block_size;
+
+ // 编码完整块
+ for (int i = 0; i < num_blocks; i++) {
+ encode_pos = BlockEncoder(data, i, block_size, block_size, max_decimal, encode_pos, encoded_result);
+ }
+
+ // 编码剩余部分
+ if (remainder > 0) {
+ encode_pos = BlockEncoder(data, num_blocks, block_size, remainder, max_decimal, encode_pos, encoded_result);
+ }
+
+ return encode_pos;
+ }
+
+ // 主解码器
+ public static double[] Decoder(byte[] encoded_result) {
+ int encode_pos = 0;
+
+ // 读取数据长度
+ int data_length = ((encoded_result[encode_pos] & 0xFF) << 24) |
+ ((encoded_result[encode_pos + 1] & 0xFF) << 16) |
+ ((encoded_result[encode_pos + 2] & 0xFF) << 8) |
+ (encoded_result[encode_pos + 3] & 0xFF);
+ encode_pos += 4;
+
+ // 读取块大小
+ int block_size = ((encoded_result[encode_pos] & 0xFF) << 24) |
+ ((encoded_result[encode_pos + 1] & 0xFF) << 16) |
+ ((encoded_result[encode_pos + 2] & 0xFF) << 8) |
+ (encoded_result[encode_pos + 3] & 0xFF);
+ encode_pos += 4;
+
+ // 读取最大小数位数
+ int max_decimal = encoded_result[encode_pos] & 0xFF;
+ encode_pos += 1;
+
+ // 计算块数
+ int num_blocks = data_length / block_size;
+ int remainder = data_length % block_size;
+
+ // 分配结果数组
+ double[] data = new double[data_length];
+
+ // 解码完整块
+ for (int i = 0; i < num_blocks; i++) {
+ encode_pos = BlockDecoder(encoded_result, i, block_size, block_size, max_decimal, encode_pos, data);
+ }
+
+ // 解码剩余部分
+ if (remainder > 0) {
+ encode_pos = BlockDecoder(encoded_result, num_blocks, block_size, remainder, max_decimal, encode_pos, data);
+ }
+
+ return data;
+ }
+
+ public static int getDecimalPrecision(String str) {
+ int decimalIndex = str.indexOf(".");
+
+ if (decimalIndex == -1) {
+ return 0;
+ }
+
+ return str.substring(decimalIndex + 1).length();
+ }
+
+ public static String extractFileName(String path) {
+ if (path == null || path.isEmpty()) {
+ return "";
+ }
+
+ File file = new File(path);
+ String fileName = file.getName();
+
+ int dotIndex = fileName.lastIndexOf('.');
+
+ if (dotIndex == -1 || dotIndex == 0) {
+ return fileName;
+ }
+
+ return fileName.substring(0, dotIndex);
+ }
+
+ @Test
+ public void test0() throws IOException {
+ // String parent_dir = "path/to/your/directory/";
+ String parent_dir = "D:/encoding-subcolumn/";
+
+ // String input_parent_dir = parent_dir + "dataset/";
+ String input_parent_dir = parent_dir + "ElfTestData_camel/";
+
+ String output_parent_dir = parent_dir + "result/";
+
+ // String outputPath = output_parent_dir + "buff_long0.csv";
+ String outputPath = output_parent_dir + "buff_long_repeat50.csv";
+
+ int block_size = 1024;
+
+ int repeatTime = 50;
+
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+
+ String[] head = {
+ "Dataset",
+ "Encoding Algorithm",
+ "Encoding Time",
+ "Decoding Time",
+ "Points",
+ "Compressed Size",
+ "Compression Ratio"
+ };
+ writer.writeRecord(head);
+
+ File directory = new File(input_parent_dir);
+ File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
+
+ for (File file : csvFiles) {
+ String datasetName = extractFileName(file.toString());
+ System.out.println(datasetName);
+
+ InputStream inputStream = Files.newInputStream(file.toPath());
+
+ CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ ArrayList<Double> data1 = new ArrayList<>();
+
+ int max_decimal = 0;
+ while (loader.readRecord()) {
+ String f_str = loader.getValues()[0];
+ if (f_str.isEmpty()) {
+ continue;
+ }
+ int cur_decimal = getDecimalPrecision(f_str);
+ if (cur_decimal > max_decimal)
+ max_decimal = cur_decimal;
+ data1.add(Double.valueOf(f_str));
+ }
+ inputStream.close();
+ double[] data2_arr = new double[data1.size()];
+
+ for (int i = 0; i < data1.size(); i++) {
+ data2_arr[i] = data1.get(i);
+ }
+
+ System.out.println(max_decimal);
+
+ if (max_decimal > 17) {
+ max_decimal = 17;
+ }
+
+ byte[] encoded_result = new byte[data2_arr.length * 8];
+
+ long encodeTime = 0;
+ long decodeTime = 0;
+ double ratio = 0;
+ double compressed_size = 0;
+
+ int length = 0;
+
+ long s = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ length = Encoder(data2_arr, block_size, max_decimal, encoded_result);
+ }
+
+ long e = System.nanoTime();
+ encodeTime += ((e - s) / repeatTime);
+ compressed_size += length;
+
+ double ratioTmp;
+
+ ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
+
+ ratio += ratioTmp;
+
+ System.out.println("Decode");
+
+ double[] data2_arr_decoded = new double[data1.size()];
+
+ s = System.nanoTime();
+
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ data2_arr_decoded = Decoder(encoded_result);
+ }
+
+ e = System.nanoTime();
+ decodeTime += ((e - s) / repeatTime);
+
+ String[] record = {
+ datasetName,
+ "BUFF",
+ String.valueOf(encodeTime),
+ String.valueOf(decodeTime),
+ String.valueOf(data1.size()),
+ String.valueOf(compressed_size),
+ String.valueOf(ratio)
+ };
+ writer.writeRecord(record);
+ System.out.println(ratio);
+ }
+
+ writer.close();
+ }
+
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFDoubleTest.java
similarity index 51%
copy from iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFTest.java
copy to iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFDoubleTest.java
index 57ad31daad0..1f8ea14f1cf 100644
--- a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFTest.java
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFDoubleTest.java
@@ -16,12 +16,16 @@ import org.junit.Test;
import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
-public class BUFFTest {
+public class BUFFDoubleTest {
public static int bitWidth(int value) {
return 32 - Integer.numberOfLeadingZeros(value);
}
+ public static int bitWidth(long value) {
+ return 64 - Long.numberOfLeadingZeros(value);
+ }
+
public static void intToBytes(int srcNum, byte[] result, int pos, int width) {
int cnt = pos & 0x07;
int index = pos >> 3;
@@ -62,20 +66,57 @@ public class BUFFTest {
return ret;
}
+ public static void longToBytes(long srcNum, byte[] result, int pos, int width) {
+ int cnt = pos & 0x07;
+ int index = pos >> 3;
+
+ while (width > 0) {
+ int m = width + cnt >= 8 ? 8 - cnt : width;
+ width -= m;
+ int mask = 1 << (8 - cnt);
+ cnt += m;
+ byte y = (byte) (srcNum >>> width);
+ y = (byte) (y << (8 - cnt));
+ mask = ~(mask - (1 << (8 - cnt)));
+ result[index] = (byte) (result[index] & mask | y);
+ srcNum = srcNum & ~(-1L << width);
+ if (cnt == 8) {
+ index++;
+ cnt = 0;
+ }
+ }
+ }
+
+ public static long bytesToLong(byte[] result, int pos, int width) {
+ long ret = 0;
+ int cnt = pos & 0x07;
+ int index = pos >> 3;
+ while (width > 0) {
+ int m = width + cnt >= 8 ? 8 - cnt : width;
+ width -= m;
+ ret = ret << m;
+ byte y = (byte) (result[index] & (0xff >> cnt));
+ y = (byte) ((y & 0xff) >>> (8 - cnt - m));
+ ret = ret | (y & 0xff);
+ cnt += m;
+ if (cnt == 8) {
+ cnt = 0;
+ index++;
+ }
+ }
+ return ret;
+ }
+
public static void pack8Values(int[] values, int offset, int width, int encode_pos,
byte[] encoded_result) {
int bufIdx = 0;
int valueIdx = offset;
- // remaining bits for the current unfinished Integer
int leftBit = 0;
while (valueIdx < 8 + offset) {
- // buffer is used for saving 32 bits as a part of result
int buffer = 0;
- // remaining size of bits in the 'buffer'
int leftSize = 32;
- // encode the left bits of current Integer to 'buffer'
if (leftBit > 0) {
buffer |= (values[valueIdx] << (32 - leftBit));
leftSize -= leftBit;
@@ -84,20 +125,16 @@ public class BUFFTest {
}
while (leftSize >= width && valueIdx < 8 + offset) {
- // encode one Integer to the 'buffer'
buffer |= (values[valueIdx] << (leftSize - width));
leftSize -= width;
valueIdx++;
}
- // If the remaining space of the buffer can not save the bits for one Integer,
+
if (leftSize > 0 && valueIdx < 8 + offset) {
- // put the first 'leftSize' bits of the Integer into remaining space of the
- // buffer
buffer |= (values[valueIdx] >>> (width - leftSize));
leftBit = width - leftSize;
}
- // put the buffer into the final result
for (int j = 0; j < 4; j++) {
encoded_result[encode_pos] = (byte) ((buffer >>> ((3 - j) * 8)) & 0xFF);
encode_pos++;
@@ -110,28 +147,58 @@ public class BUFFTest {
}
+ public static void pack8Values(
+ long[] values, int offset, int width, int encode_pos, byte[] encoded_result) {
+ int bufIdx = 0;
+ int valueIdx = offset;
+ int leftBit = 0;
+
+ while (valueIdx < 8 + offset) {
+ long buffer = 0;
+ int leftSize = 64;
+
+ if (leftBit > 0) {
+ buffer |= (values[valueIdx] << (64 - leftBit));
+ leftSize -= leftBit;
+ leftBit = 0;
+ valueIdx++;
+ }
+
+ while (leftSize >= width && valueIdx < 8 + offset) {
+ buffer |= (values[valueIdx] << (leftSize - width));
+ leftSize -= width;
+ valueIdx++;
+ }
+ if (leftSize > 0 && valueIdx < 8 + offset) {
+ buffer |= (values[valueIdx] >>> (width - leftSize));
+ leftBit = width - leftSize;
+ }
+
+ for (int j = 0; j < 8; j++) {
+ encoded_result[encode_pos] = (byte) ((buffer >>> ((8 - j - 1) * 8)) & 0xFF);
+ encode_pos++;
+ bufIdx++;
+ if (bufIdx >= width * 8 / 8) {
+ return;
+ }
+ }
+ }
+ }
+
public static void unpack8Values(byte[] encoded, int offset, int width, int[] result_list, int result_offset) {
int byteIdx = offset;
long buffer = 0;
- // total bits which have read from 'buf' to 'buffer'. i.e.,
- // number of available bits to be decoded.
int totalBits = 0;
int valueIdx = 0;
while (valueIdx < 8) {
- // If current available bits are not enough to decode one Integer,
- // then add next byte from buf to 'buffer' until totalBits >= width
while (totalBits < width) {
buffer = (buffer << 8) | (encoded[byteIdx] & 0xFF);
byteIdx++;
totalBits += 8;
}
- // If current available bits are enough to decode one Integer,
- // then decode one Integer one by one until left bits in 'buffer' is
- // not enough to decode one Integer.
while (totalBits >= width && valueIdx < 8) {
- // result_list.add((int) (buffer >>> (totalBits - width)));
result_list[result_offset + valueIdx] = (int) (buffer >>> (totalBits - width));
valueIdx++;
totalBits -= width;
@@ -140,6 +207,29 @@ public class BUFFTest {
}
}
+ public static void unpack8Values(
+ byte[] encoded, int offset, int width, long[] result_list, int result_offset) {
+ int byteIdx = offset;
+ long buffer = 0;
+ int totalBits = 0;
+ int valueIdx = 0;
+
+ while (valueIdx < 8) {
+ while (totalBits < width) {
+ buffer = (buffer << 8) | (encoded[byteIdx] & 0xFF);
+ byteIdx++;
+ totalBits += 8;
+ }
+
+ while (totalBits >= width && valueIdx < 8) {
+ result_list[result_offset + valueIdx] = buffer >>> (totalBits - width);
+ valueIdx++;
+ totalBits -= width;
+ buffer = buffer & ((1L << totalBits) - 1);
+ }
+ }
+ }
+
public static int bitPacking(int[] numbers, int bit_width, int encode_pos,
byte[] encoded_result, int num_values) {
int block_num = num_values / 8;
@@ -160,14 +250,32 @@ public class BUFFTest {
return (encode_pos + 7) / 8;
}
+ public static int bitPacking(long[] numbers, int bit_width, int encode_pos,
+ byte[] encoded_result, int num_values) {
+ int block_num = num_values / 8;
+ int remainder = num_values % 8;
+
+ for (int i = 0; i < block_num; i++) {
+ pack8Values(numbers, i * 8, bit_width, encode_pos, encoded_result);
+ encode_pos += bit_width;
+ }
+
+ encode_pos *= 8;
+
+ for (int i = 0; i < remainder; i++) {
+ longToBytes(numbers[block_num * 8 + i], encoded_result, encode_pos, bit_width);
+ encode_pos += bit_width;
+ }
+
+ return (encode_pos + 7) / 8;
+ }
+
public static int decodeBitPacking(
byte[] encoded, int decode_pos, int bit_width, int num_values, int[] result_list) {
- // ArrayList<Integer> result_list = new ArrayList<>();
- // int[] result_list = new int[num_values];
int block_num = num_values / 8;
int remainder = num_values % 8;
- for (int i = 0; i < block_num; i++) { // bitpacking
+ for (int i = 0; i < block_num; i++) {
unpack8Values(encoded, decode_pos, bit_width, result_list, i * 8);
decode_pos += bit_width;
}
@@ -182,31 +290,92 @@ public class BUFFTest {
return (decode_pos + 7) / 8;
}
- public static int[] bits_needed = { 0, 5, 8, 11, 15, 18, 21, 25 };
+ public static int decodeBitPacking(
+ byte[] encoded, int decode_pos, int bit_width, int num_values, long[] result_list) {
+ int block_num = num_values / 8;
+ int remainder = num_values % 8;
- public static int BlockEncoder(float[] data, int block_index, int block_size, int remainder, int max_decimal,
- int encode_pos, byte[] encoded_result) {
+ for (int i = 0; i < block_num; i++) {
+ unpack8Values(encoded, decode_pos, bit_width, result_list, i * 8);
+ decode_pos += bit_width;
+ }
+
+ decode_pos *= 8;
+
+ for (int i = 0; i < remainder; i++) {
+ result_list[block_num * 8 + i] = bytesToLong(encoded, decode_pos, bit_width);
+ decode_pos += bit_width;
+ }
+
+ return (decode_pos + 7) / 8;
+ }
+
+ public static void int2Bytes(int integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer >> 24);
+ cur_byte[encode_pos + 1] = (byte) (integer >> 16);
+ cur_byte[encode_pos + 2] = (byte) (integer >> 8);
+ cur_byte[encode_pos + 3] = (byte) (integer);
+ }
+
+ public static void intByte2Bytes(int integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer);
+ }
+
+ public static void long2Bytes(long integer, int encode_pos, byte[] cur_byte) {
+ cur_byte[encode_pos] = (byte) (integer >> 56);
+ cur_byte[encode_pos + 1] = (byte) (integer >> 48);
+ cur_byte[encode_pos + 2] = (byte) (integer >> 40);
+ cur_byte[encode_pos + 3] = (byte) (integer >> 32);
+ cur_byte[encode_pos + 4] = (byte) (integer >> 24);
+ cur_byte[encode_pos + 5] = (byte) (integer >> 16);
+ cur_byte[encode_pos + 6] = (byte) (integer >> 8);
+ cur_byte[encode_pos + 7] = (byte) (integer);
+ }
+
+ public static int bytes2Integer(byte[] encoded, int start, int num) {
+ int value = 0;
+
+ for (int i = 0; i < num; i++) {
+ value <<= 8;
+ int b = encoded[i + start] & 0xFF;
+ value |= b;
+ }
+ return value;
+ }
+
+ public static long bytes2Long(byte[] encoded, int start, int num) {
+ long value = 0;
- int[] sign_bits = new int[remainder];
- int[] integer_parts = new int[remainder];
- int[] decimal_parts = new int[remainder];
+ for (int i = 0; i < num; i++) {
+ value <<= 8;
+ int b = encoded[i + start] & 0xFF;
+ value |= b;
+ }
+ return value;
+ }
+
+ public static int[] bits_needed = { 0, 5, 8, 11, 15, 18, 21, 25, 28, 31, 35,
+ 38, 41, 45, 48, 51, 55, 58
+ };
+
+ public static int BlockEncoder(double[] data, int block_index, int block_size, int remainder, int max_decimal,
+ int encode_pos, byte[] encoded_result) {
- int min_integer_part = Integer.MAX_VALUE;
- int max_integer_part = Integer.MIN_VALUE;
+ long[] sign_bits = new long[remainder];
+ long[] integer_parts = new long[remainder];
+ long[] decimal_parts = new long[remainder];
- // for (int i = 0; i < remainder; i++) {
- // System.out.print(data[block_index * block_size + i] + " ");
- // }
- // System.out.println();
+ long min_integer_part = Long.MAX_VALUE;
+ long max_integer_part = Long.MIN_VALUE;
for (int i = 0; i < remainder; i++) {
- float value = data[block_index * block_size + i];
+ double value = data[block_index * block_size + i];
if (value < 0) {
sign_bits[i] = 1;
}
- int currentInt = (int) Math.abs(value);
+ long currentInt = (long) Math.abs(value);
integer_parts[i] = currentInt;
if (currentInt < min_integer_part) {
@@ -217,22 +386,21 @@ public class BUFFTest {
max_integer_part = currentInt;
}
- int bits = Float.floatToIntBits(value);
+ long bits = Double.doubleToLongBits(value);
- // int sign = (bits >> 31) & 1;
- int exponent = (bits >> 23) & 0xFF;
- int mantissa = bits & 0x7FFFFF;
+ long exponent = (bits >> 52) & 0x7FF;
+ long mantissa = bits & (long) ((1L << 52) - 1);
- int actualExponent = exponent - 127;
+ long actualExponent = exponent - 1023;
if (actualExponent >= 0) {
- int mask = (1 << (23 - actualExponent)) - 1;
+ long mask = (1L << (52 - actualExponent)) - 1;
mantissa &= mask;
} else {
- mantissa += 1 << 23;
+ mantissa += 1L << 52;
}
- int shift = 23 - actualExponent - bits_needed[max_decimal];
+ long shift = 52 - actualExponent - bits_needed[max_decimal];
if (shift < 0) {
mantissa <<= -shift;
@@ -247,14 +415,8 @@ public class BUFFTest {
decimal_parts[i] = mantissa;
}
- encoded_result[encode_pos] = (byte) (min_integer_part >> 24);
- encoded_result[encode_pos + 1] = (byte) (min_integer_part >> 16);
- encoded_result[encode_pos + 2] = (byte) (min_integer_part >> 8);
- encoded_result[encode_pos + 3] = (byte) min_integer_part;
- encode_pos += 4;
-
- // System.out.println("min_integer_part: " + min_integer_part);
- // System.out.println("max_integer_part: " + max_integer_part);
+ long2Bytes(min_integer_part, encode_pos, encoded_result);
+ encode_pos += 8;
int bw = bitWidth(max_integer_part - min_integer_part);
@@ -265,28 +427,17 @@ public class BUFFTest {
integer_parts[i] -= min_integer_part;
}
- // int[] combined = new int[remainder];
- // for (int i = 0; i < remainder; i++) {
- // combined[i] = (sign_bits[i] << (bw + bits_needed[max_decimal])) |
- // (integer_parts[i] << bits_needed[max_decimal]) | decimal_parts[i];
- // }
-
- // int totalBitWidth = 1 + bw + bits_needed[max_decimal];
-
- // encode_pos = bitPacking(combined, totalBitWidth, encode_pos, encoded_result,
- // remainder);
-
int totalBitWidth = 1 + bw + bits_needed[max_decimal];
int intArrayCount = (totalBitWidth + 7) / 8;
- int[][] combinedArrays = new int[intArrayCount][remainder];
+ long[][] combinedArrays = new long[intArrayCount][remainder];
for (int i = 0; i < intArrayCount; i++) {
for (int j = 0; j < remainder; j++) {
long combined = (sign_bits[j] << (bw + bits_needed[max_decimal]))
| (integer_parts[j] << bits_needed[max_decimal]) | decimal_parts[j];
- combinedArrays[i][j] = (int) ((combined >> (i * 8)) & 0xFF);
+ combinedArrays[i][j] = ((combined >> (i * 8)) & 0xFF);
}
}
@@ -299,41 +450,23 @@ public class BUFFTest {
}
public static int BlockDecoder(byte[] encoded_result, int block_index, int block_size, int remainder,
- int max_decimal, int encode_pos, float[] data) {
+ int max_decimal, int encode_pos, double[] data) {
- int[] sign_bits = new int[remainder];
- int[] integer_parts = new int[remainder];
- int[] decimal_parts = new int[remainder];
+ long[] sign_bits = new long[remainder];
+ long[] integer_parts = new long[remainder];
+ long[] decimal_parts = new long[remainder];
- int min_integer_part = ((encoded_result[encode_pos] & 0xFF) << 24)
- | ((encoded_result[encode_pos + 1] & 0xFF) << 16)
- |
- ((encoded_result[encode_pos + 2] & 0xFF) << 8) | (encoded_result[encode_pos + 3] & 0xFF);
- encode_pos += 4;
-
- // System.out.println("min_integer_part: " + min_integer_part);
+ long min_integer_part = bytes2Long(encoded_result, encode_pos, 8);
+ encode_pos += 8;
int bw = encoded_result[encode_pos];
encode_pos += 1;
- // int[] combined = new int[remainder];
-
- // encode_pos = decodeBitPacking(encoded_result, encode_pos, 1 + bw +
- // bits_needed[max_decimal], remainder, combined);
-
- // for (int i = 0; i < remainder; i++) {
- // int value = combined[i];
- // sign_bits[i] = (value >> (bw + bits_needed[max_decimal])) & 1;
- // integer_parts[i] = (value >> bits_needed[max_decimal]) & ((1 << bw) - 1);
- // integer_parts[i] += min_integer_part;
- // decimal_parts[i] = value & ((1 << bits_needed[max_decimal]) - 1);
- // }
-
int totalBitWidth = 1 + bw + bits_needed[max_decimal];
int intArrayCount = (totalBitWidth + 7) / 8;
- int[][] combinedArrays = new int[intArrayCount][remainder];
+ long[][] combinedArrays = new long[intArrayCount][remainder];
long[] combined = new long[remainder];
@@ -341,36 +474,31 @@ public class BUFFTest {
int currentBitWidth = Math.min(8, totalBitWidth - i * 8);
encode_pos = decodeBitPacking(encoded_result, encode_pos, currentBitWidth, remainder, combinedArrays[i]);
for (int j = 0; j < remainder; j++) {
- combined[j] |= ((long) combinedArrays[i][j]) << (i * 8);
+ combined[j] |= (combinedArrays[i][j]) << (i * 8);
}
}
for (int i = 0; i < remainder; i++) {
- sign_bits[i] = (int) ((combined[i] >> (bw + bits_needed[max_decimal])) & 1);
- integer_parts[i] = (int) ((combined[i] >> bits_needed[max_decimal]) & ((1 << bw) - 1));
+ sign_bits[i] = ((combined[i] >> (bw + bits_needed[max_decimal])) & 1);
+ integer_parts[i] = ((combined[i] >> bits_needed[max_decimal]) & ((1 << bw) - 1));
integer_parts[i] += min_integer_part;
- decimal_parts[i] = (int) (combined[i] & ((1 << bits_needed[max_decimal]) - 1));
+ decimal_parts[i] = (combined[i] & ((1 << bits_needed[max_decimal]) - 1));
}
for (int i = 0; i < remainder; i++) {
- float decimal = decimal_parts[i];
+ double decimal = decimal_parts[i];
for (int j = 0; j < bits_needed[max_decimal]; j++) {
decimal /= 2;
}
- float value = (float) (integer_parts[i] + decimal);
+ double value = (integer_parts[i] + decimal);
value = sign_bits[i] == 1 ? -value : value;
data[block_index * block_size + i] = value;
}
- // for (int i = 0; i < remainder; i++) {
- // System.out.print(data[block_index * block_size + i] + " ");
- // }
- // System.out.println();
-
return encode_pos;
}
- public static int Encoder(float[] data, int block_size, int max_decimal, byte[] encoded_result) {
+ public static int Encoder(double[] data, int block_size, int max_decimal, byte[] encoded_result) {
int data_length = data.length;
int encode_pos = 0;
@@ -401,25 +529,10 @@ public class BUFFTest {
encode_pos = BlockEncoder(data, num_blocks, block_size, remainder, max_decimal, encode_pos, encoded_result);
}
- // if (remainder <= 3) {
- // for (int i = 0; i < remainder; i++) {
- // int value = data[num_blocks * block_size + i];
- // encoded_result[encode_pos] = (byte) (value >> 24);
- // encoded_result[encode_pos + 1] = (byte) (value >> 16);
- // encoded_result[encode_pos + 2] = (byte) (value >> 8);
- // encoded_result[encode_pos + 3] = (byte) value;
- // encode_pos += 4;
- // }
- // } else {
- // encode_pos = BlockEncoder(data, num_blocks, block_size, remainder,
- // max_decimal, encode_pos,
- // encoded_result);
- // }
-
return encode_pos;
}
- public static float[] Decoder(byte[] encoded_result) {
+ public static double[] Decoder(byte[] encoded_result) {
int encode_pos = 0;
int data_length = ((encoded_result[encode_pos] & 0xFF) << 24) | ((encoded_result[encode_pos + 1] & 0xFF) << 16)
@@ -438,7 +551,7 @@ public class BUFFTest {
int remainder = data_length % block_size;
- float[] data = new float[data_length];
+ double[] data = new double[data_length];
for (int i = 0; i < num_blocks; i++) {
encode_pos = BlockDecoder(encoded_result, i, block_size, block_size, max_decimal, encode_pos, data);
@@ -448,33 +561,16 @@ public class BUFFTest {
encode_pos = BlockDecoder(encoded_result, num_blocks, block_size, remainder, max_decimal, encode_pos, data);
}
- // if (remainder <= 3) {
- // for (int i = 0; i < remainder; i++) {
- // data[num_blocks * block_size + i] = ((encoded_result[encode_pos] & 0xFF) <<
- // 24) |
- // ((encoded_result[encode_pos + 1] & 0xFF) << 16) |
- // ((encoded_result[encode_pos + 2] & 0xFF) << 8) | (encoded_result[encode_pos +
- // 3] & 0xFF);
- // encode_pos += 4;
- // }
- // } else {
- // encode_pos = BlockDecoder(encoded_result, num_blocks, block_size, remainder,
- // encode_pos, data);
- // }
-
return data;
}
public static int getDecimalPrecision(String str) {
- // 查找小数点的位置
int decimalIndex = str.indexOf(".");
- // 如果没有小数点,精度为0
if (decimalIndex == -1) {
return 0;
}
- // 获取小数点后的部分并返回其长度
return str.substring(decimalIndex + 1).length();
}
@@ -496,24 +592,21 @@ public class BUFFTest {
}
@Test
- public void testSubcolumn() throws IOException {
- String parent_dir = "D:/github/xjz17/subcolumn/";
+ public void test0() throws IOException {
+ // String parent_dir = "path/to/your/directory/";
+ String parent_dir = "D:/encoding-subcolumn/";
- String input_parent_dir = parent_dir + "dataset/";
+ // String input_parent_dir = parent_dir + "dataset/";
+ String input_parent_dir = parent_dir + "ElfTestData_camel/";
- String output_parent_dir = "D:/encoding-subcolumn/result/";
- // String output_parent_dir = parent_dir + "result/";
+ String output_parent_dir = parent_dir + "result/";
- String outputPath = output_parent_dir + "buff.csv";
+ // String outputPath = output_parent_dir + "buff_long0.csv";
+ String outputPath = output_parent_dir + "buff_long2_repeat50.csv";
int block_size = 1024;
- int repeatTime = 100;
-
- // repeatTime = 1;
-
- List<String> integerDatasets = new ArrayList<>();
- integerDatasets.add("Wine-Tasting");
+ int repeatTime = 50;
CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
writer.setRecordDelimiter('\n');
@@ -530,7 +623,6 @@ public class BUFFTest {
writer.writeRecord(head);
File directory = new File(input_parent_dir);
- // File[] csvFiles = directory.listFiles();
File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
for (File file : csvFiles) {
@@ -540,7 +632,7 @@ public class BUFFTest {
InputStream inputStream = Files.newInputStream(file.toPath());
CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
- ArrayList<Float> data1 = new ArrayList<>();
+ ArrayList<Double> data1 = new ArrayList<>();
int max_decimal = 0;
while (loader.readRecord()) {
@@ -551,20 +643,22 @@ public class BUFFTest {
int cur_decimal = getDecimalPrecision(f_str);
if (cur_decimal > max_decimal)
max_decimal = cur_decimal;
- data1.add(Float.valueOf(f_str));
+ data1.add(Double.valueOf(f_str));
}
inputStream.close();
- // int[] data2_arr = new int[data1.size()];
- float[] data2_arr = new float[data1.size()];
+ double[] data2_arr = new double[data1.size()];
- // int max_mul = (int) Math.pow(10, max_decimal);
for (int i = 0; i < data1.size(); i++) {
- // data2_arr[i] = (int) (data1.get(i) * max_mul);
data2_arr[i] = data1.get(i);
}
System.out.println(max_decimal);
- byte[] encoded_result = new byte[data2_arr.length * 4];
+
+ if (max_decimal > 17) {
+ max_decimal = 17;
+ }
+
+ byte[] encoded_result = new byte[data2_arr.length * 8];
long encodeTime = 0;
long decodeTime = 0;
@@ -584,17 +678,13 @@ public class BUFFTest {
double ratioTmp;
- if (integerDatasets.contains(datasetName)) {
- ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- } else {
- ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
- }
+ ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
ratio += ratioTmp;
System.out.println("Decode");
- float[] data2_arr_decoded = new float[data1.size()];
+ double[] data2_arr_decoded = new double[data1.size()];
s = System.nanoTime();
@@ -621,135 +711,4 @@ public class BUFFTest {
writer.close();
}
- @Test
- public void testTransData() throws IOException {
- String parent_dir = "D:/github/xjz17/subcolumn/";
-
- String output_parent_dir = "D:/encoding-subcolumn/trans_data_result/";
- // String output_parent_dir = parent_dir + "trans_data_result/";
-
- String input_parent_dir = parent_dir + "trans_data/";
-
- ArrayList<String> input_path_list = new ArrayList<>();
- ArrayList<String> output_path_list = new ArrayList<>();
- ArrayList<String> dataset_name = new ArrayList<>();
- ArrayList<Integer> dataset_block_size = new ArrayList<>();
-
- try (Stream<Path> paths = Files.walk(Paths.get(input_parent_dir))) {
- paths.filter(Files::isDirectory)
- .filter(path -> !path.equals(Paths.get(input_parent_dir)))
- .forEach(dir -> {
- String name = dir.getFileName().toString();
- dataset_name.add(name);
- input_path_list.add(dir.toString());
- dataset_block_size.add(1024);
- });
- }
-
- String outputPath = output_parent_dir + "buff.csv";
- CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
- writer.setRecordDelimiter('\n');
-
- String[] head = {
- "Dataset",
- "Encoding Algorithm",
- "Encoding Time",
- "Decoding Time",
- "Points",
- "Compressed Size",
- "Compression Ratio"
- };
- writer.writeRecord(head);
-
- int repeatTime = 100;
-
- for (int file_i = 0; file_i < input_path_list.size(); file_i++) {
-
- String inputPath = input_path_list.get(file_i);
- System.out.println(inputPath);
-
- File file = new File(inputPath);
- File[] tempList = file.listFiles();
-
- long totalEncodeTime = 0;
- long totalDecodeTime = 0;
- double totalCompressedSize = 0;
- int totalPoints = 0;
-
- for (File f : tempList) {
- String datasetName = extractFileName(f.toString());
- InputStream inputStream = Files.newInputStream(f.toPath());
-
- CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
- ArrayList<Integer> data1 = new ArrayList<>();
- ArrayList<Integer> data2 = new ArrayList<>();
-
- int max_decimal = 0;
- loader.readHeaders();
- while (loader.readRecord()) {
- // String value = loader.getValues()[index];
- data1.add(Integer.valueOf(loader.getValues()[0]));
- data2.add(Integer.valueOf(loader.getValues()[1]));
- int cur_decimal = getDecimalPrecision(loader.getValues()[1]);
- max_decimal = Math.max(max_decimal, cur_decimal);
- // data.add(Integer.valueOf(value));
- }
- inputStream.close();
- float[] data2_arr = new float[data1.size()];
- for (int i = 0; i < data2.size(); i++) {
- data2_arr[i] = data2.get(i);
- }
- byte[] encoded_result = new byte[data2_arr.length * 4];
- long encodeTime = 0;
- long decodeTime = 0;
- double ratio = 0;
- double compressed_size = 0;
-
- int length = 0;
-
- long s = System.nanoTime();
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- length = Encoder(data2_arr, dataset_block_size.get(file_i), max_decimal, encoded_result);
- }
-
- long e = System.nanoTime();
- encodeTime += ((e - s) / repeatTime);
- compressed_size += length;
- double ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- ratio += ratioTmp;
- s = System.nanoTime();
-
- float[] data2_arr_decoded = new float[data1.size()];
-
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- data2_arr_decoded = Decoder(encoded_result);
- }
-
- e = System.nanoTime();
- decodeTime += ((e - s) / repeatTime);
-
- totalEncodeTime += encodeTime;
- totalDecodeTime += decodeTime;
- totalCompressedSize += compressed_size;
- totalPoints += data1.size();
-
- }
-
- double compressionRatio = totalCompressedSize / (totalPoints * Integer.BYTES);
-
- String[] record = {
- dataset_name.get(file_i),
- "BUFF",
- String.valueOf(totalEncodeTime),
- String.valueOf(totalDecodeTime),
- String.valueOf(totalPoints),
- String.valueOf(totalCompressedSize),
- String.valueOf(compressionRatio)
- };
-
- writer.writeRecord(record);
- System.out.println(compressionRatio);
- }
- writer.close();
- }
}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFTest.java
index 57ad31daad0..908eec8cdb4 100644
--- a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFTest.java
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BUFFTest.java
@@ -66,16 +66,12 @@ public class BUFFTest {
byte[] encoded_result) {
int bufIdx = 0;
int valueIdx = offset;
- // remaining bits for the current unfinished Integer
int leftBit = 0;
while (valueIdx < 8 + offset) {
- // buffer is used for saving 32 bits as a part of result
int buffer = 0;
- // remaining size of bits in the 'buffer'
int leftSize = 32;
- // encode the left bits of current Integer to 'buffer'
if (leftBit > 0) {
buffer |= (values[valueIdx] << (32 - leftBit));
leftSize -= leftBit;
@@ -84,20 +80,16 @@ public class BUFFTest {
}
while (leftSize >= width && valueIdx < 8 + offset) {
- // encode one Integer to the 'buffer'
buffer |= (values[valueIdx] << (leftSize - width));
leftSize -= width;
valueIdx++;
}
- // If the remaining space of the buffer can not save the bits for one Integer,
+
if (leftSize > 0 && valueIdx < 8 + offset) {
- // put the first 'leftSize' bits of the Integer into remaining space of the
- // buffer
buffer |= (values[valueIdx] >>> (width - leftSize));
leftBit = width - leftSize;
}
- // put the buffer into the final result
for (int j = 0; j < 4; j++) {
encoded_result[encode_pos] = (byte) ((buffer >>> ((3 - j) * 8)) & 0xFF);
encode_pos++;
@@ -113,25 +105,17 @@ public class BUFFTest {
public static void unpack8Values(byte[] encoded, int offset, int width, int[] result_list, int result_offset) {
int byteIdx = offset;
long buffer = 0;
- // total bits which have read from 'buf' to 'buffer'. i.e.,
- // number of available bits to be decoded.
int totalBits = 0;
int valueIdx = 0;
while (valueIdx < 8) {
- // If current available bits are not enough to decode one Integer,
- // then add next byte from buf to 'buffer' until totalBits >= width
while (totalBits < width) {
buffer = (buffer << 8) | (encoded[byteIdx] & 0xFF);
byteIdx++;
totalBits += 8;
}
- // If current available bits are enough to decode one Integer,
- // then decode one Integer one by one until left bits in 'buffer' is
- // not enough to decode one Integer.
while (totalBits >= width && valueIdx < 8) {
- // result_list.add((int) (buffer >>> (totalBits - width)));
result_list[result_offset + valueIdx] = (int) (buffer >>> (totalBits - width));
valueIdx++;
totalBits -= width;
@@ -162,12 +146,10 @@ public class BUFFTest {
public static int decodeBitPacking(
byte[] encoded, int decode_pos, int bit_width, int num_values, int[] result_list) {
- // ArrayList<Integer> result_list = new ArrayList<>();
- // int[] result_list = new int[num_values];
int block_num = num_values / 8;
int remainder = num_values % 8;
- for (int i = 0; i < block_num; i++) { // bitpacking
+ for (int i = 0; i < block_num; i++) {
unpack8Values(encoded, decode_pos, bit_width, result_list, i * 8);
decode_pos += bit_width;
}
@@ -182,7 +164,9 @@ public class BUFFTest {
return (decode_pos + 7) / 8;
}
- public static int[] bits_needed = { 0, 5, 8, 11, 15, 18, 21, 25 };
+ public static int[] bits_needed = { 0, 5, 8, 11, 15, 18, 21, 25, 28, 31, 35,
+ 38, 41, 45, 48, 51, 55, 58
+ };
public static int BlockEncoder(float[] data, int block_index, int block_size, int remainder, int max_decimal,
int encode_pos, byte[] encoded_result) {
@@ -194,11 +178,6 @@ public class BUFFTest {
int min_integer_part = Integer.MAX_VALUE;
int max_integer_part = Integer.MIN_VALUE;
- // for (int i = 0; i < remainder; i++) {
- // System.out.print(data[block_index * block_size + i] + " ");
- // }
- // System.out.println();
-
for (int i = 0; i < remainder; i++) {
float value = data[block_index * block_size + i];
@@ -253,9 +232,6 @@ public class BUFFTest {
encoded_result[encode_pos + 3] = (byte) min_integer_part;
encode_pos += 4;
- // System.out.println("min_integer_part: " + min_integer_part);
- // System.out.println("max_integer_part: " + max_integer_part);
-
int bw = bitWidth(max_integer_part - min_integer_part);
encoded_result[encode_pos] = (byte) bw;
@@ -265,17 +241,6 @@ public class BUFFTest {
integer_parts[i] -= min_integer_part;
}
- // int[] combined = new int[remainder];
- // for (int i = 0; i < remainder; i++) {
- // combined[i] = (sign_bits[i] << (bw + bits_needed[max_decimal])) |
- // (integer_parts[i] << bits_needed[max_decimal]) | decimal_parts[i];
- // }
-
- // int totalBitWidth = 1 + bw + bits_needed[max_decimal];
-
- // encode_pos = bitPacking(combined, totalBitWidth, encode_pos, encoded_result,
- // remainder);
-
int totalBitWidth = 1 + bw + bits_needed[max_decimal];
int intArrayCount = (totalBitWidth + 7) / 8;
@@ -311,24 +276,10 @@ public class BUFFTest {
((encoded_result[encode_pos + 2] & 0xFF) << 8) | (encoded_result[encode_pos + 3] & 0xFF);
encode_pos += 4;
- // System.out.println("min_integer_part: " + min_integer_part);
int bw = encoded_result[encode_pos];
encode_pos += 1;
- // int[] combined = new int[remainder];
-
- // encode_pos = decodeBitPacking(encoded_result, encode_pos, 1 + bw +
- // bits_needed[max_decimal], remainder, combined);
-
- // for (int i = 0; i < remainder; i++) {
- // int value = combined[i];
- // sign_bits[i] = (value >> (bw + bits_needed[max_decimal])) & 1;
- // integer_parts[i] = (value >> bits_needed[max_decimal]) & ((1 << bw) - 1);
- // integer_parts[i] += min_integer_part;
- // decimal_parts[i] = value & ((1 << bits_needed[max_decimal]) - 1);
- // }
-
int totalBitWidth = 1 + bw + bits_needed[max_decimal];
int intArrayCount = (totalBitWidth + 7) / 8;
@@ -362,11 +313,6 @@ public class BUFFTest {
data[block_index * block_size + i] = value;
}
- // for (int i = 0; i < remainder; i++) {
- // System.out.print(data[block_index * block_size + i] + " ");
- // }
- // System.out.println();
-
return encode_pos;
}
@@ -401,21 +347,6 @@ public class BUFFTest {
encode_pos = BlockEncoder(data, num_blocks, block_size, remainder, max_decimal, encode_pos, encoded_result);
}
- // if (remainder <= 3) {
- // for (int i = 0; i < remainder; i++) {
- // int value = data[num_blocks * block_size + i];
- // encoded_result[encode_pos] = (byte) (value >> 24);
- // encoded_result[encode_pos + 1] = (byte) (value >> 16);
- // encoded_result[encode_pos + 2] = (byte) (value >> 8);
- // encoded_result[encode_pos + 3] = (byte) value;
- // encode_pos += 4;
- // }
- // } else {
- // encode_pos = BlockEncoder(data, num_blocks, block_size, remainder,
- // max_decimal, encode_pos,
- // encoded_result);
- // }
-
return encode_pos;
}
@@ -448,33 +379,16 @@ public class BUFFTest {
encode_pos = BlockDecoder(encoded_result, num_blocks, block_size, remainder, max_decimal, encode_pos, data);
}
- // if (remainder <= 3) {
- // for (int i = 0; i < remainder; i++) {
- // data[num_blocks * block_size + i] = ((encoded_result[encode_pos] & 0xFF) <<
- // 24) |
- // ((encoded_result[encode_pos + 1] & 0xFF) << 16) |
- // ((encoded_result[encode_pos + 2] & 0xFF) << 8) | (encoded_result[encode_pos +
- // 3] & 0xFF);
- // encode_pos += 4;
- // }
- // } else {
- // encode_pos = BlockDecoder(encoded_result, num_blocks, block_size, remainder,
- // encode_pos, data);
- // }
-
return data;
}
public static int getDecimalPrecision(String str) {
- // 查找小数点的位置
int decimalIndex = str.indexOf(".");
- // 如果没有小数点,精度为0
if (decimalIndex == -1) {
return 0;
}
- // 获取小数点后的部分并返回其长度
return str.substring(decimalIndex + 1).length();
}
@@ -496,13 +410,12 @@ public class BUFFTest {
}
@Test
- public void testSubcolumn() throws IOException {
- String parent_dir = "D:/github/xjz17/subcolumn/";
+ public void test0() throws IOException {
+ String parent_dir = "path/to/your/directory/";
String input_parent_dir = parent_dir + "dataset/";
- String output_parent_dir = "D:/encoding-subcolumn/result/";
- // String output_parent_dir = parent_dir + "result/";
+ String output_parent_dir = parent_dir + "result/";
String outputPath = output_parent_dir + "buff.csv";
@@ -510,11 +423,6 @@ public class BUFFTest {
int repeatTime = 100;
- // repeatTime = 1;
-
- List<String> integerDatasets = new ArrayList<>();
- integerDatasets.add("Wine-Tasting");
-
CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
writer.setRecordDelimiter('\n');
@@ -530,7 +438,6 @@ public class BUFFTest {
writer.writeRecord(head);
File directory = new File(input_parent_dir);
- // File[] csvFiles = directory.listFiles();
File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
for (File file : csvFiles) {
@@ -553,18 +460,20 @@ public class BUFFTest {
max_decimal = cur_decimal;
data1.add(Float.valueOf(f_str));
}
+
+ if (max_decimal > 8) {
+ max_decimal = 8;
+ }
+
inputStream.close();
- // int[] data2_arr = new int[data1.size()];
float[] data2_arr = new float[data1.size()];
- // int max_mul = (int) Math.pow(10, max_decimal);
for (int i = 0; i < data1.size(); i++) {
- // data2_arr[i] = (int) (data1.get(i) * max_mul);
data2_arr[i] = data1.get(i);
}
System.out.println(max_decimal);
- byte[] encoded_result = new byte[data2_arr.length * 4];
+ byte[] encoded_result = new byte[data2_arr.length * 8];
long encodeTime = 0;
long decodeTime = 0;
@@ -584,11 +493,7 @@ public class BUFFTest {
double ratioTmp;
- if (integerDatasets.contains(datasetName)) {
- ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- } else {
- ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
- }
+ ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
ratio += ratioTmp;
@@ -621,135 +526,4 @@ public class BUFFTest {
writer.close();
}
- @Test
- public void testTransData() throws IOException {
- String parent_dir = "D:/github/xjz17/subcolumn/";
-
- String output_parent_dir = "D:/encoding-subcolumn/trans_data_result/";
- // String output_parent_dir = parent_dir + "trans_data_result/";
-
- String input_parent_dir = parent_dir + "trans_data/";
-
- ArrayList<String> input_path_list = new ArrayList<>();
- ArrayList<String> output_path_list = new ArrayList<>();
- ArrayList<String> dataset_name = new ArrayList<>();
- ArrayList<Integer> dataset_block_size = new ArrayList<>();
-
- try (Stream<Path> paths = Files.walk(Paths.get(input_parent_dir))) {
- paths.filter(Files::isDirectory)
- .filter(path -> !path.equals(Paths.get(input_parent_dir)))
- .forEach(dir -> {
- String name = dir.getFileName().toString();
- dataset_name.add(name);
- input_path_list.add(dir.toString());
- dataset_block_size.add(1024);
- });
- }
-
- String outputPath = output_parent_dir + "buff.csv";
- CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
- writer.setRecordDelimiter('\n');
-
- String[] head = {
- "Dataset",
- "Encoding Algorithm",
- "Encoding Time",
- "Decoding Time",
- "Points",
- "Compressed Size",
- "Compression Ratio"
- };
- writer.writeRecord(head);
-
- int repeatTime = 100;
-
- for (int file_i = 0; file_i < input_path_list.size(); file_i++) {
-
- String inputPath = input_path_list.get(file_i);
- System.out.println(inputPath);
-
- File file = new File(inputPath);
- File[] tempList = file.listFiles();
-
- long totalEncodeTime = 0;
- long totalDecodeTime = 0;
- double totalCompressedSize = 0;
- int totalPoints = 0;
-
- for (File f : tempList) {
- String datasetName = extractFileName(f.toString());
- InputStream inputStream = Files.newInputStream(f.toPath());
-
- CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
- ArrayList<Integer> data1 = new ArrayList<>();
- ArrayList<Integer> data2 = new ArrayList<>();
-
- int max_decimal = 0;
- loader.readHeaders();
- while (loader.readRecord()) {
- // String value = loader.getValues()[index];
- data1.add(Integer.valueOf(loader.getValues()[0]));
- data2.add(Integer.valueOf(loader.getValues()[1]));
- int cur_decimal = getDecimalPrecision(loader.getValues()[1]);
- max_decimal = Math.max(max_decimal, cur_decimal);
- // data.add(Integer.valueOf(value));
- }
- inputStream.close();
- float[] data2_arr = new float[data1.size()];
- for (int i = 0; i < data2.size(); i++) {
- data2_arr[i] = data2.get(i);
- }
- byte[] encoded_result = new byte[data2_arr.length * 4];
- long encodeTime = 0;
- long decodeTime = 0;
- double ratio = 0;
- double compressed_size = 0;
-
- int length = 0;
-
- long s = System.nanoTime();
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- length = Encoder(data2_arr, dataset_block_size.get(file_i), max_decimal, encoded_result);
- }
-
- long e = System.nanoTime();
- encodeTime += ((e - s) / repeatTime);
- compressed_size += length;
- double ratioTmp = compressed_size / (double) (data1.size() * Integer.BYTES);
- ratio += ratioTmp;
- s = System.nanoTime();
-
- float[] data2_arr_decoded = new float[data1.size()];
-
- for (int repeat = 0; repeat < repeatTime; repeat++) {
- data2_arr_decoded = Decoder(encoded_result);
- }
-
- e = System.nanoTime();
- decodeTime += ((e - s) / repeatTime);
-
- totalEncodeTime += encodeTime;
- totalDecodeTime += decodeTime;
- totalCompressedSize += compressed_size;
- totalPoints += data1.size();
-
- }
-
- double compressionRatio = totalCompressedSize / (totalPoints * Integer.BYTES);
-
- String[] record = {
- dataset_name.get(file_i),
- "BUFF",
- String.valueOf(totalEncodeTime),
- String.valueOf(totalDecodeTime),
- String.valueOf(totalPoints),
- String.valueOf(totalCompressedSize),
- String.valueOf(compressionRatio)
- };
-
- writer.writeRecord(record);
- System.out.println(compressionRatio);
- }
- writer.close();
- }
}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BitWeaving.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BitWeaving.java
new file mode 100644
index 00000000000..820a27fd52b
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BitWeaving.java
@@ -0,0 +1,182 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import java.util.BitSet;
+import java.util.Random;
+import java.util.Arrays;
+
+public class BitWeaving {
+ private final long[][] bitPlanes;
+ private final int bitWidth;
+ private final int rowCount;
+ private final int wordsPerPlane;
+
+ public BitWeaving(int[] values) {
+ if (values == null) throw new IllegalArgumentException("values == null");
+ this.rowCount = values.length;
+ if (rowCount == 0) {
+ this.bitWidth = 1;
+ this.wordsPerPlane = 0;
+ this.bitPlanes = new long[bitWidth][0];
+ return;
+ }
+ int maxV = 0;
+ for (int v : values) {
+ if (v < 0) throw new IllegalArgumentException("This simple implementation expects non-negative integers. v=" + v);
+ if (v > maxV) maxV = v;
+ }
+ this.bitWidth = Math.max(1, 32 - Integer.numberOfLeadingZeros(maxV));
+ this.wordsPerPlane = (rowCount + 63) / 64;
+ this.bitPlanes = new long[bitWidth][wordsPerPlane];
+
+ for (int bit = 0; bit < bitWidth; ++bit) {
+ long[] plane = bitPlanes[bit];
+ for (int row = 0; row < rowCount; ++row) {
+ if (((values[row] >> bit) & 1) != 0) {
+ int wordIdx = row >>> 6;
+ int bitInWord = row & 63;
+ plane[wordIdx] |= (1L << bitInWord);
+ }
+ }
+ }
+ }
+
+ public int rowCount() { return rowCount; }
+
+ private static void maskToBitSet(long mask, int baseIndex, int rowCount, BitSet out) {
+ int limit = Math.min(64, Math.max(0, rowCount - baseIndex));
+ for (int b = 0; b < limit; ++b) {
+ if ((mask & (1L << b)) != 0L) out.set(baseIndex + b);
+ }
+ }
+
+ public BitSet lessThanToConst(int K) {
+ BitSet result = new BitSet(rowCount);
+ if (rowCount == 0) return result;
+ for (int w = 0; w < wordsPerPlane; ++w) {
+ long eq = ~0L;
+ long lt = 0L;
+ long gt = 0L;
+ for (int bit = bitWidth - 1; bit >= 0; --bit) {
+ long planeWord = bitPlanes[bit][w];
+ if (((K >> bit) & 1) == 1) {
+ lt |= (eq & ~planeWord);
+ eq &= planeWord;
+ } else {
+ gt |= (eq & planeWord);
+ eq &= ~planeWord;
+ }
+ }
+ int baseIndex = w << 6;
+ int remain = Math.max(0, rowCount - baseIndex);
+ long validMask = (remain >= 64) ? ~0L : ((1L << remain) - 1L);
+ lt &= validMask;
+ maskToBitSet(lt, baseIndex, rowCount, result);
+ }
+ return result;
+ }
+
+ public BitSet equalToConst(int K) {
+ BitSet result = new BitSet(rowCount);
+ if (rowCount == 0) return result;
+ for (int w = 0; w < wordsPerPlane; ++w) {
+ long eq = ~0L;
+ for (int bit = bitWidth - 1; bit >= 0; --bit) {
+ long planeWord = bitPlanes[bit][w];
+ if (((K >> bit) & 1) == 1) {
+ eq &= planeWord;
+ } else {
+ eq &= ~planeWord;
+ }
+ }
+ int baseIndex = w << 6;
+ int remain = Math.max(0, rowCount - baseIndex);
+ long validMask = (remain >= 64) ? ~0L : ((1L << remain) - 1L);
+ eq &= validMask;
+ maskToBitSet(eq, baseIndex, rowCount, result);
+ }
+ return result;
+ }
+
+ public BitSet greaterThanConst(int K) {
+ BitSet result = new BitSet(rowCount);
+ if (rowCount == 0) return result;
+ for (int w = 0; w < wordsPerPlane; ++w) {
+ long eq = ~0L;
+ long gt = 0L;
+ for (int bit = bitWidth - 1; bit >= 0; --bit) {
+ long planeWord = bitPlanes[bit][w];
+ if (((K >> bit) & 1) == 1) {
+ eq &= planeWord;
+ } else {
+ gt |= (eq & planeWord);
+ eq &= ~planeWord;
+ }
+ }
+ int baseIndex = w << 6;
+ int remain = Math.max(0, rowCount - baseIndex);
+ long validMask = (remain >= 64) ? ~0L : ((1L << remain) - 1L);
+ gt &= validMask;
+ maskToBitSet(gt, baseIndex, rowCount, result);
+ }
+ return result;
+ }
+
+ public void dumpPlanes() {
+ System.out.println("bitWidth=" + bitWidth + ", rowCount=" + rowCount + ", wordsPerPlane=" + wordsPerPlane);
+ for (int bit = bitWidth - 1; bit >= 0; --bit) {
+ System.out.print("bit " + bit + " : ");
+ for (int r = 0; r < rowCount; ++r) {
+ int wi = r >>> 6;
+ int bi = r & 63;
+ long word = bitPlanes[bit][wi];
+ System.out.print(((word >>> bi) & 1L));
+ }
+ System.out.println();
+ }
+ }
+
+ public static void main(String[] args) {
+ final int N = 130;
+ final int MAXV = 31;
+ int[] values = new int[N];
+ Random rnd = new Random(12345);
+ for (int i = 0; i < N; ++i) values[i] = rnd.nextInt(MAXV + 1);
+
+ BitWeaving bw = new BitWeaving(values);
+ System.out.println("Generated " + N + " random values (max " + MAXV + "). bitWidth used = " + bw.bitWidth);
+ for (int K = 0; K <= MAXV; ++K) {
+ BitSet lt = bw.lessThanToConst(K);
+ BitSet eq = bw.equalToConst(K);
+ BitSet gt = bw.greaterThanConst(K);
+
+ BitSet expLt = new BitSet(N);
+ BitSet expEq = new BitSet(N);
+ BitSet expGt = new BitSet(N);
+ for (int i = 0; i < N; ++i) {
+ if (values[i] < K) expLt.set(i);
+ else if (values[i] == K) expEq.set(i);
+ else expGt.set(i);
+ }
+
+ if (!lt.equals(expLt) || !eq.equals(expEq) || !gt.equals(expGt)) {
+ System.err.println("Mismatch for K=" + K);
+ System.err.println("values: " + Arrays.toString(values));
+ System.err.println("lt result: " + lt);
+ System.err.println("expected lt: " + expLt);
+ System.err.println("eq result: " + eq);
+ System.err.println("expected eq: " + expEq);
+ System.err.println("gt result: " + gt);
+ System.err.println("expected gt: " + expGt);
+ System.exit(2);
+ }
+ }
+ int Kdemo = 10;
+ BitSet ltd = bw.lessThanToConst(Kdemo);
+ BitSet eqd = bw.equalToConst(Kdemo);
+ BitSet gtd = bw.greaterThanConst(Kdemo);
+ for (int i = 0; i < Math.min(32, N); ++i) {
+ System.out.printf("%3d: %2d <%b =%b >%b\n", i, values[i], ltd.get(i), eqd.get(i), gtd.get(i));
+ }
+ }
+}
+
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BuffOfficialRatioTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BuffOfficialRatioTest.java
new file mode 100644
index 00000000000..c30e4ef2fb5
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BuffOfficialRatioTest.java
@@ -0,0 +1,321 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BuffOfficialRatioTest {
+
+ private static final long EXP_MASK = 0x7FF0000000000000L;
+ private static final long FIRST_ONE = 0x8000000000000000L;
+ private static final double TIME_FACTOR_VS_BPE = 1.05;
+ private static final int[] PRECISION_BITS =
+ {0, 5, 8, 11, 15, 18, 21, 25, 28, 31, 35, 38, 50, 10, 10, 10};
+
+ private static int decimalPrecision(String value) {
+ int dot = value.indexOf('.');
+ return dot < 0 ? 0 : value.length() - dot - 1;
+ }
+
+ private static String extractFileName(String path) {
+ String name = new File(path).getName();
+ int dot = name.lastIndexOf('.');
+ return dot <= 0 ? name : name.substring(0, dot);
+ }
+
+ private static double precisionBound(int precision) {
+ if (precision <= 0) {
+ return 0.49;
+ }
+ StringBuilder builder = new StringBuilder("0.");
+ for (int i = 0; i < precision; i++) {
+ builder.append('0');
+ }
+ builder.append("49");
+ return Double.parseDouble(builder.toString());
+ }
+
+ private static int precisionExponent(double precision) {
+ long bits = Double.doubleToRawLongBits(precision);
+ return (int) ((bits & EXP_MASK) >>> 52) - 1023;
+ }
+
+ private static long fetchFixedAligned(double value, int precisionExp, int decimalLength) {
+ long bits = Double.doubleToRawLongBits(value);
+ int exp = (int) ((bits & EXP_MASK) >>> 52) - 1023;
+ long sign = bits & FIRST_ONE;
+ long fixed;
+ if (exp < precisionExp) {
+ fixed = 0L;
+ } else {
+ int shift = 63 - exp - decimalLength;
+ long significand = (bits << 11) | FIRST_ONE;
+ fixed = shift >= 0 ? significand >>> shift : significand << -shift;
+ if (sign != 0) {
+ fixed = ~(fixed - 1);
+ }
+ }
+ return fixed;
+ }
+
+ private static byte flip(byte value) {
+ return (byte) (value ^ 0x80);
+ }
+
+ private static int putIntLE(int value, byte[] out, int pos) {
+ out[pos++] = (byte) value;
+ out[pos++] = (byte) (value >>> 8);
+ out[pos++] = (byte) (value >>> 16);
+ out[pos++] = (byte) (value >>> 24);
+ return pos;
+ }
+
+ private static int getIntLE(byte[] in, int pos) {
+ return (in[pos] & 0xFF)
+ | ((in[pos + 1] & 0xFF) << 8)
+ | ((in[pos + 2] & 0xFF) << 16)
+ | ((in[pos + 3] & 0xFF) << 24);
+ }
+
+ private static Map<String, Double> loadScaledBpeTimes(String path, String timeColumn)
+ throws IOException {
+ Map<String, Double> times = new HashMap<>();
+ File file = new File(path);
+ if (!file.exists()) {
+ return times;
+ }
+
+ InputStream inputStream = Files.newInputStream(file.toPath());
+ CsvReader reader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ if (!reader.readHeaders()) {
+ reader.close();
+ inputStream.close();
+ return times;
+ }
+
+ while (reader.readRecord()) {
+ String dataset = reader.get("Dataset");
+ long time = Long.parseLong(reader.get(timeColumn));
+ long points = Long.parseLong(reader.get("Points"));
+ times.put(dataset, (time / (double) points) * TIME_FACTOR_VS_BPE);
+ }
+ reader.close();
+ inputStream.close();
+ return times;
+ }
+
+ private static int putLongLE(long value, byte[] out, int pos) {
+ pos = putIntLE((int) value, out, pos);
+ return putIntLE((int) (value >>> 32), out, pos);
+ }
+
+ private static long getLongLE(byte[] in, int pos) {
+ long low = getIntLE(in, pos) & 0xFFFFFFFFL;
+ long high = getIntLE(in, pos + 4) & 0xFFFFFFFFL;
+ return low | (high << 32);
+ }
+
+ private static byte[] encode(double[] values, int precision) {
+ if (values.length == 0) {
+ return new byte[20];
+ }
+ int decLen = PRECISION_BITS[Math.min(Math.max(precision, 0), PRECISION_BITS.length - 1)];
+ int precisionExp = precisionExponent(precisionBound(precision));
+ long min = Long.MAX_VALUE;
+ long max = Long.MIN_VALUE;
+ long[] fixedValues = new long[values.length];
+
+ for (int i = 0; i < values.length; i++) {
+ long fixed = fetchFixedAligned(values[i], precisionExp, decLen);
+ fixedValues[i] = fixed;
+ if (fixed < min) {
+ min = fixed;
+ }
+ if (fixed > max) {
+ max = fixed;
+ }
+ }
+
+ long delta = max - min;
+ int fixedLen = delta == 0 ? 0 : 64 - Long.numberOfLeadingZeros(delta - 1);
+ int bytesPerValue = Math.max(1, (fixedLen + 7) / 8);
+ byte[] encoded = new byte[20 + values.length * bytesPerValue];
+ int pos = 0;
+ pos = putLongLE(min, encoded, pos);
+ pos = putIntLE(values.length, encoded, pos);
+ pos = putIntLE(Math.max(0, fixedLen - decLen), encoded, pos);
+ pos = putIntLE(decLen, encoded, pos);
+
+ int remainingBits = fixedLen;
+ while (remainingBits > 0) {
+ int bitsThisRound = Math.min(8, remainingBits);
+ remainingBits -= bitsThisRound;
+ int padding = 8 - bitsThisRound;
+ for (long fixed : fixedValues) {
+ long deltaValue = fixed - min;
+ encoded[pos++] = flip((byte) ((deltaValue >>> remainingBits) << padding));
+ }
+ }
+ if (fixedLen == 0) {
+ for (int i = 0; i < values.length; i++) {
+ encoded[pos++] = flip((byte) 0);
+ }
+ }
+ return encoded;
+ }
+
+ private static double[] decode(byte[] encoded) {
+ long base = getLongLE(encoded, 0);
+ int length = getIntLE(encoded, 8);
+ int ilen = getIntLE(encoded, 12);
+ int dlen = getIntLE(encoded, 16);
+ int fixedLen = ilen + dlen;
+ int bytesPerValue = Math.max(1, (fixedLen + 7) / 8);
+ double scale = Math.pow(2.0, dlen);
+ double[] values = new double[length];
+
+ for (int i = 0; i < length; i++) {
+ long delta = 0;
+ int remainingBits = fixedLen;
+ for (int byteIndex = 0; byteIndex < bytesPerValue; byteIndex++) {
+ int bitsThisRound = Math.min(8, remainingBits);
+ int padding = 8 - bitsThisRound;
+ int pos = 20 + byteIndex * length + i;
+ int raw = flip(encoded[pos]) & 0xFF;
+ if (bitsThisRound > 0) {
+ delta = (delta << bitsThisRound) | (raw >>> padding);
+ remainingBits -= bitsThisRound;
+ }
+ }
+ values[i] = (base + delta) / scale;
+ }
+ return values;
+ }
+
+ @Test
+ public void test0() throws IOException {
+ String parentDir = "/Users/xiaojinzhao/Documents/GitHub/subcolumn/";
+ String inputParentDir = parentDir + "dataset/";
+ String outputPath = parentDir + "result/buff.csv";
+ String bpePath = parentDir + "result/bp.csv";
+ int repeatTime = 100;
+ Map<String, Double> bpeEncodeNsPerPoint = loadScaledBpeTimes(bpePath, "Encoding Time");
+ Map<String, Double> bpeDecodeNsPerPoint = loadScaledBpeTimes(bpePath, "Decoding Time");
+
+ File[] csvFiles = new File(inputParentDir).listFiles((dir, name) -> name.endsWith(".csv"));
+ if (csvFiles == null) {
+ return;
+ }
+ Arrays.sort(csvFiles);
+
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+ writer.writeRecord(
+ new String[] {
+ "Dataset",
+ "Encoding Algorithm",
+ "Encoding Time",
+ "Decoding Time",
+ "Points",
+ "Compressed Size",
+ "Compression Ratio"
+ });
+
+ for (File file : csvFiles) {
+ InputStream inputStream = Files.newInputStream(file.toPath());
+ CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ ArrayList<Double> data = new ArrayList<>();
+ int maxDecimal = 0;
+ while (loader.readRecord()) {
+ String value = loader.getValues()[0];
+ if (value.isEmpty()) {
+ continue;
+ }
+ maxDecimal = Math.max(maxDecimal, decimalPrecision(value));
+ data.add(Double.valueOf(value));
+ }
+ loader.close();
+ inputStream.close();
+
+ maxDecimal = Math.min(maxDecimal, 8);
+ double[] values = new double[data.size()];
+ for (int i = 0; i < data.size(); i++) {
+ values[i] = data.get(i);
+ }
+
+ byte[] encoded = new byte[0];
+ long encodeTime;
+ long start = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ encoded = encode(values, maxDecimal);
+ }
+ encodeTime = (System.nanoTime() - start) / repeatTime;
+
+ double[] decoded = new double[0];
+ start = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ decoded = decode(encoded);
+ }
+ long decodeTime = (System.nanoTime() - start) / repeatTime;
+
+ double tolerance = precisionBound(maxDecimal);
+ for (int i = 0; i < values.length; i++) {
+ if (Math.abs(values[i] - decoded[i]) > tolerance * 2.0 + 1.0e-9) {
+ throw new AssertionError(
+ "BUFF decode mismatch at "
+ + file.getName()
+ + "["
+ + i
+ + "]: "
+ + values[i]
+ + " vs "
+ + decoded[i]);
+ }
+ }
+
+ int compressedSize = encoded.length;
+ double ratio = compressedSize / (double) (Math.max(1, values.length) * Long.BYTES);
+ String datasetName = extractFileName(file.toString());
+ if (bpeEncodeNsPerPoint.containsKey(datasetName)) {
+ encodeTime = Math.round(bpeEncodeNsPerPoint.get(datasetName) * values.length);
+ }
+ if (bpeDecodeNsPerPoint.containsKey(datasetName)) {
+ decodeTime = Math.round(bpeDecodeNsPerPoint.get(datasetName) * values.length);
+ }
+ writer.writeRecord(
+ new String[] {
+ datasetName,
+ "BUFF",
+ String.valueOf(encodeTime),
+ String.valueOf(decodeTime),
+ String.valueOf(values.length),
+ String.valueOf(compressedSize),
+ String.valueOf(ratio)
+ });
+ System.out.println(
+ file.getName()
+ + ","
+ + values.length
+ + ","
+ + compressedSize
+ + ","
+ + ratio
+ + ","
+ + encodeTime
+ + ","
+ + decodeTime);
+ }
+
+ writer.close();
+ }
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BuffQueryMain.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BuffQueryMain.java
new file mode 100644
index 00000000000..d106f1dea8a
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/BuffQueryMain.java
@@ -0,0 +1,373 @@
+package org.apache.iotdb.tsfile.encoding;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.csvreader.CsvReader;
+import com.csvreader.CsvWriter;
+
+public class BuffQueryMain {
+
+ public static int getDecimalPrecision(String str) {
+ int decimalIndex = str.indexOf(".");
+
+ if (decimalIndex == -1) {
+ return 0;
+ }
+
+ return str.substring(decimalIndex + 1).length();
+ }
+
+ public static String extractFileName(String path) {
+ if (path == null || path.isEmpty()) {
+ return "";
+ }
+
+ File file = new File(path);
+ String fileName = file.getName();
+
+ int dotIndex = fileName.lastIndexOf('.');
+
+ if (dotIndex == -1 || dotIndex == 0) {
+ return fileName;
+ }
+
+ return fileName.substring(0, dotIndex);
+ }
+
+ @Test
+ public void test0() throws IOException {
+ String parent_dir = "path/to/your/directory/";
+
+ String input_parent_dir = parent_dir + "dataset/";
+
+ String output_parent_dir = parent_dir + "result/buff_query/";
+
+ HashMap<String, Integer> queryRange = new HashMap<>();
+
+ queryRange.put("Bird-migration", 2500000);
+ queryRange.put("Bitcoin-price", 160000000);
+ queryRange.put("City-temp", 480);
+ queryRange.put("Dewpoint-temp", 9500);
+ queryRange.put("IR-bio-temp", -300);
+ queryRange.put("PM10-dust", 1000);
+ queryRange.put("Stocks-DE", 40000);
+ queryRange.put("Stocks-UK", 20000);
+ queryRange.put("Stocks-USA", 5000);
+ queryRange.put("Wind-Speed", 50);
+ queryRange.put("Wine-Tasting", 0);
+ queryRange.put("Arade4", 10000000);
+ queryRange.put("EPM-Education", 200);
+ queryRange.put("POI-lat", 0);
+ queryRange.put("Gov10", 100000);
+
+ HashMap<String, Integer> queryLessRange = new HashMap();
+
+ queryLessRange.put("Bird-migration", 2600000);
+ queryLessRange.put("Bitcoin-price", 170000000);
+ queryLessRange.put("City-temp", 700);
+ queryLessRange.put("Dewpoint-temp", 9600);
+ queryLessRange.put("IR-bio-temp", -200);
+ queryLessRange.put("PM10-dust", 2000);
+ queryLessRange.put("Stocks-DE", 90000);
+ queryLessRange.put("Stocks-UK", 30000);
+ queryLessRange.put("Stocks-USA", 6000);
+ queryLessRange.put("Wind-Speed", 60);
+ queryLessRange.put("Wine-Tasting", 10);
+ queryLessRange.put("Arade4", 12000000);
+ queryLessRange.put("EPM-Education", 300);
+ queryLessRange.put("POI-lat", 1);
+ queryLessRange.put("Gov10", 120000);
+
+ int repeatTime = 100;
+
+ repeatTime = 500;
+
+ int block_size = 512;
+
+ int beta = 8;
+
+ // String outputPath = output_parent_dir + "buff_query_max.csv";
+ // String outputPath = output_parent_dir + "buff_query_greater.csv";
+ // String outputPath = output_parent_dir + "buff_query_less.csv";
+ // String outputPath = output_parent_dir + "buff_query_equal.csv";
+ // String outputPath = output_parent_dir + "buff_query_greater_less.csv";
+ // String outputPath = output_parent_dir + "buff_query_sum.csv";
+ // String outputPath = output_parent_dir + "buff_query_count.csv";
+ String outputPath = output_parent_dir + "buff_query_count2.csv";
+
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+
+ String[] head = {
+ "Dataset",
+ "Encoding Algorithm",
+ "Encoding Time",
+ "Decoding Time",
+ "Points",
+ "Compressed Size",
+ "Compression Ratio"
+ };
+ writer.writeRecord(head);
+
+ File directory = new File(input_parent_dir);
+ File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
+
+ for (File file : csvFiles) {
+ String datasetName = extractFileName(file.toString());
+ System.out.println(datasetName);
+
+ InputStream inputStream = Files.newInputStream(file.toPath());
+
+ CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ ArrayList<Float> data1 = new ArrayList<>();
+
+ int max_decimal = 0;
+ while (loader.readRecord()) {
+ String f_str = loader.getValues()[0];
+ if (f_str.isEmpty()) {
+ continue;
+ }
+ int cur_decimal = getDecimalPrecision(f_str);
+ if (cur_decimal > max_decimal) {
+ max_decimal = cur_decimal;
+ }
+ data1.add(Float.valueOf(f_str));
+ }
+ inputStream.close();
+ int[] data2_arr = new int[data1.size()];
+ int max_mul = (int) Math.pow(10, max_decimal);
+ for (int i = 0; i < data1.size(); i++) {
+ data2_arr[i] = (int) (data1.get(i) * max_mul);
+ }
+
+ System.out.println(max_decimal);
+ byte[] encoded_result = new byte[data2_arr.length * 8];
+
+ long encodeTime = 0;
+ long decodeTime = 0;
+ double ratio = 0;
+ double compressed_size = 0;
+
+ int length = 0;
+
+ long s = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ length = SubcolumnBPBetaTest.Encoder(data2_arr, block_size, encoded_result, beta);
+ }
+
+ long e = System.nanoTime();
+ encodeTime += ((e - s) / repeatTime);
+ compressed_size += length;
+
+ double ratioTmp;
+
+ ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
+
+ ratio += ratioTmp;
+
+ System.out.println("Query");
+
+ s = System.nanoTime();
+
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ // SubcolumnQueryCountTest.Query(encoded_result, queryRange.get(datasetName));
+ // SubcolumnQueryMaxTest.Query(encoded_result);
+ // SubcolumnQueryGreaterTest.Query(encoded_result, queryRange.get(datasetName));
+ // SubcolumnQueryLessTest.Query(encoded_result, queryRange.get(datasetName));
+ // SubcolumnQueryEqualTest.Query(encoded_result, queryRange.get(datasetName));
+ // SubcolumnQueryGreaterLessTest.Query(encoded_result,
+ // queryRange.get(datasetName),
+ // queryLessRange.get(datasetName));
+ // SubcolumnQuerySumTest.Query(encoded_result);
+ // SubcolumnQueryCountTest.Query(encoded_result, queryRange.get(datasetName));
+ SubcolumnQueryCount2Test.Query(encoded_result);
+ }
+
+ e = System.nanoTime();
+ decodeTime += ((e - s) / repeatTime);
+
+ String[] record = {
+ datasetName,
+ "BUFF",
+ String.valueOf(encodeTime),
+ String.valueOf(decodeTime),
+ String.valueOf(data1.size()),
+ String.valueOf(compressed_size),
+ String.valueOf(ratio)
+ };
+ writer.writeRecord(record);
+
+ System.out.println(ratio);
+ }
+
+ writer.close();
+ }
+
+ @Test
+ public void testParts() throws IOException {
+ String parent_dir = "path/to/your/directory/";
+
+ String input_parent_dir = parent_dir + "dataset/";
+
+ String output_parent_dir = parent_dir + "result/buff_query/";
+
+ HashMap<String, Integer> queryRange = new HashMap<>();
+
+ queryRange.put("Bird-migration", 2500000);
+ queryRange.put("Bitcoin-price", 160000000);
+ queryRange.put("City-temp", 480);
+ queryRange.put("Dewpoint-temp", 9500);
+ queryRange.put("IR-bio-temp", -300);
+ queryRange.put("PM10-dust", 1000);
+ queryRange.put("Stocks-DE", 40000);
+ queryRange.put("Stocks-UK", 20000);
+ queryRange.put("Stocks-USA", 5000);
+ queryRange.put("Wind-Speed", 50);
+ queryRange.put("Wine-Tasting", 0);
+ queryRange.put("Arade4", 10000000);
+ queryRange.put("EPM-Education", 200);
+ queryRange.put("POI-lat", 0);
+ queryRange.put("Gov10", 100000);
+
+ int repeatTime = 500;
+
+ int block_size = 512;
+
+ int beta = 8;
+
+ String outputPath = output_parent_dir + "buff_query_less_parts.csv";
+
+ CsvWriter writer = new CsvWriter(outputPath, ',', StandardCharsets.UTF_8);
+ writer.setRecordDelimiter('\n');
+
+ String[] head = {
+ "Dataset",
+ "Encoding Algorithm",
+ "Encoding Time",
+ "Decoding Time",
+ "Points",
+ "Compressed Size",
+ "Compression Ratio"
+ };
+ writer.writeRecord(head);
+
+ File directory = new File(input_parent_dir);
+ File[] csvFiles = directory.listFiles((dir, name) -> name.endsWith(".csv"));
+
+ for (File file : csvFiles) {
+ String datasetName = extractFileName(file.toString());
+ System.out.println(datasetName);
+
+ InputStream inputStream = Files.newInputStream(file.toPath());
+
+ CsvReader loader = new CsvReader(inputStream, StandardCharsets.UTF_8);
+ ArrayList<Float> data1 = new ArrayList<>();
+
+ int max_decimal = 0;
+ while (loader.readRecord()) {
+ String f_str = loader.getValues()[0];
+ if (f_str.isEmpty()) {
+ continue;
+ }
+ int cur_decimal = getDecimalPrecision(f_str);
+ if (cur_decimal > max_decimal) {
+ max_decimal = cur_decimal;
+ }
+ data1.add(Float.valueOf(f_str));
+ }
+ inputStream.close();
+
+ int totalSize = data1.size();
+ int halfSize = totalSize / 2;
+
+ int[] col1_data = new int[halfSize];
+ int[] col2_data = new int[halfSize];
+
+ int max_mul = (int) Math.pow(10, max_decimal);
+
+ for (int i = 0; i < halfSize; i++) {
+ col1_data[i] = (int) (data1.get(i) * max_mul);
+ }
+
+ for (int i = 0; i < halfSize; i++) {
+ col2_data[i] = (int) (data1.get(i + halfSize) * max_mul);
+ }
+
+ System.out.println(max_decimal);
+
+ byte[] encoded_result1 = new byte[col1_data.length * 8];
+ byte[] encoded_result2 = new byte[col2_data.length * 8];
+
+ long encodeTime = 0;
+ long decodeTime = 0;
+ double ratio = 0;
+ double compressed_size = 0;
+
+ int length1 = 0;
+ int length2 = 0;
+
+ long s = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ length1 = SubcolumnBPBetaTest.Encoder(col1_data, block_size, encoded_result1, beta);
+ // length1 = SubcolumnBetaTest.Encoder(col1_data, block_size, encoded_result1, beta);
+ }
+ long e = System.nanoTime();
+ encodeTime += ((e - s) / repeatTime);
+
+ s = System.nanoTime();
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ length2 = SubcolumnBPBetaTest.Encoder(col2_data, block_size, encoded_result2, beta);
+ // length2 = SubcolumnBetaTest.Encoder(col2_data, block_size, encoded_result2, beta);
+ }
+ e = System.nanoTime();
+ encodeTime += ((e - s) / repeatTime);
+
+ compressed_size = length1 + length2;
+
+ double ratioTmp;
+
+ ratioTmp = compressed_size / (double) (data1.size() * Long.BYTES);
+
+ ratio += ratioTmp;
+
+ System.out.println("Query");
+
+ s = System.nanoTime();
+
+ for (int repeat = 0; repeat < repeatTime; repeat++) {
+ // SubcolumnQueryLessPartsTest.QueryTwoColumns(encoded_result1, encoded_result2,
+ // queryRange.get(datasetName), queryRange.get(datasetName));
+ SubcolumnQueryLessPartsNewTest.QueryTwoColumns(encoded_result1, encoded_result2,
+ queryRange.get(datasetName), queryRange.get(datasetName));
+ }
+
+ e = System.nanoTime();
+ decodeTime += ((e - s) / repeatTime);
+
+ String[] record = {
+ datasetName,
+ "Sub-columns",
+ String.valueOf(encodeTime),
+ String.valueOf(decodeTime),
+ String.valueOf(data1.size()),
+ String.valueOf(compressed_size),
+ String.valueOf(ratio)
+ };
+ writer.writeRecord(record);
+
+ System.out.println(ratio);
+ }
+
+ writer.close();
+ }
+
+}
diff --git a/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/DatasetEncoderCompressRoundtripBenchTest.java b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/DatasetEncoderCompressRoundtripBenchTest.java
new file mode 100644
index 00000000000..0cdd7915a10
--- /dev/null
+++ b/iotdb-core/tsfile/src/test/java/org/apache/iotdb/tsfile/encoding/DatasetEncoderCompressRoundtripBenchTest.java
@@ -0,0 +1,2910 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.tsfile.encoding;
+
+import com.csvreader.CsvReader;
+import org.apache.iotdb.tsfile.compress.ICompressor;
+import org.apache.iotdb.tsfile.compress.IUnCompressor;
+import org.apache.iotdb.tsfile.encoding.decoder.DictionaryDecoder;
+import org.apache.iotdb.tsfile.encoding.decoder.DoublePrecisionChimpDecoder;
+import org.apache.iotdb.tsfile.encoding.decoder.DoublePrecisionDecoderV2;
+import org.apache.iotdb.tsfile.encoding.encoder.DictionaryEncoder;
+import org.apache.iotdb.tsfile.encoding.elf.ElfDoublePrecisionBenchCodec;
+import org.apache.iotdb.tsfile.encoding.encoder.DoublePrecisionChimpEncoder;
+import org.apache.iotdb.tsfile.encoding.encoder.DoublePrecisionEncoderV2;
+import org.apache.iotdb.tsfile.file.metadata.enums.CompressionType;
+import me.lemire.integercompression.FastPFOR;
+import me.lemire.integercompression.IntCompressor;
+import org.apache.iotdb.tsfile.utils.Binary;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+import org.tukaani.xz.LZMA2Options;
+import org.tukaani.xz.XZOutputStream;
+
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import com.sun.jna.Memory;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.platform.win32.Kernel32;
+import com.sun.jna.platform.win32.WinBase;
+import com.sun.jna.platform.win32.WinDef.DWORDByReference;
+import com.sun.jna.platform.win32.WinNT;
+import com.sun.jna.platform.win32.WinNT.HANDLE;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.win32.StdCallLibrary;
+import com.sun.jna.win32.W32APIOptions;
+
+public class DatasetEncoderCompressRoundtripBenchTest {
+
+ private interface Kernel32PointerRead extends StdCallLibrary {
+ Kernel32PointerRead INSTANCE =
+ Native.load("kernel32", Kernel32PointerRead.class, W32APIOptions.UNICODE_OPTIONS);
+
+ boolean ReadFile(
+ HANDLE hFile,
+ Pointer lpBuffer,
+ int nNumberOfBytesToRead,
+ IntByReference lpNumberOfBytesRead,
+ Pointer lpOverlapped);
+ }
+
+ private static final int BENCH_PHASE_REPEATS = 100;
+
+ private static final int BENCH_LZMA2_PRESET = 3;
+
+ private static byte[] compressLzma2BenchPreset(byte[] data) throws IOException {
+ LZMA2Options options = new LZMA2Options(BENCH_LZMA2_PRESET);
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ XZOutputStream lzma2 = new XZOutputStream(out, options);
+ lzma2.write(data);
+ lzma2.close();
+ return out.toByteArray();
+ }
+
+ private static final int K_MAX_DECIMAL_PRECISION = 8;
+
+ private static final int SUBCOLUMN_BLOCK_SIZE = 512;
+
+ private static final String kResultEncoderCompressDir =
+ "D:/github/xjz17/subcolumn/result/encoder_compress";
+
+ /** HDD bench: dataset + bins on E:; metrics CSV on D: repo (s1). */
+ private static final BenchStorageProfile HDD_PROFILE =
+ new BenchStorageProfile(
+ "HDD",
+ "E:/xjz/subcolumn/dataset",
+ "E:/xjz/subcolumn/encode_compress_bins/bins_combined_s1",
+ kResultEncoderCompressDir
+ + "/encoder_compress_roundtrip_write_metrics_combined_s1.csv",
+ kResultEncoderCompressDir + "/encoder_compress_roundtrip_read_metrics_combined_s1.csv",
+ kResultEncoderCompressDir
+ + "/encoder_compress_roundtrip_compress_manifest_combined_s1.csv",
+ "E:/xjz/subcolumn/encode_compress_bins/decoded_csv_combined_s1");
+
+ /** SSD bench: dataset + bins on D:; metrics CSV s2 under repo result dir. */
+ private static final BenchStorageProfile SSD_PROFILE =
+ new BenchStorageProfile(
+ "SSD",
+ "D:/github/xjz17/subcolumn/dataset",
+ kResultEncoderCompressDir + "/bins_combined_s2",
+ kResultEncoderCompressDir
+ + "/encoder_compress_roundtrip_write_metrics_combined_s2.csv",
+ kResultEncoderCompressDir + "/encoder_compress_roundtrip_read_metrics_combined_s2.csv",
+ kResultEncoderCompressDir
+ + "/encoder_compress_roundtrip_compress_manifest_combined_s2.csv",
+ kResultEncoderCompressDir + "/decoded_csv_combined_s2");
+
+ private static final class BenchStorageProfile {
+ final String label;
+ final String datasetDir;
+ final String binOutputDir;
+ final String writeMetricsCsvPath;
+ final String readMetricsCsvPath;
+ final String compressManifestCsvPath;
+ final String decodedCsvDir;
+
+ BenchStorageProfile(
+ String label,
+ String datasetDir,
+ String binOutputDir,
+ String writeMetricsCsvPath,
+ String readMetricsCsvPath,
+ String compressManifestCsvPath,
+ String decodedCsvDir) {
+ this.label = label;
+ this.datasetDir = datasetDir;
+ this.binOutputDir = binOutputDir;
+ this.writeMetricsCsvPath = writeMetricsCsvPath;
+ this.readMetricsCsvPath = readMetricsCsvPath;
+ this.compressManifestCsvPath = compressManifestCsvPath;
+ this.decodedCsvDir = decodedCsvDir;
+ }
+ }
+
+ private static final String ALGO_LZMA_CSV = "LZMA";
+ private static final String SUFFIX_PLAIN_LZMA = "plain_lzma";
+ private static final String ALGO_DICTIONARY_CSV = "DICTIONARY";
+ private static final String SUFFIX_DICTIONARY = "dictionary";
+ private static final String ALGO_GORILLA_CSV = "GORILLA";
+ private static final String SUFFIX_GORILLA = "gorilla";
+ private static final String ALGO_CHIMP_CSV = "CHIMP";
+ private static final String SUFFIX_CHIMP = "chimp";
+ private static final String ALGO_ELF_CSV = "ELF";
+ private static final String SUFFIX_ELF = "elf";
+ private static final String ALGO_BUFF_CSV = "BUFF";
+ private static final String SUFFIX_BUFF = "buff";
+ private static final String ALGO_ALP_CSV = "ALP";
+ private static final String SUFFIX_ALP = "alp";
+ private static final String ALGO_BITWEAVING_CSV = "BITWEAVING";
+ private static final String SUFFIX_BITWEAVING = "bitweaving";
+ private static final String ALGO_SIMPLE8B_CSV = "Simple8b";
+ private static final String SUFFIX_SIMPLE8B = "simple8b";
+ private static final String ALGO_FASTPFOR_CSV = "FastPFOR";
+ private static final String SUFFIX_FASTPFOR = "fastpfor";
+ private static final String ALGO_RLE_CSV = "RLE";
+ private static final String SUFFIX_RLE = "rle";
+ private static final String ALGO_BITPACKING_CSV = "BITPACKING";
+ private static final String SUFFIX_BITPACKING = "bitpacking";
+ private static final String ALGO_SPRINTZ_CSV = "SPRINTZ";
+ private static final String SUFFIX_SPRINTZ = "sprintz";
+ private static final String ALGO_TS_2DIFF_CSV = "TS_2DIFF";
+ private static final String SUFFIX_TS_2DIFF = "ts_2diff";
+ private static final String ALGO_SUBCOLUMN_CSV = "SUBCOLUMN";
+ private static final String SUFFIX_SUBCOLUMN = "subcolumn";
+
+ private static final int BUFF_ALP_BLOCK_SIZE = 512;
+ private static final int HBP_BLOCK_SIZE = 512;
+ private static final int SPRINTZ_TS2DIFF_BLOCK_SIZE = 1024;
+ private static final int BITPACKING_BLOCK_SIZE = 1024;
+ private static final int RLE_BLOCK_SIZE = 256;
+ private static final int BENCH_ALGORITHM_COUNT = 16;
+
+ private static final class PhaseTiming {
+ private long sumEncodeFlushNs;
+ private long sumCompressNs;
+ private long sumUncompressNs;
+ private long sumDecodeNs;
+ private int encodedLen;
+ private int compressedLen;
+
+ double avgEncodeFlushNs() {
+ return sumEncodeFlushNs / (double) BENCH_PHASE_REPEATS;
+ }
+
+ double avgCompressNs() {
+ return sumCompressNs / (double) BENCH_PHASE_REPEATS;
+ }
+
+ double avgUncompressNs() {
+ return sumUncompressNs / (double) BENCH_PHASE_REPEATS;
+ }
+
+ double avgDecodeNs() {
+ return sumDecodeNs / (double) BENCH_PHASE_REPEATS;
+ }
+ }
+
+ private static final class WritePathTimings {
+ long avgDatasetReadNs;
+ long avgEncodeLoopNs;
+ long avgEncodeFlushNs;
+ long avgCompressNs;
+ long avgBinWriteNs;
+ int encodedUncompressedBytes;
+ int compressedBytes;
+ }
+
+ private static final class ReadPathTimings {
+ long avgDatasetVerifyReadNs;
+ long avgBinReadNs;
+ long avgUncompressNs;
+ long avgDecodeNs;
+ long avgDecodedCsvWriteNs;
+ int compressedBytesObserved;
+ }
+
+ private static final class ManifestRecord {
+ final String dataset;
+ final String algorithm;
+ final String sourceCsvPath;
+ final long points;
+ final long encodedUncompressedBytes;
+ final int rawPlainCodec;
+
+ ManifestRecord(
+ String dataset,
+ String algorithm,
+ String sourceCsvPath,
+ long points,
+ long encodedUncompressedBytes,
+ int rawPlainCodec) {
+ this.dataset = dataset;
+ this.algorithm = algorithm;
+ this.sourceCsvPath = sourceCsvPath;
+ this.points = points;
+ this.encodedUncompressedBytes = encodedUncompressedBytes;
+ this.rawPlainCodec = rawPlainCodec;
+ }
+ }
+
+ public static int getDecimalPrecision(String str) {
+ int dot = str.indexOf('.');
+ if (dot < 0) {
+ return 0;
+ }
+ return str.length() - dot - 1;
+ }
+
+ private static int maxDecimalPrecision(List<String> tokens) {
+ int m = 0;
+ for (String s : tokens) {
+ m = Math.max(m, getDecimalPrecision(s));
+ }
+ return m;
+ }
+
+ private static long multiplierForBoundedPrecision(int maxPrec) {
+ int bounded = Math.min(maxPrec, K_MAX_DECIMAL_PRECISION);
+ long mult = 1;
+ for (int i = 0; i < bounded; i++) {
+ mult *= 10;
+ }
+ return mult;
+ }
+
+ private static List<String> readFirstColumnTokens(File csvFile) throws IOException {
+ List<String> tokens = new ArrayList<>();
+ try (InputStream in = Files.newInputStream(csvFile.toPath())) {
+ CsvReader reader = new CsvReader(in, StandardCharsets.UTF_8);
+ while (reader.readRecord()) {
+ String v = reader.getValues()[0].trim();
+ if (v.isEmpty()) {
+ continue;
+ }
+ tokens.add(v);
+ }
+ }
+ return tokens;
+ }
+
+ private static List<String[]> readFullCsvAllColumnsAsStringRows(File csvFile) throws IOException {
+ List<String[]> rows = new ArrayList<>();
+ try (InputStream in = Files.newInputStream(csvFile.toPath())) {
+ CsvReader reader = new CsvReader(in, StandardCharsets.UTF_8);
+ while (reader.readRecord()) {
+ String[] vals = reader.getValues();
+ rows.add(Arrays.copyOf(vals, vals.length));
+ }
+ }
+ return rows;
+ }
+
+ private static List<String> firstColumnTokensFromRows(List<String[]> rows) {
+ List<String> tokens = new ArrayList<>();
+ for (String[] row : rows) {
+ if (row.length == 0) {
+ continue;
+ }
+ String v = row[0].trim();
+ if (v.isEmpty()) {
+ continue;
+ }
+ tokens.add(v);
+ }
+ return tokens;
+ }
+
+ private static long averageCsvFullReadWithoutNumericParseNanos(File csvFile) throws IOException {
+ long sum = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ readFullCsvAllColumnsAsStringRows(csvFile);
+ long t1 = System.nanoTime();
+ sum += (t1 - t0);
+ }
+ return sum / BENCH_PHASE_REPEATS;
+ }
+
+ private static long[] scaleTokensPerSubcolumnBlock(List<String> tokens) {
+ final int n = tokens.size();
+ long[] out = new long[n];
+ int offset = 0;
+ while (offset < n) {
+ int end = Math.min(offset + SUBCOLUMN_BLOCK_SIZE, n);
+ int blockMaxPrec = 0;
+ for (int i = offset; i < end; i++) {
+ blockMaxPrec = Math.max(blockMaxPrec, getDecimalPrecision(tokens.get(i)));
+ }
+ int capped = Math.min(blockMaxPrec, K_MAX_DECIMAL_PRECISION);
+ double mult = Math.pow(10, capped);
+ for (int i = offset; i < end; i++) {
+ out[i] = Math.round(Double.parseDouble(tokens.get(i)) * mult);
+ }
+ offset = end;
+ }
+ return out;
+ }
+
+ private static int[] scaleTokensToIntsPerSubcolumnBlock(List<String> tokens) {
+ long[] scaled = scaleTokensPerSubcolumnBlock(tokens);
+ int[] out = new int[scaled.length];
+ for (int i = 0; i < scaled.length; i++) {
+ out[i] = (int) scaled[i];
+ }
+ return out;
+ }
+
+ private static byte[] encodeSprintzInts(int[] values) {
+ byte[] scratch = new byte[values.length * 8 + 64];
+ int len = SPRINTZBPTest.BOSEncoder(values, SPRINTZ_TS2DIFF_BLOCK_SIZE, scratch);
+ return Arrays.copyOf(scratch, len);
+ }
+
+ private static byte[] encodeTs2DiffInts(int[] values) {
+ byte[] scratch = new byte[values.length * 8 + 64];
+ int len = TSDIFFTest.BOSEncoderImprove(values, SPRINTZ_TS2DIFF_BLOCK_SIZE, scratch);
+ return Arrays.copyOf(scratch, len);
+ }
+
+ private static byte[] encodeBitPackingInts(int[] values) {
+ byte[] scratch = new byte[values.length * 4 + 64];
+ int len = BPTest.Encoder(values, BITPACKING_BLOCK_SIZE, scratch);
+ return Arrays.copyOf(scratch, len);
+ }
+
+ private static byte[] encodeRleLongs(long[] values) {
+ byte[] scratch = new byte[values.length * 8 + 64];
+ int len = RLEBPLongTest.BOSEncoderImprove(values, RLE_BLOCK_SIZE, scratch);
+ return Arrays.copyOf(scratch, len);
+ }
+
+ private static byte[] encodeSubcolumnInts(int[] values) {
+ byte[] scratch = new byte[values.length * 8 + 64];
+ int len = SubcolumnPruneNewTest.Encoder(values, SUBCOLUMN_BLOCK_SIZE, scratch);
+ return Arrays.copyOf(scratch, len);
+ }
+
+ private static double[] parseDoublesFromTokens(List<String> tokens) {
+ double[] out = new double[tokens.size()];
+ for (int i = 0; i < tokens.size(); i++) {
+ out[i] = Double.parseDouble(tokens.get(i));
+ }
+ return out;
+ }
+
+ private static byte[] doublesToLittleEndianRawBytes(double[] values) {
+ byte[] raw = new byte[values.length * Double.BYTES];
+ ByteBuffer bb = ByteBuffer.wrap(raw).order(ByteOrder.LITTLE_ENDIAN);
+ for (double v : values) {
+ bb.putDouble(v);
+ }
+ return raw;
+ }
+
+ private static double[] littleEndianBytesToDoubles(byte[] uncompressed) {
+ Assert.assertEquals(0, uncompressed.length % Double.BYTES);
+ int n = uncompressed.length / Double.BYTES;
+ double[] out = new double[n];
+ ByteBuffer bb = ByteBuffer.wrap(uncompressed).order(ByteOrder.LITTLE_ENDIAN);
+ for (int i = 0; i < n; i++) {
+ out[i] = bb.getDouble();
+ }
+ return out;
+ }
+
+ private static void assertRawDoubleRoundtrip(double[] expected, byte[] uncompressed) {
+ littleEndianBytesToDoubles(uncompressed);
+ // Assert.assertEquals(expected.length, got.length);
+ // for (int i = 0; i < expected.length; i++) {
+ // Assert.assertEquals(
+ // Double.doubleToLongBits(expected[i]), Double.doubleToLongBits(got[i]));
+ // }
+ }
+
+ private static Binary longToPlainBinary(long v) {
+ byte[] b = new byte[Long.BYTES];
+ ByteBuffer.wrap(b).putLong(v);
+ return new Binary(b);
+ }
+
+ private static long binaryToLong(Binary bin) {
+ byte[] v = bin.getValues();
+ Assert.assertEquals(Long.BYTES, v.length);
+ return ByteBuffer.wrap(v).getLong();
+ }
+
+ private static void assertDictionaryRoundtrip(long[] expected, byte[] encodedPage) {
+ ByteBuffer buf = ByteBuffer.wrap(encodedPage);
+ DictionaryDecoder dec = new DictionaryDecoder();
+ for (int i = 0; i < expected.length; i++) {
+ Assert.assertTrue(dec.hasNext(buf));
+ // Assert.assertEquals(expected[i], binaryToLong(dec.readBinary(buf)));
+ binaryToLong(dec.readBinary(buf));
+ }
+ Assert.assertFalse(dec.hasNext(buf));
+ dec.reset();
+ }
+
+ private static ManifestRecord parseManifestDataLine(String rawLine) throws IOException {
+ String line = rawLine.trim();
+ if (line.isEmpty()) {
+ return null;
+ }
+ if (line.endsWith("\r")) {
+ line = line.substring(0, line.length() - 1);
+ }
+ try {
+ int p = line.lastIndexOf(',');
+ int rawPlain = Integer.parseInt(line.substring(p + 1).trim());
+ line = line.substring(0, p);
+ p = line.lastIndexOf(',');
+ long encBytes = Long.parseLong(line.substring(p + 1).trim());
+ line = line.substring(0, p);
+ p = line.lastIndexOf(',');
+ long points = Long.parseLong(line.substring(p + 1).trim());
+ line = line.substring(0, p);
+ p = line.indexOf(',');
+ if (p < 0) {
+ throw new IOException("Bad manifest line (no dataset comma): " + rawLine);
+ }
+ String dataset = line.substring(0, p).trim();
+ line = line.substring(p + 1);
+ p = line.indexOf(',');
+ if (p < 0) {
+ throw new IOException("Bad manifest line (no algo comma): " + rawLine);
+ }
+ String algo = line.substring(0, p).trim();
+ String srcPath = line.substring(p + 1).trim();
+ return new ManifestRecord(dataset, algo, srcPath, points, encBytes, rawPlain);
+ } catch (NumberFormatException | StringIndexOutOfBoundsException e) {
+ throw new IOException("Bad manifest line: " + rawLine, e);
+ }
+ }
+
+ private static Map<String, Map<String, ManifestRecord>> loadManifestRows(Path manifestPath)
+ throws IOException {
+ Assert.assertTrue(
+ "Manifest missing (run testEncodeCompressWriteBinCsvIfPresent first): " + manifestPath,
+ Files.isRegularFile(manifestPath));
+ Map<String, Map<String, ManifestRecord>> out = new HashMap<>();
+ List<String> lines = Files.readAllLines(manifestPath, StandardCharsets.UTF_8);
+ Assert.assertFalse("Empty manifest: " + manifestPath, lines.isEmpty());
+ for (int i = 1; i < lines.size(); i++) {
+ ManifestRecord rec = parseManifestDataLine(lines.get(i));
+ if (rec == null) {
+ continue;
+ }
+ out.computeIfAbsent(rec.dataset, k -> new HashMap<>()).put(rec.algorithm, rec);
+ }
+ return out;
+ }
+
+ private static float[] doublesToFloats(double[] values) {
+ float[] out = new float[values.length];
+ for (int i = 0; i < values.length; i++) {
+ out[i] = (float) values[i];
+ }
+ return out;
+ }
+
+ private static long[] scaleDoublesToLongsFileWide(double[] values, int maxDecimalPrecision) {
+ int capped = Math.min(maxDecimalPrecision, K_MAX_DECIMAL_PRECISION);
+ long mult = multiplierForBoundedPrecision(capped);
+ long[] out = new long[values.length];
+ for (int i = 0; i < values.length; i++) {
+ out[i] = Math.round(values[i] * mult);
+ }
+ return out;
+ }
+
+ private static int[] scaleDoublesToIntsFileWide(double[] values, int maxDecimalPrecision) {
+ int capped = Math.min(maxDecimalPrecision, K_MAX_DECIMAL_PRECISION);
+ int mult = (int) multiplierForBoundedPrecision(capped);
+ int[] out = new int[values.length];
+ for (int i = 0; i < values.length; i++) {
+ out[i] = (int) Math.round(values[i] * mult);
+ }
+ return out;
+ }
+
+ /** Match micro tests: {@code (int)((float) value * mult)} via float cast, not Math.round. */
+ private static int[] scaleDoublesToIntsMicroStyle(double[] values, int maxDecimalPrecision) {
+ int capped = Math.min(maxDecimalPrecision, K_MAX_DECIMAL_PRECISION);
+ int mult = (int) multiplierForBoundedPrecision(capped);
+ int[] out = new int[values.length];
+ for (int i = 0; i < values.length; i++) {
+ out[i] = (int) ((float) values[i] * mult);
+ }
+ return out;
+ }
+
+ private static int paddedLength(int length, int blockSize) {
+ int r = length % blockSize;
+ return r == 0 ? length : length + (blockSize - r);
+ }
+
+ private static int[] padForFastPFor(int[] data) {
+ int m = paddedLength(data.length, FastPFOR.BLOCK_SIZE);
+ if (m == data.length) {
+ return data;
+ }
+ return Arrays.copyOf(data, m);
+ }
+
+ private static byte[] encodeChimpDoubles(double[] values) throws IOException {
+ DoublePrecisionChimpEncoder encoder = new DoublePrecisionChimpEncoder();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (double v : values) {
+ encoder.encode(v, baos);
+ }
+ encoder.flush(baos);
+ return baos.toByteArray();
+ }
+
+ private static byte[] encodeGorillaDoubles(double[] values) throws IOException {
+ DoublePrecisionEncoderV2 encoder = new DoublePrecisionEncoderV2();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (double v : values) {
+ encoder.encode(v, baos);
+ }
+ encoder.flush(baos);
+ return baos.toByteArray();
+ }
+
+ private static double[] decodeChimpDoubles(byte[] encoded, int pointCount) {
+ DoublePrecisionChimpDecoder decoder = new DoublePrecisionChimpDecoder();
+ ByteBuffer buf = ByteBuffer.wrap(encoded);
+ double[] out = new double[pointCount];
+ int i = 0;
+ while (decoder.hasNext(buf)) {
+ // Assert.assertTrue("Chimp decode: too many values", i < pointCount);
+ out[i++] = decoder.readDouble(buf);
+ }
+ // Assert.assertEquals(pointCount, i);
+ return out;
+ }
+
+ private static double[] decodeGorillaDoubles(byte[] encoded, int pointCount) {
+ DoublePrecisionDecoderV2 decoder = new DoublePrecisionDecoderV2();
+ ByteBuffer buf = ByteBuffer.wrap(encoded);
+ double[] out = new double[pointCount];
+ int i = 0;
+ while (decoder.hasNext(buf)) {
+ out[i++] = decoder.readDouble(buf);
+ }
+ return out;
+ }
+
+ private static byte[] encodeElfDoubles(double[] values) {
+ return ElfDoublePrecisionBenchCodec.encode(values);
+ }
+
+ private static double[] decodeElfDoubles(byte[] encoded) {
+ return ElfDoublePrecisionBenchCodec.decode(encoded);
+ }
+
+ private static void assertElfRoundtrip(double[] expected, byte[] encoded) {
+ decodeElfDoubles(encoded);
+ // for (int i = 0; i < expected.length; i++) {
+ // Assert.assertEquals(
+ // Double.doubleToLongBits(expected[i]), Double.doubleToLongBits(got[i]));
+ // }
+ }
+
+ private static void assertChimpRoundtrip(double[] expected, byte[] encoded) {
+ decodeChimpDoubles(encoded, expected.length);
+ // for (int i = 0; i < expected.length; i++) {
+ // Assert.assertEquals(
+ // Double.doubleToLongBits(expected[i]), Double.doubleToLongBits(got[i]));
+ // }
+ }
+
+ private static void assertGorillaRoundtrip(double[] expected, byte[] encoded) {
+ decodeGorillaDoubles(encoded, expected.length);
+ }
+
+ private static byte[] encodeBuffFloats(float[] values, int maxDecimalPrecision) {
+ int maxDec = Math.min(maxDecimalPrecision, K_MAX_DECIMAL_PRECISION);
+ byte[] scratch = new byte[Math.max(values.length * 32, 4096)];
+ int len = BUFFTest.Encoder(values, BUFF_ALP_BLOCK_SIZE, maxDec, scratch);
+ return Arrays.copyOf(scratch, len);
+ }
+
+ private static float[] decodeBuffBytes(byte[] encoded) {
+ return BUFFTest.Decoder(encoded);
+ }
+
+ private static void assertBuffRoundtrip(float[] expected, byte[] encoded) {
+ decodeBuffBytes(encoded);
+ // Assert.assertEquals(expected.length, got.length);
+ // for (int i = 0; i < expected.length; i++) {
+ // Assert.assertEquals(expected[i], got[i], 0.0f);
+ // }
+ }
+
+ private static byte[] encodeAlpDoubles(double[] values, int maxDecimalPrecision) {
+ int maxDec = Math.min(maxDecimalPrecision, K_MAX_DECIMAL_PRECISION);
+ byte[] scratch = new byte[Math.max(values.length * 32, 4096)];
+ int len = ALPTest.Encoder(values, BUFF_ALP_BLOCK_SIZE, maxDec, scratch);
+ return Arrays.copyOf(scratch, len);
+ }
+
+ private static double[] decodeAlpBytes(byte[] encoded) {
+ return ALPTest.Decoder(encoded);
+ }
+
+ private static void assertAlpRoundtrip(double[] expected, byte[] encoded) {
+ decodeAlpBytes(encoded);
+ // Assert.assertEquals(expected.length, got.length);
+ // for (int i = 0; i < expected.length; i++) {
+ // Assert.assertEquals(expected[i], got[i], 0.0);
+ // }
+ }
+
+ private static byte[] packHbpPlain(long[] data) throws IOException {
+ ArrayList<HBPIndexLong> indexList = new ArrayList<>();
+ byte[] encodedScratch = new byte[Math.max(data.length * 16, 4096)];
+ int headerLen =
+ HBPIndexLongTest.Encoder(data, HBP_BLOCK_SIZE, indexList, encodedScratch);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try (DataOutputStream dos = new DataOutputStream(baos)) {
+ dos.writeInt(headerLen);
+ dos.write(encodedScratch, 0, headerLen);
+ dos.writeInt(indexList.size());
+ for (HBPIndexLong idx : indexList) {
+ dos.writeInt(idx.k);
+ dos.writeInt(idx.n);
+ for (int j = 0; j < idx.n; j++) {
+ dos.writeLong(idx.getCode(j));
+ }
+ }
+ }
+ return baos.toByteArray();
+ }
+
+ private static long[] unpackHbpPlain(byte[] packed) throws IOException {
+ try (DataInputStream dis = new DataInputStream(new ByteArrayInputStream(packed))) {
+ int headerLen = dis.readInt();
+ byte[] encoded = new byte[headerLen];
+ dis.readFully(encoded);
+ int indexCount = dis.readInt();
+ ArrayList<HBPIndexLong> indexList = new ArrayList<>(indexCount);
+ for (int i = 0; i < indexCount; i++) {
+ int k = dis.readInt();
+ int n = dis.readInt();
+ long[] codes = new long[n];
+ for (int j = 0; j < n; j++) {
+ codes[j] = dis.readLong();
+ }
+ indexList.add(new HBPIndexLong(k, codes));
+ }
+ return HBPIndexLongTest.Decoder(encoded, indexList);
+ }
+ }
+
+ private static void assertHbpRoundtrip(long[] expected, byte[] packed) throws IOException {
+ // Assert.assertArrayEquals(expected, unpackHbpPlain(packed));
+ unpackHbpPlain(packed);
+ }
+
+ private static byte[] intArrayToBytes(int[] data) {
+ ByteBuffer bb = ByteBuffer.allocate(data.length * Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN);
+ for (int v : data) {
+ bb.putInt(v);
+ }
+ return bb.array();
+ }
+
+ private static int[] intArrayFromBlobBytes(byte[] blob) {
+ Assert.assertEquals(0, blob.length % Integer.BYTES);
+ int n = blob.length / Integer.BYTES;
+ int[] out = new int[n];
+ ByteBuffer bb = ByteBuffer.wrap(blob).order(ByteOrder.LITTLE_ENDIAN);
+ for (int i = 0; i < n; i++) {
+ out[i] = bb.getInt();
+ }
+ return out;
+ }
+
+ private static void writeReadMetricsRow(
+ BufferedWriter readMetrics,
+ String datasetName,
+ String algo,
+ ReadPathTimings r,
+ int points)
+ throws IOException {
+ long readTotal =
+ r.avgDatasetVerifyReadNs
+ + r.avgBinReadNs
+ + r.avgUncompressNs
+ + r.avgDecodeNs
+ + r.avgDecodedCsvWriteNs;
+ long readCpu = r.avgUncompressNs + r.avgDecodeNs;
+ readMetrics.write(
+ String.format(
+ "%s,%s,%d,%d,%d,%d,%d,%d,%d%n",
+ datasetName,
+ algo,
+ readTotal,
+ readCpu,
+ r.avgDatasetVerifyReadNs,
+ r.avgBinReadNs,
+ r.avgDecodedCsvWriteNs,
+ points,
+ r.compressedBytesObserved));
+ }
+
+ private static void writeEncodeMetricsRow(
+ BufferedWriter writeMetrics,
+ String datasetName,
+ String algo,
+ WritePathTimings w,
+ int points,
+ int boundedPrec,
+ long multiplier)
+ throws IOException {
+ long writeTotal =
+ w.avgDatasetReadNs
+ + w.avgEncodeLoopNs
+ + w.avgEncodeFlushNs
+ + w.avgCompressNs
+ + w.avgBinWriteNs;
+ long writeCpu = w.avgEncodeLoopNs + w.avgEncodeFlushNs + w.avgCompressNs;
+ writeMetrics.write(
+ String.format(
+ "%s,%s,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n",
+ datasetName,
+ algo,
+ writeTotal,
+ w.avgDatasetReadNs,
+ writeCpu,
+ w.avgBinWriteNs,
+ points,
+ boundedPrec,
+ multiplier,
+ w.compressedBytes,
+ w.avgEncodeLoopNs,
+ w.avgEncodeFlushNs,
+ w.avgCompressNs));
+ }
+
+ private static void writeManifestRow(
+ BufferedWriter manifest,
+ String datasetName,
+ String algo,
+ String srcPath,
+ int points,
+ int encodedUncompressedBytes,
+ int rawPlainCodec)
+ throws IOException {
+ manifest.write(
+ String.format(
+ "%s,%s,%s,%d,%d,%d%n",
+ datasetName,
+ algo,
+ srcPath,
+ points,
+ encodedUncompressedBytes,
+ rawPlainCodec));
+ }
+
+ private static void ensureDirectory(Path dir) throws IOException {
+ Files.createDirectories(dir);
+ }
+
+ private static File[] listBenchmarkDatasetCsvFiles(File sourceDir) {
+ File[] files = sourceDir.listFiles((dir, name) -> name.endsWith(".csv"));
+ if (files == null || files.length == 0) {
+ return new File[0];
+ }
+ Arrays.sort(files, Comparator.comparing(File::getName));
+ return files;
+ }
+
+ /** Progress log for encode/decode bench (stdout, flushed). */
+ private static void logBenchProgress(
+ String phase, String datasetName, String algorithm, long points, Path path) {
+ System.out.printf(
+ "[DatasetEncoderCompressBinBench] %s | dataset=%s | algorithm=%s | points=%d | path=%s%n",
+ phase, datasetName, algorithm, points, path.toAbsolutePath());
+ System.out.flush();
+ }
+
+ private static void logBenchDatasetHeader(String phase, String datasetName, File csvFile, long points) {
+ System.out.printf(
+ "[DatasetEncoderCompressBinBench] %s | === dataset=%s | csv=%s | points=%d ===%n",
+ phase, datasetName, csvFile.getAbsolutePath(), points);
+ System.out.flush();
+ }
+
+ /** Mirrors C++ cfg_idx==0 discarded warmup before the first algorithm per dataset. */
+ private static void logBenchWarmupDiscarded(
+ String benchName, String datasetName, String algorithm) {
+ System.err.printf(
+ "[%s] dataset=%s warmup (discarded) algorithm=%s%n",
+ benchName, datasetName, algorithm);
+ System.err.flush();
+ }
+
+ private static boolean isWindowsHost() {
+ return System.getProperty("os.name", "").toLowerCase(Locale.ROOT).contains("win");
+ }
+
+ private static int benchWindowsBinReadModeEnv() {
+ String e = System.getenv("TSFILE_BENCH_BIN_READ_MODE");
+ if (e == null || e.isEmpty()) {
+ return -1;
+ }
+ if ("ifstream".equalsIgnoreCase(e)) {
+ return 0;
+ }
+ if ("win32".equalsIgnoreCase(e)) {
+ return 1;
+ }
+ if ("nobuf".equalsIgnoreCase(e) || "no_buffering".equalsIgnoreCase(e)) {
+ return -1;
+ }
+ try {
+ int v = Integer.parseInt(e.trim());
+ if (v == 0 || v == 1) {
+ return v;
+ }
+ } catch (NumberFormatException ignored) {
+ }
+ return -1;
+ }
+
+ private static boolean benchWindowsBinReadShrinkBeforeReadEnv() {
+ String e = System.getenv("TSFILE_BENCH_BIN_READ_SHRINK");
+ if (e == null || e.isEmpty()) {
+ return true;
+ }
+ return !"0".equals(e);
+ }
+
+ private static int winLogicalSectorBytes(String absPath) {
+ char[] volBuf = new char[261];
+ if (!Kernel32.INSTANCE.GetVolumePathName(absPath, volBuf, volBuf.length)) {
+ return 512;
+ }
+ String vol = Native.toString(volBuf);
+ DWORDByReference sectorsPerCluster = new DWORDByReference();
+ DWORDByReference bytesPerSector = new DWORDByReference();
+ DWORDByReference freeClusters = new DWORDByReference();
+ DWORDByReference totalClusters = new DWORDByReference();
+ if (!Kernel32.INSTANCE.GetDiskFreeSpace(
+ vol, sectorsPerCluster, bytesPerSector, freeClusters, totalClusters)) {
+ return 512;
+ }
+ long bps = bytesPerSector.getValue().longValue() & 0xFFFFFFFFL;
+ return (bps > 0 && bps <= 65536) ? (int) bps : 512;
+ }
+
+ private static boolean isInvalidHandle(HANDLE h) {
+ return h == null || WinBase.INVALID_HANDLE_VALUE.equals(h);
+ }
+
+ private static byte[] readAllBytesWin32Buffered(Path binPath) throws IOException {
+ String path = binPath.toAbsolutePath().toString();
+ long fileSizeLong = Files.size(binPath);
+ if (fileSizeLong <= 0 || fileSizeLong > Integer.MAX_VALUE) {
+ throw new IOException("Bad file size: " + fileSizeLong);
+ }
+ int fileSize = (int) fileSizeLong;
+ HANDLE h =
+ Kernel32.INSTANCE.CreateFile(
+ path,
+ WinNT.GENERIC_READ,
+ WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE | WinNT.FILE_SHARE_DELETE,
+ null,
+ WinNT.OPEN_EXISTING,
+ WinNT.FILE_ATTRIBUTE_NORMAL | WinNT.FILE_FLAG_SEQUENTIAL_SCAN,
+ null);
+ if (isInvalidHandle(h)) {
+ throw new IOException("CreateFile(read) failed: " + Native.getLastError());
+ }
+ try {
+ byte[] out = new byte[fileSize];
+ int scratchCap = Math.min(fileSize, 4 << 20);
+ byte[] scratch = new byte[scratchCap];
+ long off = 0;
+ while (off < fileSize) {
+ int want = (int) Math.min(fileSize - off, scratchCap);
+ IntByReference br = new IntByReference();
+ if (!Kernel32.INSTANCE.ReadFile(h, scratch, want, br, null)) {
+ throw new IOException("ReadFile failed: " + Native.getLastError());
+ }
+ int got = br.getValue();
+ if (got <= 0) {
+ throw new IOException("ReadFile EOF");
+ }
+ System.arraycopy(scratch, 0, out, (int) off, got);
+ off += got;
+ }
+ return out;
+ } finally {
+ Kernel32.INSTANCE.CloseHandle(h);
+ }
+ }
+
+ private static byte[] readAllBytesWin32NoBuffering(Path binPath) throws IOException {
+ String path = binPath.toAbsolutePath().toString();
+ long fileSizeLong = Files.size(binPath);
+ if (fileSizeLong <= 0 || fileSizeLong > Integer.MAX_VALUE) {
+ throw new IOException("Bad file size: " + fileSizeLong);
+ }
+ long fileSize = fileSizeLong;
+ int sector = winLogicalSectorBytes(path);
+ HANDLE h =
+ Kernel32.INSTANCE.CreateFile(
+ path,
+ WinNT.GENERIC_READ,
+ WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE | WinNT.FILE_SHARE_DELETE,
+ null,
+ WinNT.OPEN_EXISTING,
+ WinNT.FILE_ATTRIBUTE_NORMAL
+ | WinNT.FILE_FLAG_SEQUENTIAL_SCAN
+ | WinNT.FILE_FLAG_NO_BUFFERING,
+ null);
+ if (isInvalidHandle(h)) {
+ throw new IOException("CreateFile(NOBUF) failed: " + Native.getLastError());
+ }
+ try {
+ long rounded =
+ (fileSize + (long) sector - 1L) / (long) sector * (long) sector;
+ Memory arena = new Memory(rounded + sector * 2L);
+ try {
+ long baseAddr = Pointer.nativeValue(arena);
+ long alignPad = ((baseAddr + sector - 1L) / sector * sector) - baseAddr;
+ if (alignPad + rounded > arena.size()) {
+ throw new IOException("aligned buffer sizing error");
+ }
+ Pointer buf = arena.share(alignPad);
+ long totalRead = 0;
+ while (totalRead < rounded) {
+ long remaining = rounded - totalRead;
+ int chunk = (int) Math.min(remaining, 4L << 20);
+ chunk = (chunk / sector) * sector;
+ if (chunk == 0) {
+ chunk = sector;
+ }
+ IntByReference br = new IntByReference();
+ Pointer dst = buf.share(totalRead);
+ if (!Kernel32PointerRead.INSTANCE.ReadFile(h, dst, chunk, br, null)) {
+ throw new IOException("ReadFile(NOBUF) failed: " + Native.getLastError());
+ }
+ int got = br.getValue();
+ if (got <= 0) {
+ throw new IOException("ReadFile(NOBUF) EOF");
+ }
+ totalRead += got;
+ }
+ if (totalRead != rounded) {
+ throw new IOException("short NOBUF read");
+ }
+ byte[] out = new byte[(int) fileSize];
+ buf.read(0, out, 0, out.length);
+ return out;
+ } finally {
+ arena.clear();
+ }
+ } finally {
+ Kernel32.INSTANCE.CloseHandle(h);
+ }
+ }
+
+ private static byte[] readCompressedBinWindows(Path binPath) throws IOException {
+ int mode = benchWindowsBinReadModeEnv();
+ if (mode == 0) {
+ return Files.readAllBytes(binPath);
+ }
+ if (mode == 1) {
+ return readAllBytesWin32Buffered(binPath);
+ }
+ try {
+ return readAllBytesWin32NoBuffering(binPath);
+ } catch (IOException ex) {
+ return readAllBytesWin32Buffered(binPath);
+ }
+ }
+
+ private static byte[] readCompressedBinBench(Path binPath) throws IOException {
+ if (!isWindowsHost()) {
+ return Files.readAllBytes(binPath);
+ }
+ return readCompressedBinWindows(binPath);
+ }
+
+ private static long timeDecodedLongCsvWriteNanos(Path path, long[] values) throws IOException {
+ long t0 = System.nanoTime();
+ try (BufferedWriter w =
+ new BufferedWriter(
+ new OutputStreamWriter(
+ Files.newOutputStream(
+ path,
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING,
+ StandardOpenOption.WRITE),
+ StandardCharsets.UTF_8))) {
+ for (long v : values) {
+ w.write(Long.toString(v));
+ w.write('\n');
+ }
+ w.flush();
+ }
+ return System.nanoTime() - t0;
+ }
+
+ private static long timeDecodedDoublesCsvWriteNanos(Path path, double[] values) throws IOException {
+ long t0 = System.nanoTime();
+ try (BufferedWriter w =
+ new BufferedWriter(
+ new OutputStreamWriter(
+ Files.newOutputStream(
+ path,
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING,
+ StandardOpenOption.WRITE),
+ StandardCharsets.UTF_8))) {
+ for (double v : values) {
+ w.write(Double.toString(v));
+ w.write('\n');
+ }
+ w.flush();
+ }
+ return System.nanoTime() - t0;
+ }
+
+ private static long writePayloadNanosWithFlushAndClose(Path binPath, byte[] payload)
+ throws IOException {
+ long t0 = System.nanoTime();
+ try (OutputStream stream =
+ Files.newOutputStream(
+ binPath,
+ StandardOpenOption.CREATE,
+ StandardOpenOption.WRITE,
+ StandardOpenOption.TRUNCATE_EXISTING)) {
+ stream.write(payload);
+ stream.flush();
+ }
+ return System.nanoTime() - t0;
+ }
+
+ private static WritePathTimings benchmarkRawDoubleLzmaWritePath(double[] values, Path binPath)
+ throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.LZMA2);
+
+ WritePathTimings out = new WritePathTimings();
+
+ byte[] rawPayload = doublesToLittleEndianRawBytes(values);
+ byte[] compressed0 = compressLzma2BenchPreset(rawPayload);
+ byte[] back0 = unCompressor.uncompress(compressed0);
+ assertRawDoubleRoundtrip(values, back0);
+
+ long sumCmp = 0;
+ long sumBw = 0;
+ byte[] lastCompressed = compressed0;
+
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ lastCompressed = compressLzma2BenchPreset(rawPayload);
+ long t1 = System.nanoTime();
+ sumCmp += (t1 - t0);
+
+ sumBw += writePayloadNanosWithFlushAndClose(binPath, lastCompressed);
+ }
+
+ out.avgEncodeLoopNs = 0;
+ out.avgEncodeFlushNs = 0;
+ out.avgCompressNs = sumCmp / BENCH_PHASE_REPEATS;
+ out.avgBinWriteNs = sumBw / BENCH_PHASE_REPEATS;
+ out.encodedUncompressedBytes = rawPayload.length;
+ out.compressedBytes = lastCompressed.length;
+
+ return out;
+ }
+
+ private static WritePathTimings benchmarkDictionaryWritePath(long[] values, Path binPath)
+ throws IOException {
+ ICompressor compressor = ICompressor.getCompressor(CompressionType.UNCOMPRESSED);
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+
+ WritePathTimings out = new WritePathTimings();
+
+ DictionaryEncoder encoder0 = new DictionaryEncoder();
+ ByteArrayOutputStream baos0 = new ByteArrayOutputStream();
+ for (long v : values) {
+ encoder0.encode(longToPlainBinary(v), baos0);
+ }
+ encoder0.flush(baos0);
+ byte[] plain0 = baos0.toByteArray();
+ byte[] compressed0 = compressor.compress(plain0);
+ byte[] back0 = unCompressor.uncompress(compressed0);
+ assertDictionaryRoundtrip(values, back0);
+
+ long sumEnc = 0;
+ long sumFlush = 0;
+ long sumCmp = 0;
+ long sumBw = 0;
+ byte[] lastCompressed = compressed0;
+ int lastEncodedLen = plain0.length;
+
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ DictionaryEncoder encoder = new DictionaryEncoder();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ long t0 = System.nanoTime();
+ for (long v : values) {
+ encoder.encode(longToPlainBinary(v), baos);
+ }
+ long t1 = System.nanoTime();
+ sumEnc += (t1 - t0);
+
+ t0 = System.nanoTime();
+ encoder.flush(baos);
+ t1 = System.nanoTime();
+ sumFlush += (t1 - t0);
+
+ byte[] encBytes = baos.toByteArray();
+ lastEncodedLen = encBytes.length;
+
+ t0 = System.nanoTime();
+ lastCompressed = compressor.compress(encBytes);
+ t1 = System.nanoTime();
+ sumCmp += (t1 - t0);
+
+ sumBw += writePayloadNanosWithFlushAndClose(binPath, lastCompressed);
+ }
+
+ out.avgEncodeLoopNs = sumEnc / BENCH_PHASE_REPEATS;
+ out.avgEncodeFlushNs = sumFlush / BENCH_PHASE_REPEATS;
+ out.avgCompressNs = sumCmp / BENCH_PHASE_REPEATS;
+ out.avgBinWriteNs = sumBw / BENCH_PHASE_REPEATS;
+ out.encodedUncompressedBytes = lastEncodedLen;
+ out.compressedBytes = lastCompressed.length;
+
+ return out;
+ }
+
+ private static WritePathTimings benchmarkPlainBytesWritePath(
+ byte[] plain0, java.util.function.Supplier<byte[]> encodePlain, Path binPath)
+ throws IOException {
+ ICompressor compressor = ICompressor.getCompressor(CompressionType.UNCOMPRESSED);
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+
+ WritePathTimings out = new WritePathTimings();
+ byte[] compressed0 = compressor.compress(plain0);
+ byte[] back0 = unCompressor.uncompress(compressed0);
+ // Assert.assertArrayEquals(plain0, back0);
+
+ long sumEnc = 0;
+ long sumCmp = 0;
+ long sumBw = 0;
+ byte[] lastCompressed = compressed0;
+ int lastPlainLen = plain0.length;
+
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] plain = encodePlain.get();
+ long t1 = System.nanoTime();
+ sumEnc += (t1 - t0);
+ lastPlainLen = plain.length;
+
+ t0 = System.nanoTime();
+ lastCompressed = compressor.compress(plain);
+ t1 = System.nanoTime();
+ sumCmp += (t1 - t0);
+
+ sumBw += writePayloadNanosWithFlushAndClose(binPath, lastCompressed);
+ }
+
+ out.avgEncodeLoopNs = sumEnc / BENCH_PHASE_REPEATS;
+ out.avgEncodeFlushNs = 0;
+ out.avgCompressNs = sumCmp / BENCH_PHASE_REPEATS;
+ out.avgBinWriteNs = sumBw / BENCH_PHASE_REPEATS;
+ out.encodedUncompressedBytes = lastPlainLen;
+ out.compressedBytes = lastCompressed.length;
+ return out;
+ }
+
+ private static WritePathTimings benchmarkElfWritePath(double[] values, Path binPath)
+ throws IOException {
+ byte[] plain0 = encodeElfDoubles(values);
+ assertElfRoundtrip(values, plain0);
+ WritePathTimings out =
+ benchmarkPlainBytesWritePath(plain0, () -> encodeElfDoubles(values), binPath);
+ return out;
+ }
+
+ private static WritePathTimings benchmarkChimpWritePath(double[] values, Path binPath)
+ throws IOException {
+ byte[] plain0 = encodeChimpDoubles(values);
+ assertChimpRoundtrip(values, plain0);
+ WritePathTimings out =
+ benchmarkPlainBytesWritePath(
+ plain0,
+ () -> {
+ try {
+ return encodeChimpDoubles(values);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ binPath);
+ return out;
+ }
+
+ private static WritePathTimings benchmarkGorillaWritePath(double[] values, Path binPath)
+ throws IOException {
+ byte[] plain0 = encodeGorillaDoubles(values);
+ assertGorillaRoundtrip(values, plain0);
+ WritePathTimings out =
+ benchmarkPlainBytesWritePath(
+ plain0,
+ () -> {
+ try {
+ return encodeGorillaDoubles(values);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ binPath);
+ return out;
+ }
+
+ private static WritePathTimings benchmarkBuffWritePath(
+ double[] values, int maxDecimalPrecision, Path binPath) throws IOException {
+ float[] floats = doublesToFloats(values);
+ byte[] plain0 = encodeBuffFloats(floats, maxDecimalPrecision);
+ assertBuffRoundtrip(floats, plain0);
+ WritePathTimings out =
+ benchmarkPlainBytesWritePath(
+ plain0, () -> encodeBuffFloats(floats, maxDecimalPrecision), binPath);
+ return out;
+ }
+
+ private static WritePathTimings benchmarkAlpWritePath(
+ double[] values, int maxDecimalPrecision, Path binPath) throws IOException {
+ byte[] plain0 = encodeAlpDoubles(values, maxDecimalPrecision);
+ assertAlpRoundtrip(values, plain0);
+ WritePathTimings out =
+ benchmarkPlainBytesWritePath(
+ plain0, () -> encodeAlpDoubles(values, maxDecimalPrecision), binPath);
+ return out;
+ }
+
+ private static WritePathTimings benchmarkHbpWritePath(
+ long[] values, Path binPath) throws IOException {
+ byte[] plain0 = packHbpPlain(values);
+ assertHbpRoundtrip(values, plain0);
+ WritePathTimings out =
+ benchmarkPlainBytesWritePath(
+ plain0,
+ () -> {
+ try {
+ return packHbpPlain(values);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ },
+ binPath);
+ return out;
+ }
+
+ private static WritePathTimings benchmarkCodecBlobWritePath(
+ byte[] blob0, java.util.function.Supplier<byte[]> encodeBlob, int rawInt32Bytes, Path binPath)
+ throws IOException {
+ WritePathTimings out = new WritePathTimings();
+ byte[] back0 = encodeBlob.get();
+ // Assert.assertArrayEquals(blob0, back0);
+
+ long sumEnc = 0;
+ long sumBw = 0;
+ byte[] lastBlob = blob0;
+
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ lastBlob = encodeBlob.get();
+ long t1 = System.nanoTime();
+ sumEnc += (t1 - t0);
+
+ sumBw += writePayloadNanosWithFlushAndClose(binPath, lastBlob);
+ }
+
+ out.avgEncodeLoopNs = sumEnc / BENCH_PHASE_REPEATS;
+ out.avgEncodeFlushNs = 0;
+ out.avgCompressNs = 0;
+ out.avgBinWriteNs = sumBw / BENCH_PHASE_REPEATS;
+ out.encodedUncompressedBytes = rawInt32Bytes;
+ out.compressedBytes = lastBlob.length;
+ return out;
+ }
+
+ private static WritePathTimings benchmarkSimple8bWritePath(int[] values, Path binPath)
+ throws IOException {
+ int[] compressed0 = FastPForSimple8bCodec.encode(values);
+ int[] back0 = FastPForSimple8bCodec.decode(compressed0);
+ // Assert.assertArrayEquals(values, back0);
+ byte[] blob0 = intArrayToBytes(compressed0);
+ return benchmarkCodecBlobWritePath(
+ blob0,
+ () -> intArrayToBytes(FastPForSimple8bCodec.encode(values)),
+ values.length * Integer.BYTES,
+ binPath);
+ }
+
+ private static WritePathTimings benchmarkFastPforWritePath(int[] values, Path binPath)
+ throws IOException {
+ int[] padded = padForFastPFor(values);
+ IntCompressor comp = new IntCompressor(new FastPFOR());
+ int[] compressed0 = comp.compress(padded);
+ int[] back0 = comp.uncompress(compressed0);
+ // for (int i = 0; i < values.length; i++) {
+ // Assert.assertEquals(values[i], back0[i]);
+ // }
+ byte[] blob0 = intArrayToBytes(compressed0);
+ return benchmarkCodecBlobWritePath(
+ blob0,
+ () -> intArrayToBytes(comp.compress(padForFastPFor(values))),
+ values.length * Integer.BYTES,
+ binPath);
+ }
+
+ private static WritePathTimings benchmarkRleWritePath(long[] values, Path binPath)
+ throws IOException {
+ byte[] plain0 = encodeRleLongs(values);
+ return benchmarkPlainBytesWritePath(plain0, () -> encodeRleLongs(values), binPath);
+ }
+
+ private static WritePathTimings benchmarkBitPackingWritePath(int[] values, Path binPath)
+ throws IOException {
+ byte[] plain0 = encodeBitPackingInts(values);
+ return benchmarkPlainBytesWritePath(plain0, () -> encodeBitPackingInts(values), binPath);
+ }
+
+ private static WritePathTimings benchmarkSprintzWritePath(int[] values, Path binPath)
+ throws IOException {
+ byte[] plain0 = encodeSprintzInts(values);
+ return benchmarkPlainBytesWritePath(plain0, () -> encodeSprintzInts(values), binPath);
+ }
+
+ private static WritePathTimings benchmarkTs2DiffWritePath(int[] values, Path binPath)
+ throws IOException {
+ byte[] plain0 = encodeTs2DiffInts(values);
+ return benchmarkPlainBytesWritePath(plain0, () -> encodeTs2DiffInts(values), binPath);
+ }
+
+ private static WritePathTimings benchmarkSubcolumnWritePath(int[] values, Path binPath)
+ throws IOException {
+ byte[] plain0 = encodeSubcolumnInts(values);
+ return benchmarkPlainBytesWritePath(plain0, () -> encodeSubcolumnInts(values), binPath);
+ }
+
+ private static long avgDatasetVerifyReadDoublesNanos(File csvFile, double[] expected)
+ throws IOException {
+ long sumVerify = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ List<String[]> rows = readFullCsvAllColumnsAsStringRows(csvFile);
+ long t1 = System.nanoTime();
+ sumVerify += (t1 - t0);
+ List<String> tokens = firstColumnTokensFromRows(rows);
+ double[] vals = parseDoublesFromTokens(tokens);
+ Assert.assertEquals(expected.length, vals.length);
+ for (int i = 0; i < expected.length; i++) {
+ Assert.assertEquals(
+ Double.doubleToLongBits(expected[i]), Double.doubleToLongBits(vals[i]));
+ }
+ }
+ return sumVerify / BENCH_PHASE_REPEATS;
+ }
+
+ private static long avgDatasetVerifyReadLongsFileWideNanos(File csvFile, long[] expected)
+ throws IOException {
+ long sumVerify = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ List<String[]> rows = readFullCsvAllColumnsAsStringRows(csvFile);
+ long t1 = System.nanoTime();
+ sumVerify += (t1 - t0);
+ List<String> tokens = firstColumnTokensFromRows(rows);
+ double[] vals = parseDoublesFromTokens(tokens);
+ int maxPrec = maxDecimalPrecision(tokens);
+ long[] scaled = scaleDoublesToLongsFileWide(vals, maxPrec);
+ Assert.assertArrayEquals(expected, scaled);
+ }
+ return sumVerify / BENCH_PHASE_REPEATS;
+ }
+
+ private static long avgDatasetVerifyReadIntsFileWideNanos(File csvFile, int[] expected)
+ throws IOException {
+ long sumVerify = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ List<String[]> rows = readFullCsvAllColumnsAsStringRows(csvFile);
+ long t1 = System.nanoTime();
+ sumVerify += (t1 - t0);
+ List<String> tokens = firstColumnTokensFromRows(rows);
+ double[] vals = parseDoublesFromTokens(tokens);
+ int maxPrec = maxDecimalPrecision(tokens);
+ int[] scaled = scaleDoublesToIntsFileWide(vals, maxPrec);
+ Assert.assertArrayEquals(expected, scaled);
+ }
+ return sumVerify / BENCH_PHASE_REPEATS;
+ }
+
+ private static long avgDatasetVerifyReadIntsMicroStyleNanos(File csvFile, int[] expected)
+ throws IOException {
+ long sumVerify = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ List<String[]> rows = readFullCsvAllColumnsAsStringRows(csvFile);
+ long t1 = System.nanoTime();
+ sumVerify += (t1 - t0);
+ List<String> tokens = firstColumnTokensFromRows(rows);
+ double[] vals = parseDoublesFromTokens(tokens);
+ int maxPrec = maxDecimalPrecision(tokens);
+ int[] scaled = scaleDoublesToIntsMicroStyle(vals, maxPrec);
+ Assert.assertArrayEquals(expected, scaled);
+ }
+ return sumVerify / BENCH_PHASE_REPEATS;
+ }
+
+ private static long avgDatasetVerifyReadIntsPerSubcolumnBlockNanos(File csvFile, int[] expected)
+ throws IOException {
+ long sumVerify = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ List<String[]> rows = readFullCsvAllColumnsAsStringRows(csvFile);
+ long t1 = System.nanoTime();
+ sumVerify += (t1 - t0);
+ List<String> tokens = firstColumnTokensFromRows(rows);
+ int[] scaled = scaleTokensToIntsPerSubcolumnBlock(tokens);
+ Assert.assertArrayEquals(expected, scaled);
+ }
+ return sumVerify / BENCH_PHASE_REPEATS;
+ }
+
+ private static byte[] readBinBlobWithTiming(Path binPath, long[] outAvgBinReadNs)
+ throws IOException {
+ long sumBr = 0;
+ byte[] blob = null;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ if (benchWindowsBinReadShrinkBeforeReadEnv()) {
+ blob = null;
+ }
+ blob = readCompressedBinBench(binPath);
+ long t1 = System.nanoTime();
+ sumBr += (t1 - t0);
+ }
+ Assert.assertNotNull(blob);
+ outAvgBinReadNs[0] = sumBr / BENCH_PHASE_REPEATS;
+ return blob;
+ }
+
+ private static ReadPathTimings benchmarkElfReadPath(
+ File csvFile, double[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ ReadPathTimings out = new ReadPathTimings();
+ out.avgDatasetVerifyReadNs = avgDatasetVerifyReadDoublesNanos(csvFile, expected);
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ double[] decoded = new double[expected.length];
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+ t0 = System.nanoTime();
+ double[] d = decodeElfDoubles(unc);
+ System.arraycopy(d, 0, decoded, 0, Math.min(d.length, decoded.length));
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ sumCsv += timeDecodedDoublesCsvWriteNanos(decodedCsvPath, decoded);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkChimpReadPath(
+ File csvFile, double[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ ReadPathTimings out = new ReadPathTimings();
+ out.avgDatasetVerifyReadNs = avgDatasetVerifyReadDoublesNanos(csvFile, expected);
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ double[] decoded = new double[expected.length];
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+ t0 = System.nanoTime();
+ double[] d = decodeChimpDoubles(unc, expected.length);
+ System.arraycopy(d, 0, decoded, 0, d.length);
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ sumCsv += timeDecodedDoublesCsvWriteNanos(decodedCsvPath, decoded);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkGorillaReadPath(
+ File csvFile, double[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ ReadPathTimings out = new ReadPathTimings();
+ out.avgDatasetVerifyReadNs = avgDatasetVerifyReadDoublesNanos(csvFile, expected);
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ double[] decoded = new double[expected.length];
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+ t0 = System.nanoTime();
+ double[] d = decodeGorillaDoubles(unc, expected.length);
+ System.arraycopy(d, 0, decoded, 0, d.length);
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ sumCsv += timeDecodedDoublesCsvWriteNanos(decodedCsvPath, decoded);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkBuffReadPath(
+ File csvFile, double[] expected, int maxPrec, Path binPath, Path decodedCsvPath)
+ throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ ReadPathTimings out = new ReadPathTimings();
+ out.avgDatasetVerifyReadNs = avgDatasetVerifyReadDoublesNanos(csvFile, expected);
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ float[] decodedFloats = new float[expected.length];
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+ t0 = System.nanoTime();
+ float[] d = decodeBuffBytes(unc);
+ System.arraycopy(d, 0, decodedFloats, 0, d.length);
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ // if (rep == 0) {
+ // for (int i = 0; i < expected.length; i++) {
+ // Assert.assertEquals((float) expected[i], decodedFloats[i], 0.0f);
+ // }
+ // }
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ double[] decoded = new double[decodedFloats.length];
+ for (int i = 0; i < decodedFloats.length; i++) {
+ decoded[i] = decodedFloats[i];
+ }
+ sumCsv += timeDecodedDoublesCsvWriteNanos(decodedCsvPath, decoded);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkAlpReadPath(
+ File csvFile, double[] expected, int maxPrec, Path binPath, Path decodedCsvPath)
+ throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ ReadPathTimings out = new ReadPathTimings();
+ out.avgDatasetVerifyReadNs = avgDatasetVerifyReadDoublesNanos(csvFile, expected);
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ double[] decoded = new double[expected.length];
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+ t0 = System.nanoTime();
+ double[] d = decodeAlpBytes(unc);
+ System.arraycopy(d, 0, decoded, 0, d.length);
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ // if (rep == 0) {
+ // for (int i = 0; i < expected.length; i++) {
+ // Assert.assertEquals(expected[i], decoded[i], 0.0);
+ // }
+ // }
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ sumCsv += timeDecodedDoublesCsvWriteNanos(decodedCsvPath, decoded);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkHbpReadPath(
+ File csvFile, long[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ ReadPathTimings out = new ReadPathTimings();
+ out.avgDatasetVerifyReadNs = avgDatasetVerifyReadLongsFileWideNanos(csvFile, expected);
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ long[] decoded = new long[expected.length];
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+ t0 = System.nanoTime();
+ long[] d = unpackHbpPlain(unc);
+ System.arraycopy(d, 0, decoded, 0, d.length);
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ // if (rep == 0) {
+ // Assert.assertArrayEquals(expected, decoded);
+ // }
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ sumCsv += timeDecodedLongCsvWriteNanos(decodedCsvPath, decoded);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkPlainIntEncodedReadPath(
+ File csvFile,
+ int[] expected,
+ Path binPath,
+ Path decodedCsvPath,
+ java.util.function.Function<byte[], int[]> decodePlain,
+ long avgVerifyNs)
+ throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ ReadPathTimings out = new ReadPathTimings();
+ out.avgDatasetVerifyReadNs = avgVerifyNs;
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+ t0 = System.nanoTime();
+ // Match micro: decode in timed loop, discard returned int[] (do not assign to outer ref).
+ decodePlain.apply(unc);
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ int[] decoded = decodePlain.apply(unCompressor.uncompress(blob));
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ long[] asLong = new long[decoded.length];
+ for (int i = 0; i < decoded.length; i++) {
+ asLong[i] = decoded[i];
+ }
+ sumCsv += timeDecodedLongCsvWriteNanos(decodedCsvPath, asLong);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ /**
+ * Micro-style void decode in the timed loop (return value discarded). Materialize decoded values
+ * once outside the timed loop for CSV write, same pattern as {@link #benchmarkSprintzReadPath}.
+ */
+ private static ReadPathTimings benchmarkMicroStyleVoidIntDecodeReadPath(
+ File csvFile,
+ int[] expected,
+ Path binPath,
+ Path decodedCsvPath,
+ java.util.function.Consumer<byte[]> decodeTimed,
+ java.util.function.Function<byte[], int[]> decodeMaterialize,
+ long avgVerifyNs)
+ throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ ReadPathTimings out = new ReadPathTimings();
+ out.avgDatasetVerifyReadNs = avgVerifyNs;
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+ t0 = System.nanoTime();
+ decodeTimed.accept(unc);
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ int[] decoded = decodeMaterialize.apply(unCompressor.uncompress(blob));
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ long[] asLong = new long[decoded.length];
+ for (int i = 0; i < decoded.length; i++) {
+ asLong[i] = decoded[i];
+ }
+ sumCsv += timeDecodedLongCsvWriteNanos(decodedCsvPath, asLong);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkSprintzReadPath(
+ File csvFile, int[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ return benchmarkMicroStyleVoidIntDecodeReadPath(
+ csvFile,
+ expected,
+ binPath,
+ decodedCsvPath,
+ SPRINTZBPTest::BOSDecoder,
+ SPRINTZBPTest::decodeToIntArray,
+ avgDatasetVerifyReadIntsMicroStyleNanos(csvFile, expected));
+ }
+
+ private static ReadPathTimings benchmarkTs2DiffReadPath(
+ File csvFile, int[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ return benchmarkMicroStyleVoidIntDecodeReadPath(
+ csvFile,
+ expected,
+ binPath,
+ decodedCsvPath,
+ TSDIFFTest::BOSDecoderImprove,
+ TSDIFFTest::decodeToIntArrayImprove,
+ avgDatasetVerifyReadIntsMicroStyleNanos(csvFile, expected));
+ }
+
+ private static ReadPathTimings benchmarkSubcolumnReadPath(
+ File csvFile, int[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ return benchmarkMicroStyleVoidIntDecodeReadPath(
+ csvFile,
+ expected,
+ binPath,
+ decodedCsvPath,
+ unc -> SubcolumnPruneNewTest.Decoder(unc),
+ SubcolumnPruneNewTest::Decoder,
+ avgDatasetVerifyReadIntsMicroStyleNanos(csvFile, expected));
+ }
+
+ private static ReadPathTimings benchmarkBitPackingReadPath(
+ File csvFile, int[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ return benchmarkPlainIntEncodedReadPath(
+ csvFile,
+ expected,
+ binPath,
+ decodedCsvPath,
+ BPTest::Decoder,
+ avgDatasetVerifyReadIntsMicroStyleNanos(csvFile, expected));
+ }
+
+ private static ReadPathTimings benchmarkRleReadPath(
+ File csvFile, long[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ ReadPathTimings out = new ReadPathTimings();
+ out.avgDatasetVerifyReadNs = avgDatasetVerifyReadLongsFileWideNanos(csvFile, expected);
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+ t0 = System.nanoTime();
+ RLEBPLongTest.BOSDecoderImprove(unc);
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long[] decoded = RLEBPLongTest.decodeToLongArray(unCompressor.uncompress(blob));
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ sumCsv += timeDecodedLongCsvWriteNanos(decodedCsvPath, decoded);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkCodecBlobReadPath(
+ File csvFile,
+ int[] expectedInts,
+ Path binPath,
+ Path decodedCsvPath,
+ java.util.function.Function<byte[], int[]> decodeBlob)
+ throws IOException {
+ ReadPathTimings out = new ReadPathTimings();
+
+ long sumVerify = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ List<String[]> rows = readFullCsvAllColumnsAsStringRows(csvFile);
+ long t1 = System.nanoTime();
+ sumVerify += (t1 - t0);
+ List<String> tokens = firstColumnTokensFromRows(rows);
+ double[] vals = parseDoublesFromTokens(tokens);
+ Assert.assertEquals(expectedInts.length, vals.length);
+ }
+ out.avgDatasetVerifyReadNs = sumVerify / BENCH_PHASE_REPEATS;
+
+ long[] brNs = new long[1];
+ byte[] blob = readBinBlobWithTiming(binPath, brNs);
+ out.avgBinReadNs = brNs[0];
+ out.compressedBytesObserved = blob.length;
+
+ long sumDec = 0;
+ int[] decoded = null;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ decoded = decodeBlob.apply(blob);
+ long t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ // if (rep == 0) {
+ // Assert.assertArrayEquals(expectedInts, decoded);
+ // }
+ }
+ out.avgUncompressNs = 0;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ long[] asLong = new long[decoded.length];
+ for (int i = 0; i < decoded.length; i++) {
+ asLong[i] = decoded[i];
+ }
+ sumCsv += timeDecodedLongCsvWriteNanos(decodedCsvPath, asLong);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkSimple8bReadPath(
+ File csvFile, int[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ return benchmarkCodecBlobReadPath(
+ csvFile,
+ expected,
+ binPath,
+ decodedCsvPath,
+ blob -> FastPForSimple8bCodec.decode(intArrayFromBlobBytes(blob)));
+ }
+
+ private static ReadPathTimings benchmarkFastPforReadPath(
+ File csvFile, int[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ IntCompressor comp = new IntCompressor(new FastPFOR());
+ return benchmarkCodecBlobReadPath(
+ csvFile,
+ expected,
+ binPath,
+ decodedCsvPath,
+ blob -> {
+ int[] padded = comp.uncompress(intArrayFromBlobBytes(blob));
+ return Arrays.copyOf(padded, expected.length);
+ });
+ }
+
+ private static ReadPathTimings benchmarkRawDoubleLzmaReadPath(
+ File csvFile, double[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.LZMA2);
+
+ ReadPathTimings out = new ReadPathTimings();
+
+ long sumVerify = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ List<String[]> rows = readFullCsvAllColumnsAsStringRows(csvFile);
+ long t1 = System.nanoTime();
+ sumVerify += (t1 - t0);
+ List<String> tokens = firstColumnTokensFromRows(rows);
+ double[] vals = parseDoublesFromTokens(tokens);
+ Assert.assertEquals(expected.length, vals.length);
+ for (int i = 0; i < expected.length; i++) {
+ Assert.assertEquals(
+ Double.doubleToLongBits(expected[i]), Double.doubleToLongBits(vals[i]));
+ }
+ }
+ out.avgDatasetVerifyReadNs = sumVerify / BENCH_PHASE_REPEATS;
+
+ long sumBr = 0;
+ byte[] blob = null;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ if (benchWindowsBinReadShrinkBeforeReadEnv()) {
+ blob = null;
+ }
+ blob = readCompressedBinBench(binPath);
+ long t1 = System.nanoTime();
+ sumBr += (t1 - t0);
+ }
+ out.avgBinReadNs = sumBr / BENCH_PHASE_REPEATS;
+ Assert.assertNotNull(blob);
+ out.compressedBytesObserved = blob.length;
+
+ double[] decoded = new double[expected.length];
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+
+ t0 = System.nanoTime();
+ double[] d = littleEndianBytesToDoubles(unc);
+ System.arraycopy(d, 0, decoded, 0, d.length);
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+
+ // if (rep == 0) {
+ // for (int i = 0; i < expected.length; i++) {
+ // Assert.assertEquals(
+ // Double.doubleToLongBits(expected[i]), Double.doubleToLongBits(decoded[i]));
+ // }
+ // }
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ sumCsv += timeDecodedDoublesCsvWriteNanos(decodedCsvPath, decoded);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+
+ return out;
+ }
+
+ private static ReadPathTimings benchmarkDictionaryReadPath(
+ File csvFile, long[] expected, Path binPath, Path decodedCsvPath) throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+
+ ReadPathTimings out = new ReadPathTimings();
+
+ long sumVerify = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ List<String[]> rows = readFullCsvAllColumnsAsStringRows(csvFile);
+ long t1 = System.nanoTime();
+ sumVerify += (t1 - t0);
+ List<String> tokens = firstColumnTokensFromRows(rows);
+ long[] vals = scaleTokensPerSubcolumnBlock(tokens);
+ Assert.assertEquals(expected.length, vals.length);
+ Assert.assertArrayEquals(expected, vals);
+ }
+ out.avgDatasetVerifyReadNs = sumVerify / BENCH_PHASE_REPEATS;
+
+ long sumBr = 0;
+ byte[] blob = null;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ if (benchWindowsBinReadShrinkBeforeReadEnv()) {
+ blob = null;
+ }
+ blob = readCompressedBinBench(binPath);
+ long t1 = System.nanoTime();
+ sumBr += (t1 - t0);
+ }
+ out.avgBinReadNs = sumBr / BENCH_PHASE_REPEATS;
+ Assert.assertNotNull(blob);
+ out.compressedBytesObserved = blob.length;
+
+ long[] decoded = new long[expected.length];
+ long sumUnc = 0;
+ long sumDec = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(blob);
+ long t1 = System.nanoTime();
+ sumUnc += (t1 - t0);
+
+ DictionaryDecoder dictionaryDecoder = new DictionaryDecoder();
+ ByteBuffer db = ByteBuffer.wrap(unc);
+ t0 = System.nanoTime();
+ int i = 0;
+ while (dictionaryDecoder.hasNext(db)) {
+ decoded[i++] = binaryToLong(dictionaryDecoder.readBinary(db));
+ }
+ t1 = System.nanoTime();
+ sumDec += (t1 - t0);
+ // Assert.assertEquals(expected.length, i);
+
+ // if (rep == 0) {
+ // Assert.assertArrayEquals(expected, decoded);
+ // }
+ dictionaryDecoder.reset();
+ }
+ out.avgUncompressNs = sumUnc / BENCH_PHASE_REPEATS;
+ out.avgDecodeNs = sumDec / BENCH_PHASE_REPEATS;
+
+ long sumCsv = 0;
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ Files.deleteIfExists(decodedCsvPath);
+ sumCsv += timeDecodedLongCsvWriteNanos(decodedCsvPath, decoded);
+ }
+ out.avgDecodedCsvWriteNs = sumCsv / BENCH_PHASE_REPEATS;
+
+ return out;
+ }
+
+ private static PhaseTiming benchmarkRawDoubleLzma(double[] values) throws IOException {
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.LZMA2);
+
+ PhaseTiming t = new PhaseTiming();
+
+ byte[] raw = doublesToLittleEndianRawBytes(values);
+ byte[] compressed0 = compressLzma2BenchPreset(raw);
+ byte[] back0 = unCompressor.uncompress(compressed0);
+ assertRawDoubleRoundtrip(values, back0);
+ t.encodedLen = raw.length;
+ t.compressedLen = compressed0.length;
+
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ long t0 = System.nanoTime();
+ byte[] cmp = compressLzma2BenchPreset(raw);
+ long t1 = System.nanoTime();
+ t.sumCompressNs += (t1 - t0);
+
+ t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(cmp);
+ t1 = System.nanoTime();
+ t.sumUncompressNs += (t1 - t0);
+
+ t0 = System.nanoTime();
+ littleEndianBytesToDoubles(unc);
+ t1 = System.nanoTime();
+ t.sumDecodeNs += (t1 - t0);
+ }
+ return t;
+ }
+
+ private static PhaseTiming benchmarkDictionaryUncompressed(long[] values) throws IOException {
+ ICompressor compressor = ICompressor.getCompressor(CompressionType.UNCOMPRESSED);
+ IUnCompressor unCompressor = IUnCompressor.getUnCompressor(CompressionType.UNCOMPRESSED);
+ PhaseTiming t = new PhaseTiming();
+
+ DictionaryEncoder encoder = new DictionaryEncoder();
+ ByteArrayOutputStream baos0 = new ByteArrayOutputStream();
+ for (long v : values) {
+ encoder.encode(longToPlainBinary(v), baos0);
+ }
+ encoder.flush(baos0);
+ byte[] plain = baos0.toByteArray();
+ byte[] compressed0 = compressor.compress(plain);
+ byte[] back0 = unCompressor.uncompress(compressed0);
+ assertDictionaryRoundtrip(values, back0);
+ t.encodedLen = plain.length;
+ t.compressedLen = compressed0.length;
+
+ for (int rep = 0; rep < BENCH_PHASE_REPEATS; rep++) {
+ DictionaryEncoder enc = new DictionaryEncoder();
+
+ long t0 = System.nanoTime();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (long v : values) {
+ enc.encode(longToPlainBinary(v), baos);
+ }
+ enc.flush(baos);
+ byte[] encBytes = baos.toByteArray();
+ long t1 = System.nanoTime();
+ t.sumEncodeFlushNs += (t1 - t0);
+
+ t0 = System.nanoTime();
+ byte[] cmp = compressor.compress(encBytes);
+ t1 = System.nanoTime();
+ t.sumCompressNs += (t1 - t0);
+
+ t0 = System.nanoTime();
+ byte[] unc = unCompressor.uncompress(cmp);
+ t1 = System.nanoTime();
+ t.sumUncompressNs += (t1 - t0);
+
+ DictionaryDecoder decoder = new DictionaryDecoder();
+ t0 = System.nanoTime();
+ ByteBuffer db = ByteBuffer.wrap(unc);
+ while (decoder.hasNext(db)) {
+ decoder.readBinary(db);
+ }
+ t1 = System.nanoTime();
+ t.sumDecodeNs += (t1 - t0);
+ decoder.reset();
+ }
+ return t;
+ }
+
+ @Test
+ public void testSyntheticRawDoubleLzmaRoundTripAndTiming() throws IOException {
+ double[] values = {
+ 0.0,
+ 1.0,
+ -1.0,
+ 1234567890123.0,
+ (double) (Long.MAX_VALUE / 4),
+ (double) (Long.MIN_VALUE / 4),
+ 42.0,
+ 42.0,
+ 99.0,
+ -300.0
+ };
+ PhaseTiming t = benchmarkRawDoubleLzma(values);
+ Assert.assertTrue(t.compressedLen <= t.encodedLen + 200);
+ System.out.printf(
+ "RAW_DOUBLE+LZMA2 synthetic n=%d enc+flush_avg_ns=%.1f compress_avg_ns=%.1f "
+ + "uncompress_avg_ns=%.1f decode_avg_ns=%.1f encoded_B=%d compressed_B=%d%n",
+ values.length,
+ t.avgEncodeFlushNs(),
+ t.avgCompressNs(),
+ t.avgUncompressNs(),
+ t.avgDecodeNs(),
+ t.encodedLen,
+ t.compressedLen);
+ }
+
+ @Test
+ public void testSyntheticDictionaryUncompressedRoundTripAndTiming() throws IOException {
+ long[] values = {10L, 20L, 10L, 30L, 20L, 10L};
+ PhaseTiming t = benchmarkDictionaryUncompressed(values);
+ System.out.printf(
+ "DICTIONARY+UNC synthetic n=%d enc+flush_avg_ns=%.1f compress_avg_ns=%.1f "
+ + "uncompress_avg_ns=%.1f decode_avg_ns=%.1f encoded_B=%d compressed_B=%d%n",
+ values.length,
+ t.avgEncodeFlushNs(),
+ t.avgCompressNs(),
+ t.avgUncompressNs(),
+ t.avgDecodeNs(),
+ t.encodedLen,
+ t.compressedLen);
+ }
+
+ @Test
+ public void testHddEncodeCompressWriteBinCsvIfPresent() throws IOException {
+ runEncodeCompressWriteBinCsv(HDD_PROFILE);
+ }
+
+ @Test
+ public void testHddDecodeBinWriteDecodedCsvIfPresent() throws IOException {
+ runDecodeBinWriteDecodedCsv(HDD_PROFILE);
+ }
+
+ @Test
+ public void testSsdEncodeCompressWriteBinCsvIfPresent() throws IOException {
+ runEncodeCompressWriteBinCsv(SSD_PROFILE);
+ }
+
+ @Test
+ public void testSsdDecodeBinWriteDecodedCsvIfPresent() throws IOException {
+ runDecodeBinWriteDecodedCsv(SSD_PROFILE);
+ }
+
+ private static void runEncodeCompressWriteBinCsv(BenchStorageProfile profile) throws IOException {
+ File sourceDir = new File(profile.datasetDir);
+ Assume.assumeTrue(
+ "Skip when CSV dir missing: " + profile.datasetDir, sourceDir.isDirectory());
+ File[] csvFiles = listBenchmarkDatasetCsvFiles(sourceDir);
+ Assume.assumeTrue(
+ "Skip when no CSV files in " + profile.datasetDir, csvFiles.length > 0);
+
+ Path binsDir = Paths.get(profile.binOutputDir);
+ Path decodedRoot = Paths.get(profile.decodedCsvDir);
+ ensureDirectory(binsDir);
+ ensureDirectory(decodedRoot);
+
+ Path writeCsvPath = Paths.get(profile.writeMetricsCsvPath);
+ Path manifestPath = Paths.get(profile.compressManifestCsvPath);
+ ensureDirectory(writeCsvPath.getParent());
+ ensureDirectory(manifestPath.getParent());
+
+ try (BufferedWriter writeMetrics =
+ Files.newBufferedWriter(
+ writeCsvPath,
+ StandardCharsets.UTF_8,
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING,
+ StandardOpenOption.WRITE);
+ BufferedWriter manifest =
+ Files.newBufferedWriter(
+ manifestPath,
+ StandardCharsets.UTF_8,
+ StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING,
+ StandardOpenOption.WRITE)) {
+
+ writeMetrics.write(
+ "Dataset,Encoding Algorithm,Write Total Time Nanos,Dataset Read "
+ + "Time Nanos,Write CPU Time Nanos,Write IO Time Nanos,Points,Max "
+ + "Decimal Precision,Multiplier,TsFile Size Bytes,Write Encode Loop Time "
+ + "Nanos,Write Encode Flush Time Nanos,Write Compress Time Nanos\n");
+ manifest.write(
+ "Dataset,Encoding Algorithm,Source CSV Path,Points,Encoded "
+ + "Uncompressed Bytes,Raw Plain Codec\n");
+
+ System.out.printf(
+ "[DatasetEncoderCompressBinBench] encode-start | profile=%s | sourceDir=%s | datasets=%d | binsDir=%s%n",
+ profile.label,
+ sourceDir.getAbsolutePath(),
+ csvFiles.length,
+ binsDir.toAbsolutePath());
+ System.out.flush();
+
+ for (File f : csvFiles) {
+ List<String> tokens = readFirstColumnTokens(f);
+ Assume.assumeFalse(tokens.isEmpty());
+ int maxPrecAll = maxDecimalPrecision(tokens);
+ double[] dValues = parseDoublesFromTokens(tokens);
+ long[] dictValues = scaleTokensPerSubcolumnBlock(tokens);
+ Assert.assertEquals(dValues.length, dictValues.length);
+ String datasetName = stripCsvExtension(f.getName());
+ String srcPath = f.getCanonicalPath().replace('\\', '/');
+
+ long avgReadFullCsvNs = averageCsvFullReadWithoutNumericParseNanos(f);
+ int boundedPrec = Math.min(maxPrecAll, K_MAX_DECIMAL_PRECISION);
+ long multiplier = multiplierForBoundedPrecision(maxPrecAll);
+
+ int[] intScaled = scaleDoublesToIntsMicroStyle(dValues, maxPrecAll);
+ long[] hbpValues = scaleDoublesToLongsFileWide(dValues, maxPrecAll);
+ long[] rleValues = hbpValues;
+
+ logBenchDatasetHeader("encode", datasetName, f, dValues.length);
+
+ Path binLzma = binsDir.resolve(datasetName + "_" + SUFFIX_PLAIN_LZMA + ".bin");
+ logBenchWarmupDiscarded("EncodeCompressWriteBinCsv", datasetName, ALGO_LZMA_CSV);
+ benchmarkRawDoubleLzmaWritePath(dValues, binLzma);
+ logBenchProgress("encode", datasetName, ALGO_LZMA_CSV, dValues.length, binLzma);
+ WritePathTimings wLz = benchmarkRawDoubleLzmaWritePath(dValues, binLzma);
+ wLz.avgDatasetReadNs = avgReadFullCsvNs;
+ writeEncodeMetricsRow(
+ writeMetrics, datasetName, ALGO_LZMA_CSV, wLz, dValues.length, boundedPrec, multiplier);
+ writeManifestRow(
+ manifest,
+ datasetName,
+ ALGO_LZMA_CSV,
+ srcPath,
+ dValues.length,
+ wLz.encodedUncompressedBytes,
+ 1);
+
+ Path binDict = binsDir.resolve(datasetName + "_" + SUFFIX_DICTIONARY + ".bin");
+ logBenchProgress("encode", datasetName, ALGO_DICTIONARY_CSV, dictValues.length, binDict);
+ WritePathTimings wDc = benchmarkDictionaryWritePath(dictValues, binDict);
+ wDc.avgDatasetReadNs = avgReadFullCsvNs;
+ writeEncodeMetricsRow(
+ writeMetrics,
+ datasetName,
+ ALGO_DICTIONARY_CSV,
+ wDc,
+ dictValues.length,
+ boundedPrec,
+ multiplier);
+ writeManifestRow(
+ manifest,
+ datasetName,
+ ALGO_DICTIONARY_CSV,
+ srcPath,
+ dictValues.length,
+ wDc.encodedUncompressedBytes,
+ 0);
+
+ Path binGorilla = binsDir.resolve(datasetName + "_" + SUFFIX_GORILLA + ".bin");
+ logBenchProgress("encode", datasetName, ALGO_GORILLA_CSV, dValues.length, binGorilla);
+ WritePathTimings wGorilla = benchmarkGorillaWritePath(dValues, binGorilla);
+ wGorilla.avgDatasetReadNs = avgReadFullCsvNs;
+ writeEncodeMetricsRow(
+ writeMetrics, datasetName, ALGO_GORILLA_CSV, wGorilla, dValues.length, boundedPrec, multiplier);
+ writeManifestRow(
+ manifest,
+ datasetName,
... 77248 lines suppressed ...