| /* |
| * 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.ignite.internal.benchmark; |
| |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.time.Duration; |
| import java.util.concurrent.TimeUnit; |
| import org.apache.ignite.internal.sql.engine.util.tpch.TpchHelper; |
| import org.apache.ignite.internal.sql.engine.util.tpch.TpchTables; |
| import org.apache.ignite.sql.IgniteSql; |
| import org.openjdk.jmh.annotations.Benchmark; |
| import org.openjdk.jmh.annotations.BenchmarkMode; |
| import org.openjdk.jmh.annotations.Fork; |
| import org.openjdk.jmh.annotations.Measurement; |
| 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.Threads; |
| import org.openjdk.jmh.annotations.Warmup; |
| import org.openjdk.jmh.infra.Blackhole; |
| import org.openjdk.jmh.runner.Runner; |
| import org.openjdk.jmh.runner.RunnerException; |
| import org.openjdk.jmh.runner.options.Options; |
| import org.openjdk.jmh.runner.options.OptionsBuilder; |
| |
| /** |
| * Benchmark that runs sql queries from TPC-H suite via embedded client. |
| */ |
| @State(Scope.Benchmark) |
| @Fork(1) |
| @Threads(1) |
| @Warmup(iterations = 10, time = 2) |
| @Measurement(iterations = 20, time = 2) |
| @BenchmarkMode(Mode.AverageTime) |
| @OutputTimeUnit(TimeUnit.MILLISECONDS) |
| @SuppressWarnings({"WeakerAccess", "unused"}) |
| public class TpchBenchmark extends AbstractMultiNodeBenchmark { |
| /* |
| Minimal configuration of this benchmark requires specifying pathToDataset. Latest known location |
| of dataset is https://github.com/cmu-db/benchbase/tree/main/data/tpch-sf0.01 for scale factor 0.01 |
| and https://github.com/cmu-db/benchbase/tree/main/data/tpch-sf0.1 for scale factor 0.1. Dataset |
| is set of CSV files with name `{$tableName}.tbl` per each table and character `|` as separator. |
| |
| By default, cluster's work directory will be created as a temporary folder. This implies, |
| that all data generated by benchmark will be cleared automatically. However, this also implies |
| that cluster will be recreated on EVERY RUN. Given there are 25 queries and 2 different modes |
| (fsync on/off), it results in 50 schema initialization and data upload cycles. To initialize |
| cluster once and then reuse it state override `AbstractMultiNodeBenchmark.workDir()` method. |
| Don't forget to clear that directory afterwards. |
| */ |
| |
| private static final String DATASET_READY_MARK_FILE_NAME = "ready.txt"; |
| |
| private final Path pathToDataset = null; |
| |
| /** Useful when you need to initialize only subset of tables. */ |
| private final TpchTables[] tablesToInit = TpchTables.values(); |
| |
| @Param({ |
| "1", "2", "3", "4", "5", "6", "7", "8", "8v", "9", "10", "11", "12", "12v", |
| "13", "14", "14v", "15", "16", "17", "18", "19", "20", "21", "22" |
| }) |
| private String queryId; |
| |
| private IgniteSql sql; |
| private String queryString; |
| |
| /** Initializes a schema and fills tables with data. */ |
| @Setup |
| public void setUp() throws Exception { |
| try { |
| sql = clusterNode.sql(); |
| |
| if (!Files.exists(workDir().resolve(DATASET_READY_MARK_FILE_NAME))) { |
| if (pathToDataset == null) { |
| throw new IllegalStateException("Path do dataset is not provided. Please read the comment" |
| + " in the beginning of TpchBenchmark.class"); |
| } |
| |
| System.out.println("Going to create schema..."); |
| |
| for (TpchTables table : tablesToInit) { |
| System.out.println("Going to create table \"" + table.tableName() + "\"..."); |
| sql.executeScript(table.ddlScript()); |
| System.out.println("Done"); |
| |
| fillTable(table); |
| } |
| |
| Files.createFile(workDir().resolve(DATASET_READY_MARK_FILE_NAME)); |
| } |
| |
| queryString = TpchHelper.getQuery(queryId); |
| } catch (Exception e) { |
| nodeTearDown(); |
| |
| throw e; |
| } |
| } |
| |
| /** Benchmark that measures performance of queries from TPC-H suite. */ |
| @Benchmark |
| public void run(Blackhole bh) { |
| try (var rs = sql.execute(null, queryString)) { |
| while (rs.hasNext()) { |
| bh.consume(rs.next()); |
| } |
| } |
| } |
| |
| /** |
| * Benchmark's entry point. |
| */ |
| public static void main(String[] args) throws RunnerException { |
| Options opt = new OptionsBuilder() |
| .include(".*" + TpchBenchmark.class.getSimpleName() + ".*") |
| .build(); |
| |
| new Runner(opt).run(); |
| } |
| |
| private void fillTable(TpchTables table) { |
| System.out.println("Going to fill table \"" + table.tableName() + "\"..."); |
| long start = System.nanoTime(); |
| int count = 0; |
| Iterable<Object[]> dataProvider = () -> { |
| try { |
| return table.dataProvider(pathToDataset); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| }; |
| for (Object[] params : dataProvider) { |
| sql.execute(null, table.insertPrepareStatement(), params); |
| |
| if (++count % 10_000 == 0) { |
| System.out.println("Uploaded " + count + " rows"); |
| } |
| } |
| System.out.println("Done in " + Duration.ofNanos(System.nanoTime() - start)); |
| } |
| |
| @Override |
| protected void createTable(String tableName) { |
| // NO-OP |
| } |
| } |