You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by jf...@apache.org on 2022/01/02 09:54:17 UTC
[iotdb] branch experimental/code-generation updated: Added Code Generation to DataSetWithTimeGenerator. Added extensive Benchmarking
This is an automated email from the ASF dual-hosted git repository.
jfeinauer pushed a commit to branch experimental/code-generation
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/experimental/code-generation by this push:
new 6025be9 Added Code Generation to DataSetWithTimeGenerator. Added extensive Benchmarking
6025be9 is described below
commit 6025be924cefc2a9f7865e618a98c3a398930e67
Author: julian <j....@pragmaticminds.de>
AuthorDate: Sun Jan 2 10:53:41 2022 +0100
Added Code Generation to DataSetWithTimeGenerator.
Added extensive Benchmarking
---
.../query/dataset/DataSetWithTimeGenerator.java | 298 +++++++++++++++++--
...rPerformanceTest.java => CodeGenBenchmark.java} | 329 +++++++++++----------
2 files changed, 442 insertions(+), 185 deletions(-)
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/dataset/DataSetWithTimeGenerator.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/dataset/DataSetWithTimeGenerator.java
index 1773c66..d381545 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/dataset/DataSetWithTimeGenerator.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/query/dataset/DataSetWithTimeGenerator.java
@@ -18,15 +18,37 @@
*/
package org.apache.iotdb.tsfile.read.query.dataset;
+import org.apache.calcite.linq4j.tree.BlockStatement;
+import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.linq4j.tree.MethodDeclaration;
+import org.apache.calcite.linq4j.tree.ParameterExpression;
+import org.apache.calcite.linq4j.tree.Statement;
+import org.apache.calcite.linq4j.tree.Types;
+import org.apache.iotdb.tsfile.exception.NotImplementedException;
+import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.read.common.Field;
import org.apache.iotdb.tsfile.read.common.Path;
import org.apache.iotdb.tsfile.read.common.RowRecord;
import org.apache.iotdb.tsfile.read.query.timegenerator.TimeGenerator;
import org.apache.iotdb.tsfile.read.reader.series.FileSeriesReaderByTimestamp;
import org.apache.iotdb.tsfile.utils.TsPrimitiveType;
+import org.codehaus.commons.compiler.CompileException;
+import org.codehaus.janino.ClassBodyEvaluator;
+import org.codehaus.janino.Scanner;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.io.StringReader;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
/**
* query processing: (1) generate time by series that has filter (2) get value of series that does
@@ -34,18 +56,30 @@ import java.util.List;
*/
public class DataSetWithTimeGenerator extends QueryDataSet {
+ private static final Logger logger = LoggerFactory.getLogger(DataSetWithTimeGenerator.class);
+
+ public static final AtomicBoolean generate = new AtomicBoolean(true);
+ private static final AtomicLong classId = new AtomicLong(0);
+
+ private final RecordGenerator generator;
private TimeGenerator timeGenerator;
private List<FileSeriesReaderByTimestamp> readers;
private List<Boolean> cached;
+ public static interface RecordGenerator {
+
+ public void accept(RowRecord record, long timestamp);
+
+ }
+
/**
* constructor of DataSetWithTimeGenerator.
*
- * @param paths paths in List structure
- * @param cached cached boolean in List(boolean) structure
- * @param dataTypes TSDataTypes in List structure
+ * @param paths paths in List structure
+ * @param cached cached boolean in List(boolean) structure
+ * @param dataTypes TSDataTypes in List structure
* @param timeGenerator TimeGenerator object
- * @param readers readers in List(FileSeriesReaderByTimestamp) structure
+ * @param readers readers in List(FileSeriesReaderByTimestamp) structure
*/
public DataSetWithTimeGenerator(
List<Path> paths,
@@ -57,6 +91,237 @@ public class DataSetWithTimeGenerator extends QueryDataSet {
this.cached = cached;
this.timeGenerator = timeGenerator;
this.readers = readers;
+
+ if (DataSetWithTimeGenerator.generate.get()) {
+ // Do fancy stuff here, code was
+// FileSeriesReaderByTimestamp fileSeriesReaderByTimestamp = readers.get(i);
+// Object value = fileSeriesReaderByTimestamp.getValueInTimestamp(timestamp);
+// if (dataTypes.get(i) == TSDataType.VECTOR) {
+// TsPrimitiveType v = ((TsPrimitiveType[]) value)[0];
+// rowRecord.addField(v.getValue(), v.getDataType());
+// } else {
+// rowRecord.addField(value, dataTypes.get(i));
+// }
+ ParameterExpression readers_ = Expressions.parameter(new Types.ArrayType(FileSeriesReaderByTimestamp.class), "readers");
+ ParameterExpression rowRecord_ = Expressions.parameter(RowRecord.class, "rowRecord");
+ ParameterExpression timestamp_ = Expressions.parameter(long.class, "timestamp");
+
+
+ ArrayList<Statement> body = new ArrayList<>();
+ Method getValueInTimestamp;
+ Method addField;
+ Method getValue;
+ Method getDataType;
+ try {
+ getValueInTimestamp = FileSeriesReaderByTimestamp.class.getMethod("getValueInTimestamp", long.class);
+ addField = RowRecord.class.getMethod("addField", Field.class);
+ getValue = TsPrimitiveType.class.getMethod("getValue");
+ getDataType = TsPrimitiveType.class.getMethod("getDataType");
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException();
+ }
+
+ ParameterExpression reader_ = Expressions.parameter(FileSeriesReaderByTimestamp.class, "reader");
+ ParameterExpression value_ = Expressions.parameter(Object.class, "value");
+
+ body.add(Expressions.declare(0, reader_, null));
+ body.add(Expressions.declare(0, value_, null));
+
+ for (int i = 0; i < paths.size(); i++) {
+ if (this.cached.get(i) == false) {
+ body.add(
+ Expressions.statement(
+ Expressions.assign(reader_,
+ Expressions.convert_(
+ Expressions.arrayIndex(readers_, Expressions.constant(i)),
+ FileSeriesReaderByTimestamp.class
+ )
+ )
+ )
+ );
+ body.add(
+ Expressions.statement(
+ Expressions.assign(value_, Expressions.call(reader_, getValueInTimestamp, timestamp_))
+ )
+ );
+ if (dataTypes.get(i) == TSDataType.VECTOR) {
+ ParameterExpression v_ = Expressions.parameter(TsPrimitiveType.class, "v");
+ body.add(
+ Expressions.declare(1, v_,
+ Expressions.convert_(
+ Expressions.arrayIndex(v_, Expressions.constant(0)),
+ TsPrimitiveType.class
+ )
+ )
+ );
+ body.add(
+ Expressions.statement(
+ Expressions.call(
+ rowRecord_, addField,
+ Expressions.call(v_, getValue),
+ Expressions.call(v_, getDataType)
+ )
+ )
+ );
+ } else {
+ // We can get rid of the switch statement here in addField
+ ParameterExpression field_ = Expressions.parameter(Field.class, "field" + i);
+
+ // From Field.getField(xxx)
+ // TODO add Null Check
+// if (value == null) {
+// return null;
+// }
+
+
+// Field field = new Field(dataType);
+ body.add(
+ Expressions.declare(0, field_, Expressions.new_(Field.class, Expressions.constant(dataTypes.get(i))))
+ );
+ switch (dataTypes.get(i)) {
+ case INT32:
+ // field.setIntV((int) value);
+ body.add(
+ Expressions.statement(
+ Expressions.call(field_, "setIntV",
+ Expressions.unbox(
+ Expressions.convert_(value_, Integer.class)
+ )
+ )
+ )
+ );
+ break;
+// case INT64:
+// field.setLongV((long) value);
+// break;
+ case FLOAT:
+// field.setFloatV((float) value);
+ body.add(
+ Expressions.statement(
+ Expressions.call(field_, "setFloatV",
+ Expressions.unbox(
+ Expressions.convert_(value_, Float.class)
+ )
+ )
+ )
+ );
+ break;
+// case DOUBLE:
+// field.setDoubleV((double) value);
+// break;
+// case BOOLEAN:
+// field.setBoolV((boolean) value);
+// break;
+// case TEXT:
+// field.setBinaryV((Binary) value);
+// break;
+ default:
+ throw new UnSupportedDataTypeException(dataTypes.get(i).toString());
+ }
+
+
+ // Old ("inefficient") method
+// body.add(
+// Expressions.statement(
+// Expressions.call(
+// rowRecord_, addField,
+// value_,
+// Expressions.constant(dataTypes.get(i))
+// )
+// )
+// );
+
+ // Efficient method as we avoid a switch in the Field::addField method
+ body.add(
+ Expressions.statement(
+ Expressions.call(
+ rowRecord_, addField,
+ field_
+ )
+ )
+ );
+ }
+ } else {
+ throw new NotImplementedException();
+ }
+ }
+
+ BlockStatement block = Expressions.block(
+ Expressions.tryCatch(
+ Expressions.block(body),
+ Expressions.catch_(
+ Expressions.parameter(IOException.class, "e"),
+ Expressions.block()
+ )
+ )
+ );
+
+ MethodDeclaration method = Expressions.methodDecl(1, void.class, "accept", Arrays.asList(rowRecord_, timestamp_), block);
+
+ String s = Expressions.toString(method);
+
+ String className = "Generator" + classId.getAndIncrement();
+
+ String s2 = "import org.apache.iotdb.tsfile.read.reader.series.FileSeriesReaderByTimestamp;\n" +
+ "import java.util.List;" +
+ "\nprivate final FileSeriesReaderByTimestamp[] readers;\n" +
+ "public " + className + "(List<FileSeriesReaderByTimestamp> readers) {\nthis.readers = (FileSeriesReaderByTimestamp[])readers.toArray(new FileSeriesReaderByTimestamp[]{});\n}\n\n" + s;
+
+ logger.debug(s2);
+
+ RecordGenerator f;
+
+ try {
+ Scanner scanner = new Scanner("", new StringReader(s2));
+ ClassBodyEvaluator ev = new ClassBodyEvaluator(scanner, className, null, new Class[]{RecordGenerator.class}, null);
+
+ f = (RecordGenerator) ev.getClazz().getConstructors()[0].newInstance(readers);
+
+ } catch (CompileException | IllegalAccessException | InstantiationException | InvocationTargetException | IOException ex) {
+ ex.printStackTrace();
+ throw new IllegalStateException();
+ }
+
+ this.generator = f;
+ } else {
+ this.generator = new RecordGenerator() {
+ @Override
+ public void accept(RowRecord record, long timestamp) {
+ try {
+ for (int i = 0; i < paths.size(); i++) {
+
+ // get value from readers in time generator
+ if (cached.get(i)) {
+ Object value = null;
+
+ value = timeGenerator.getValue(paths.get(i));
+
+ if (dataTypes.get(i) == TSDataType.VECTOR) {
+ TsPrimitiveType v = ((TsPrimitiveType[]) value)[0];
+ record.addField(v.getValue(), v.getDataType());
+ } else {
+ record.addField(value, dataTypes.get(i));
+ }
+ continue;
+ }
+
+
+ // get value from series reader without filter
+ FileSeriesReaderByTimestamp fileSeriesReaderByTimestamp = readers.get(i);
+ Object value = fileSeriesReaderByTimestamp.getValueInTimestamp(timestamp);
+ if (dataTypes.get(i) == TSDataType.VECTOR) {
+ TsPrimitiveType v = ((TsPrimitiveType[]) value)[0];
+ record.addField(v.getValue(), v.getDataType());
+ } else {
+ record.addField(value, dataTypes.get(i));
+ }
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException();
+ }
+ }
+ };
+ }
}
@Override
@@ -69,30 +334,7 @@ public class DataSetWithTimeGenerator extends QueryDataSet {
long timestamp = timeGenerator.next();
RowRecord rowRecord = new RowRecord(timestamp);
- for (int i = 0; i < paths.size(); i++) {
-
- // get value from readers in time generator
- if (cached.get(i)) {
- Object value = timeGenerator.getValue(paths.get(i));
- if (dataTypes.get(i) == TSDataType.VECTOR) {
- TsPrimitiveType v = ((TsPrimitiveType[]) value)[0];
- rowRecord.addField(v.getValue(), v.getDataType());
- } else {
- rowRecord.addField(value, dataTypes.get(i));
- }
- continue;
- }
-
- // get value from series reader without filter
- FileSeriesReaderByTimestamp fileSeriesReaderByTimestamp = readers.get(i);
- Object value = fileSeriesReaderByTimestamp.getValueInTimestamp(timestamp);
- if (dataTypes.get(i) == TSDataType.VECTOR) {
- TsPrimitiveType v = ((TsPrimitiveType[]) value)[0];
- rowRecord.addField(v.getValue(), v.getDataType());
- } else {
- rowRecord.addField(value, dataTypes.get(i));
- }
- }
+ this.generator.accept(rowRecord, timestamp);
return rowRecord;
}
diff --git a/tsfile/src/test/java/org/apache/iotdb/tsfile/write/TsFilterPerformanceTest.java b/tsfile/src/test/java/org/apache/iotdb/tsfile/write/CodeGenBenchmark.java
similarity index 60%
rename from tsfile/src/test/java/org/apache/iotdb/tsfile/write/TsFilterPerformanceTest.java
rename to tsfile/src/test/java/org/apache/iotdb/tsfile/write/CodeGenBenchmark.java
index 3d835ca..22224e1 100644
--- a/tsfile/src/test/java/org/apache/iotdb/tsfile/write/TsFilterPerformanceTest.java
+++ b/tsfile/src/test/java/org/apache/iotdb/tsfile/write/CodeGenBenchmark.java
@@ -37,6 +37,7 @@ import org.apache.iotdb.tsfile.read.filter.ValueFilter;
import org.apache.iotdb.tsfile.read.filter.basic.Filter;
import org.apache.iotdb.tsfile.read.filter.codegen.Generator;
import org.apache.iotdb.tsfile.read.filter.factory.FilterFactory;
+import org.apache.iotdb.tsfile.read.query.dataset.DataSetWithTimeGenerator;
import org.apache.iotdb.tsfile.read.query.dataset.QueryDataSet;
import org.apache.iotdb.tsfile.utils.FilePathUtils;
import org.apache.iotdb.tsfile.utils.TsFileGeneratorForTest;
@@ -47,13 +48,20 @@ import org.apache.iotdb.tsfile.write.schema.UnaryMeasurementSchema;
import org.junit.Assert;
import org.junit.Test;
import org.openjdk.jmh.annotations.Benchmark;
-import org.openjdk.jmh.annotations.BenchmarkMode;
-import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
-import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+import org.openjdk.jmh.results.RunResult;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.format.OutputFormat;
+import org.openjdk.jmh.runner.format.OutputFormatFactory;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openjdk.jmh.runner.options.VerboseMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -61,26 +69,50 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+/**
+ * This class is a Benchmarking Tool for the evaluation of code generation.
+ * It makes heavy use of JMH.
+ *
+ * To start the Benchmarking, just run the main class.
+ */
@State(Scope.Benchmark)
-public class TsFilterPerformanceTest {
+public class CodeGenBenchmark {
+
+ private static Logger logger = LoggerFactory.getLogger(CodeGenBenchmark.class);
+
+ @Param("standard")
+ public String filter;
+
+ @Param("1")
+ public int runs;
+
+// @Param("false")
+// public boolean optimizeFilters;
+//
+// @Param("false")
+// public boolean generateSeries;
- private static Logger logger = LoggerFactory.getLogger(TsFilterPerformanceTest.class);
+ @Param("false")
+ public boolean optimize;
- public static int runs = 1;
+ @Param("100000000")
+ public int datapoints;
- TsFileWriter writer = null;
String fileName = TsFileGeneratorForTest.getTestTsFilePath("root.sg1", 0, 0, 1);
boolean closed = false;
- String getFilename() {
+ static String getFilename() {
String filePath =
String.format(
"/tmp/root.sg1/0/0/",
@@ -95,115 +127,32 @@ public class TsFilterPerformanceTest {
return filePath.concat(fileName);
}
- /**
- * Benchmark Mode Cnt Score Error Units
- * TsFilterPerformanceTest.getSimpleUnoptimized avgt 15 10699,176 ± 511,309 ms/op
- * @throws IOException
- */
- @Benchmark
- @BenchmarkMode(Mode.AverageTime)
- @OutputTimeUnit(TimeUnit.MILLISECONDS)
- @Warmup(iterations = 3)
- @Fork(3)
- public void getSimpleUnoptimized() throws IOException {
- List<Filter> filters = getSimpleFilters();
- runTests(filters, false);
- }
-
- /**
- * Benchmark Mode Cnt Score Error Units
- * TsFilterPerformanceTest.getSimpleOptimized avgt 15 11084,838 ± 331,142 ms/op
- * @throws IOException
- */
@Benchmark
- @BenchmarkMode(Mode.AverageTime)
- @OutputTimeUnit(TimeUnit.MILLISECONDS)
- @Warmup(iterations = 3)
- @Fork(3)
- public void getSimpleOptimized() throws IOException {
- List<Filter> filters = getSimpleFilters();
- runTests(filters, true);
- }
-
-
- /**
- * Benchmark Mode Cnt Score Error Units
- * TsFilterPerformanceTest.getStandardUnoptimized avgt 15 10345,496 ± 571,557 ms/op
- * @throws IOException
- */
- @Benchmark
- @BenchmarkMode(Mode.AverageTime)
- @OutputTimeUnit(TimeUnit.MILLISECONDS)
- @Warmup(iterations = 3)
- @Fork(3)
- public void getStandardUnoptimized() throws IOException {
- List<Filter> filters = getStandardFilters();
- runTests(filters, false);
- }
-
- /**
- * Benchmark Mode Cnt Score Error Units
- * TsFilterPerformanceTest.getStandardOptimized avgt 15 9864,581 ± 591,591 ms/op
- * -> 4.6%
- * @throws IOException
- */
- @Benchmark
- @BenchmarkMode(Mode.AverageTime)
- @OutputTimeUnit(TimeUnit.MILLISECONDS)
- @Warmup(iterations = 3)
- @Fork(3)
- public void getStandardOptimized() throws IOException {
- List<Filter> filters = getStandardFilters();
- runTests(filters, true);
- }
-
- /**
- * Benchmark Mode Cnt Score Error Units
- * TsFilterPerformanceTest.getComplexUnoptimized avgt 15 15053,562 ± 115,997 ms/op
- * -> 12.3%
- * @throws IOException
- */
- @Benchmark
- @BenchmarkMode(Mode.AverageTime)
- @OutputTimeUnit(TimeUnit.MILLISECONDS)
- @Warmup(iterations = 3)
- @Fork(3)
- public void getComplexUnoptimized() throws IOException {
- List<Filter> filters = getComplexFilters();
- runTests(filters, false);
- }
-
- /**
- * Benchmark Mode Cnt Score Error Units
- * TsFilterPerformanceTest.getComplexOptimized avgt 15 13196,650 ± 477,061 ms/op
- * @throws IOException
- */
- @Benchmark
- @BenchmarkMode(Mode.AverageTime)
- @OutputTimeUnit(TimeUnit.MILLISECONDS)
- @Warmup(iterations = 3)
- @Fork(3)
- public void getComplexOptimized() throws IOException {
- List<Filter> filters = getComplexFilters();
- runTests(filters, true);
+ public void runBenchmark(Blackhole blackhole) throws IOException {
+ List<Filter> filters = getFilter();
+ // set optimizing parameters
+ Generator.active.set(this.optimize);
+ DataSetWithTimeGenerator.generate.set(this.optimize);
+ runTests(filters, blackhole);
}
@Test
public void generate() throws IOException, WriteProcessException {
+ generate(100_000_000);
+ }
+
+ public static void generate(int datapoints) throws IOException, WriteProcessException {
String filename = getFilename();
- System.out.println("Writing to " + filename);
+ logger.info("Writing {} datapoints to {}", datapoints, filename);
File f = new File(filename);
if (!f.getParentFile().exists()) {
Assert.assertTrue(f.getParentFile().mkdirs());
}
- writer = new TsFileWriter(f);
- registerTimeseries();
+ TsFileWriter writer = new TsFileWriter(f);
+ registerTimeseries(writer);
- for (long t = 0; t <= 100_000_000; t++) {
- if (t % 100 == 0) {
- System.out.println(t);
- }
+ for (long t = 0; t <= datapoints; t++) {
// normal
TSRecord record = new TSRecord(t, "d1");
record.addTuple(new FloatDataPoint("s1", (float) (100.0 * Math.random())));
@@ -214,56 +163,12 @@ public class TsFilterPerformanceTest {
writer.close();
}
+ private double runTests(List<Filter> filter, Blackhole blackhole) throws IOException {
- /**
- * Without optimization: 10798.055595900001 ms
- * With: 9907.020887499999
- * Improvement ~ 8%
- */
- @Test
- public void readMany() throws IOException {
- List<List<Filter>> scenarios = Arrays.asList(
- getSimpleFilters(),
- getStandardFilters(),
- getComplexFilters()
- );
-
- ArrayList<Double> results = new ArrayList<>();
-
- for (int scenarioCount = 0; scenarioCount < scenarios.size(); scenarioCount++) {
- System.out.println("Starting Scenario " + scenarioCount);
- List<Filter> filter = scenarios.get(scenarioCount);
-
- // First, no optimizer
- double noOptimizer = runTests(filter, false);
- double optimizer = runTests(filter, true);
- double improvement = 100.0 * (1 - optimizer / noOptimizer);
-
- System.out.println("==========================");
- System.out.println("Scenario " + scenarioCount);
- System.out.println("==========================");
- System.out.printf("Duration (no optimizer): %.2f ms\n", noOptimizer);
- System.out.printf("Duration (optimizer): %.2f ms\n", optimizer);
- System.out.printf("Improvement: %.2f %%\n\n", improvement);
-
- results.add(improvement);
- }
-
- System.out.println("==========================");
- System.out.println("Final Result");
- System.out.println("==========================");
- for (Double result : results) {
- System.out.printf("Improvement: %.2f %%\n", result);
- }
- System.out.println("==========================");
- }
-
- private double runTests(List<Filter> filter, boolean optimizer) throws IOException {
- Generator.active.set(optimizer);
long start = System.nanoTime();
for (int i = 1; i <= runs; i++) {
logger.info("Round " + i);
- read(filter);
+ read(filter, blackhole);
}
long end = System.nanoTime();
@@ -283,6 +188,17 @@ public class TsFilterPerformanceTest {
return filters;
}
+ private List<Filter> getFilter() {
+ if ("standard".equals(this.filter)){
+ return getStandardFilters();
+ } else if ("simple".equals(this.filter)) {
+ return getSimpleFilters();
+ } else if ("complex".equals(this.filter)) {
+ return getComplexFilters();
+ }
+ throw new IllegalStateException();
+ }
+
private List<Filter> getStandardFilters() {
Filter filter = TimeFilter.lt(50_000_000);
Filter filter2 = FilterFactory.and(ValueFilter.gt(25), ValueFilter.lt(75));
@@ -315,7 +231,7 @@ public class TsFilterPerformanceTest {
return filters;
}
- public void read(List<Filter> filters) throws IOException {
+ public void read(List<Filter> filters, Blackhole blackhole) throws IOException {
TsFileSequenceReader fileSequenceReader = new TsFileSequenceReader(getFilename());
TsFileReader fileReader = new TsFileReader(fileSequenceReader);
@@ -343,13 +259,14 @@ public class TsFilterPerformanceTest {
int count = 0;
while (dataSet.hasNext()) {
RowRecord rowRecord = dataSet.next();
+ blackhole.consume(rowRecord);
count++;
}
logger.debug("Iterartion done, " + count + " points");
}
- private void registerTimeseries() {
+ private static void registerTimeseries(TsFileWriter writer) {
// register nonAligned timeseries "d1.s1","d1.s2","d1.s3"
try {
writer.registerTimeseries(
@@ -428,4 +345,102 @@ public class TsFilterPerformanceTest {
}*/
}
+ @Setup(Level.Trial)
+ public void setup() throws IOException, WriteProcessException {
+ generate(datapoints);
+ }
+
+ /**
+ * Benchmark (filter) (generateSeries) (optimizeFilters) (runs) Mode Cnt Score Error Units
+ * TsFilterPerformanceTest.runBenchmark standard false false 1 avgt 5 8665,837 ± 703,708 ms/op
+ * TsFilterPerformanceTest.runBenchmark standard false false 5 avgt 5 43861,836 ± 8981,774 ms/op
+ * TsFilterPerformanceTest.runBenchmark standard false false 10 avgt 5 88036,981 ± 13661,095 ms/op
+ * @param args
+ * @throws RunnerException
+ */
+ public static void main(String[] args) throws RunnerException, IOException, WriteProcessException {
+ // Start with the execution
+ OptionsBuilder optionsBuilder = new OptionsBuilder();
+ optionsBuilder
+ .include("runBenchmark")
+ .forks(1)
+ .measurementIterations(3)
+ .warmupIterations(3)
+ .mode(Mode.AverageTime)
+ .timeUnit(TimeUnit.MILLISECONDS)
+ .param("datapoints", "100", "10000", "1000000", "100000000", "1000000000")
+ .param("optimize", "false", "true")
+ .param("filter", "simple", "standard", "complex")
+ // .param("runs", "1")
+ ;
+ OutputFormat outputFormat = OutputFormatFactory.createFormatInstance(System.out, VerboseMode.NORMAL);
+ Runner runner = new Runner(optionsBuilder.build(), outputFormat);
+
+ Collection<RunResult> results = runner.run();
+
+ printResults(results);
+ }
+
+ private static void printResults(Collection<RunResult> results) {
+ // Prepare Output for csv
+ StringBuilder sb = new StringBuilder();
+ RunResult first = results.iterator().next();
+
+ ArrayList<String> orderedParameters = new ArrayList<>(first.getParams().getParamsKeys());
+
+ for (RunResult result : results) {
+ System.out.println("\n===================");
+ for (String paramsKey : result.getParams().getParamsKeys()) {
+ String paramValue = result.getParams().getParam(paramsKey);
+ System.out.printf("%s: %s\n", paramsKey, paramValue);
+ }
+
+ // Now calculate the baseline
+ double duration = result.getPrimaryResult().getScore();
+
+ // Now find the score of the base run with this parameters
+ Optional<RunResult> baseline = findBaselineForResult(results, result);
+
+ if (!baseline.isPresent()) {
+ System.out.println(" -- no baseline found -- ");
+ continue;
+ }
+
+ double baseLineDuration = baseline.get().getPrimaryResult().getScore();
+
+ double improvement = 100.0 * (1 - duration/baseLineDuration);
+
+ String scoreUnit = result.getPrimaryResult().getScoreUnit();
+ System.out.printf("Result: %.2f %s\n", duration, scoreUnit);
+ System.out.printf("Baseline: %.2f %s\n", baseLineDuration, scoreUnit);
+ System.out.printf("Improvement: %.2f %%\n", improvement);
+
+
+ // Create all Parameters
+ String prefix = orderedParameters.stream().map(key -> result.getParams().getParam(key)).collect(Collectors.joining(";"));
+
+ sb.append(String.format(Locale.ENGLISH, "%s;%f;%f;%f\n", prefix, duration, baseLineDuration, improvement/100.0));
+ }
+
+ System.out.println("Final Result");
+ System.out.println("==========");
+ System.out.println(sb);
+ }
+
+ private static Optional<RunResult> findBaselineForResult(Collection<RunResult> results, RunResult result) {
+ Optional<RunResult> baseline = results.stream().filter(new Predicate<RunResult>() {
+ @Override
+ public boolean test(RunResult runResult) {
+ boolean baseline = "false".equals(runResult.getParams().getParam("optimize"));
+
+ Set<String> compareKeys = result.getParams().getParamsKeys().stream().filter(key -> !Arrays.asList("optimize").contains(key)).collect(Collectors.toSet());
+
+ boolean same = compareKeys.stream().allMatch(key -> result.getParams().getParam(key).equals(runResult.getParams().getParam(key)));
+
+ return same && baseline;
+ }
+ }).findAny();
+ return baseline;
+ }
+
}