From 47a97006d91bc7b2a427200b4ea77e54948d0113 Mon Sep 17 00:00:00 2001 From: Benjamin Wagner Date: Sun, 5 Nov 2023 14:11:12 +0100 Subject: [PATCH] Add TPC-H Q19 Add the physical plan for TPC-H Q19. Add Q19 to the different test/runner/benchmark binaries. --- CMakeLists.txt | 9 +- reproduce/sql/q19.sql | 36 ++ src/algebra/ExpressionOp.cpp | 2 +- src/algebra/ExpressionOp.h | 1 + src/algebra/suboperators/RuntimeParam.h | 2 +- .../expressions/ExpressionHelpers.cpp | 7 +- src/codegen/Expression.h | 4 +- src/codegen/Value.h | 57 ++- src/codegen/backend_c/BackendC.cpp | 14 +- src/codegen/backend_c/BackendC.h | 5 +- src/codegen/backend_c/FunctionsC.cpp | 22 + src/common/TPCH.cpp | 429 +++++++++++++++++- src/common/TPCH.h | 2 + .../RuntimeExpressionFragmentizer.cpp | 10 + test/tpch/test_queries.cpp | 4 +- tools/inkfuse_bench.cpp | 1 + tools/inkfuse_runner.cpp | 5 +- 17 files changed, 592 insertions(+), 18 deletions(-) create mode 100644 reproduce/sql/q19.sql create mode 100644 src/codegen/backend_c/FunctionsC.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e9737aa..1589d11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,13 +15,13 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Generate DWARF 4 in debug to work on older GDB versions # flto required as xxhash is also built with flto to allow efficient inlining # of the hash functions. -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4 -stdlib=libc++ -flto") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4 -stdlib=libc++") # For benchmarks: easier profiling & links against system installed googlebench # if that was build with non libcxx. # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4 -flto -fno-omit-frame-pointer") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 -fsanitize=address") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g -O3") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 -fsanitize=address -flto=thin") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g -O3 -flto") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -flto") # --------------------------------------------------------------------------- # Dependencies @@ -126,6 +126,7 @@ set(SRC_CC "${CMAKE_SOURCE_DIR}/src/codegen/Value.cpp" "${CMAKE_SOURCE_DIR}/src/codegen/Statement.cpp" "${CMAKE_SOURCE_DIR}/src/codegen/backend_c/BackendC.cpp" + "${CMAKE_SOURCE_DIR}/src/codegen/backend_c/FunctionsC.cpp" "${CMAKE_SOURCE_DIR}/src/codegen/backend_c/ScopedWriter.cpp" "${CMAKE_SOURCE_DIR}/src/runtime/Runtime.cpp" "${CMAKE_SOURCE_DIR}/src/exec/ExecutionContext.cpp" diff --git a/reproduce/sql/q19.sql b/reproduce/sql/q19.sql new file mode 100644 index 0000000..a4a4976 --- /dev/null +++ b/reproduce/sql/q19.sql @@ -0,0 +1,36 @@ +-- No modifications +select + sum(l_extendedprice* (1 - l_discount)) as revenue +from + lineitem, + part +where + ( + p_partkey = l_partkey + and p_brand = 'Brand#12' + and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') + and l_quantity >= 1 and l_quantity <= 1 + 10 + and p_size between 1 and 5 + and l_shipmode in ('AIR', 'AIR REG') + and l_shipinstruct = 'DELIVER IN PERSON' + ) + or + ( + p_partkey = l_partkey + and p_brand = 'Brand#23' + and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') + and l_quantity >= 10 and l_quantity <= 10 + 10 + and p_size between 1 and 10 + and l_shipmode in ('AIR', 'AIR REG') + and l_shipinstruct = 'DELIVER IN PERSON' + ) + or + ( + p_partkey = l_partkey + and p_brand = 'Brand#34' + and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') + and l_quantity >= 20 and l_quantity <= 20 + 10 + and p_size between 1 and 15 + and l_shipmode in ('AIR', 'AIR REG') + and l_shipinstruct = 'DELIVER IN PERSON' + ); diff --git a/src/algebra/ExpressionOp.cpp b/src/algebra/ExpressionOp.cpp index 28ea58b..29e4aa7 100644 --- a/src/algebra/ExpressionOp.cpp +++ b/src/algebra/ExpressionOp.cpp @@ -22,7 +22,7 @@ IR::TypeArc ExpressionOp::derive(ComputeNode::Type code, const std::vectorsupportsInlining()) { \ const auto& program = context.getProgram(); \ auto state_expr = context.accessGlobalState(op); \ auto casted_state = IR::CastExpr::build( \ diff --git a/src/algebra/suboperators/expressions/ExpressionHelpers.cpp b/src/algebra/suboperators/expressions/ExpressionHelpers.cpp index 4b6eff2..1f66501 100644 --- a/src/algebra/suboperators/expressions/ExpressionHelpers.cpp +++ b/src/algebra/suboperators/expressions/ExpressionHelpers.cpp @@ -18,7 +18,9 @@ const std::unordered_map expr_names{ {Type::Or, "or"}, {Type::Eq, "eq"}, {Type::Neq, "neq"}, - {Type::StrEquals, "streq"}}; + {Type::StrEquals, "streq"}, + {Type::InList, "string_in"}, +}; /// Map from algebra expression types to IR expressions in the jitted code. const std::unordered_map code_map{ @@ -34,7 +36,8 @@ const std::unordered_map code_map{ {Type::And, IR::ArithmeticExpr::Opcode::And}, {Type::Or, IR::ArithmeticExpr::Opcode::Or}, {Type::Neq, IR::ArithmeticExpr::Opcode::Neq}, - {Type::StrEquals, IR::ArithmeticExpr::Opcode::StrEquals} + {Type::StrEquals, IR::ArithmeticExpr::Opcode::StrEquals}, + {Type::InList, IR::ArithmeticExpr::Opcode::StrInList} }; } diff --git a/src/codegen/Expression.h b/src/codegen/Expression.h index 965e061..349ddb0 100644 --- a/src/codegen/Expression.h +++ b/src/codegen/Expression.h @@ -120,8 +120,10 @@ struct ArithmeticExpr : public BinaryExpr { Greater, GreaterEqual, HashCombine, - /// String equals - not really an arithmethic function, but easiest to put here for now. + /// String equals - not really an arithmetic function, but easiest to put here for now. StrEquals, + /// In list with strings - not really an arithmetic function, but easiest to put here for now. + StrInList, }; /// Opcode of this expression. diff --git a/src/codegen/Value.h b/src/codegen/Value.h index 919a495..a5d2afb 100644 --- a/src/codegen/Value.h +++ b/src/codegen/Value.h @@ -11,6 +11,8 @@ namespace IR { /// Value of a certain type struct Value { virtual TypeArc getType() const = 0; + + virtual bool supportsInlining() const { return true; } virtual std::string str() const { return ""; }; virtual std::unique_ptr copy() = 0; @@ -184,7 +186,60 @@ struct StringVal : public Value { } private: - StringVal(std::string value): value(std::move(value)), value_ptr(this->value.data()) { + StringVal(std::string value) : value(std::move(value)), value_ptr(this->value.data()) { + } +}; + +/// A list of strings.Can be extended to a general-purpose value list in the future. +struct StringList : public Value { + struct StringListView { + const char** start; + uint64_t size; + }; + + static ValuePtr build(std::vector strings_) { + return ValuePtr(new StringList(std::move(strings_))); + } + + TypeArc getType() const override { + // Pointer behind which the actual `StringListView` hides. + return IR::Pointer::build(IR::Char::build()); + }; + + // String list cannot be inlined. It needs to stay an abstract char*. + bool supportsInlining() const override { return false; }; + + std::string str() const override { + throw std::runtime_error("str() on StringList not implemented"); + }; + + std::unique_ptr copy() override { + return StringList::build(strings); + }; + + void* rawData() override { + // Return the raw state that can be interpreted by the runtime. + assert(raw_view.size < 1000); + assert(erased_view == &raw_view); + return &erased_view; + } + + std::vector strings; + std::unique_ptr raw_chars; + StringListView raw_view; + void* erased_view; + + private: + StringList(std::vector strings_) : strings(std::move(strings_)) { + raw_chars = std::make_unique(strings.size()); + for (size_t k = 0; k < strings.size(); ++k) { + raw_chars[k] = strings[k].c_str(); + } + raw_view = StringListView{ + .start = raw_chars.get(), + .size = strings.size(), + }; + erased_view = &raw_view; } }; diff --git a/src/codegen/backend_c/BackendC.cpp b/src/codegen/backend_c/BackendC.cpp index cbcd5ff..16cc166 100644 --- a/src/codegen/backend_c/BackendC.cpp +++ b/src/codegen/backend_c/BackendC.cpp @@ -211,7 +211,7 @@ std::unique_ptr BackendC::generate(const IR::Program& progra ScopedWriter writer; // Step 1: Set up the preamble. - createPreamble(writer); + createPreamble(writer, program.program_name == "global_runtime"); // Step 2: Set up the includes. for (const auto& include : program.getIncludes()) { @@ -231,12 +231,16 @@ std::unique_ptr BackendC::generate(const IR::Program& progra return std::make_unique(*this, writer.str(), program.program_name); } -void BackendC::createPreamble(ScopedWriter& writer) { +void BackendC::createPreamble(ScopedWriter& writer, bool is_runtime) { // Include the integer types needed. writer.stmt(false).stream() << "#include "; // We need to access strcmp. writer.stmt(false).stream() << "#include "; writer.stmt(false).stream() << "#include \n"; + + if (is_runtime) { + writer.stmt(false).stream() << runtime_functions; + } } void BackendC::compileInclude(const IR::Program& include, ScopedWriter& writer) { @@ -493,6 +497,12 @@ void BackendC::compileExpression(const IR::Expr& expr, ScopedWriter::Statement& stmt.stream() << ", "; compileExpression(*type.children[1], stmt); stmt.stream() << ") == 0)"; + } else if (type.code == IR::ArithmeticExpr::Opcode::StrInList) { + stmt.stream() << "in_strlist("; + compileExpression(*type.children[0], stmt); + stmt.stream() << ", "; + compileExpression(*type.children[1], stmt); + stmt.stream() << ")"; } else { // Regular arithmethic operation that's directly supported by C. assert(opcode_map.count(type.code)); diff --git a/src/codegen/backend_c/BackendC.h b/src/codegen/backend_c/BackendC.h index 94cdfbe..a582b95 100644 --- a/src/codegen/backend_c/BackendC.h +++ b/src/codegen/backend_c/BackendC.h @@ -60,11 +60,14 @@ struct BackendC : public IR::Backend { std::unique_ptr generate(const IR::Program& program) override; private: + /// Runtime function implementations that are added to the global runtime implementation. + static char* runtime_functions; + /// Compile an included other program. void compileInclude(const IR::Program& include, ScopedWriter& writer); /// Set up the preamble. - static void createPreamble(ScopedWriter& writer); + static void createPreamble(ScopedWriter& writer, bool is_runtime); /// Add a type description to the backing string stream. static void typeDescription(const IR::Type& type, ScopedWriter::Statement& writer); diff --git a/src/codegen/backend_c/FunctionsC.cpp b/src/codegen/backend_c/FunctionsC.cpp new file mode 100644 index 0000000..1f43915 --- /dev/null +++ b/src/codegen/backend_c/FunctionsC.cpp @@ -0,0 +1,22 @@ +#include "BackendC.h" +namespace inkfuse { + +/// C code that's dumped into global_runtime.h during initial runtime +/// code generation. +char* BackendC::runtime_functions = R"PRE( +struct InLiteralList { + const char** start; + uint64_t size; +}; + +bool in_strlist(const char* strlist, char* arg) { + const struct InLiteralList* list = (const struct InLiteralList*) strlist; + bool res = false; + for (uint64_t k = 0; k < list->size; ++k) { + res |= (strcmp(list->start[k], arg) == 0); + } + return res; +} +)PRE"; + +}; diff --git a/src/common/TPCH.cpp b/src/common/TPCH.cpp index b865f26..07da0f8 100644 --- a/src/common/TPCH.cpp +++ b/src/common/TPCH.cpp @@ -21,6 +21,32 @@ using Node = ExpressionOp::Node; using ComputeNode = ExpressionOp::ComputeNode; using IURefNode = ExpressionOp::IURefNode; +// Utility to make it easier to build expressions. +template +Node* Concatenate( + std::vector& pred_nodes, + std::vector + or_nodes) { + static_assert(type == ComputeNode::Type::Or || type == ComputeNode::Type::And); + assert(or_nodes.size() >= 2); + Node* curr_top = pred_nodes.emplace_back( + std::make_unique( + type, + std::vector{or_nodes[0], or_nodes[1]})) + .get(); + for (auto node = or_nodes.begin() + 2; node < or_nodes.end(); ++node) { + curr_top = pred_nodes.emplace_back( + std::make_unique( + type, + std::vector{curr_top, *node})) + .get(); + } + return curr_top; +} + +constexpr auto Andify = Concatenate; +constexpr auto Orify = Concatenate; + // Types used throughout TPC-H. const auto t_i4 = IR::SignedInt::build(4); const auto t_f8 = IR::Float::build(8); @@ -733,7 +759,7 @@ std::unique_ptr q5(const Schema& schema) { JoinType::Inner, true); auto& n_c_join_ref = *n_c_join; - assert(n_c_join_ref.getOutput().size() == 3); + assert(n_c_join_ref.getOutput().size() == 4); // 6.1 Scan from orders. auto& orders_rel = schema.at("orders"); @@ -883,7 +909,7 @@ std::unique_ptr q5(const Schema& schema) { JoinType::Inner, true); auto& s_l_join_ref = *s_l_join; - assert(s_l_join_ref.getOutput().size() == 6); + assert(s_l_join_ref.getOutput().size() == 7); // 12. Aggregate (n_name, sum(l_extendedprice * (1 - l_discount))) // 12.1 Expression @@ -1360,6 +1386,404 @@ std::unique_ptr q18(const Schema& schema) { std::move(out_ius), std::move(colnames)); } +std::unique_ptr q19(const Schema& schema) { + // Build a branch for a part condition. + auto build_part_branch = []( + std::vector& pred_nodes, + Node* p_brand_ref, + Node* p_size_ref, + Node* p_container_ref, + std::string brand, + std::pair + size_between, + std::vector + container_list) -> Node* { + auto p_brand_pred = pred_nodes.emplace_back( + std::make_unique( + ComputeNode::Type::StrEquals, + IR::StringVal::build(brand), + p_brand_ref)) + .get(); + auto p_size_pred_1 = pred_nodes.emplace_back( + std::make_unique( + ComputeNode::Type::GreaterEqual, + IR::SI<4>::build(size_between.second), + p_size_ref)) + .get(); + auto p_size_pred_2 = pred_nodes.emplace_back( + std::make_unique( + ComputeNode::Type::LessEqual, + IR::SI<4>::build(size_between.first), + p_size_ref)) + .get(); + + auto p_container_pred = pred_nodes.emplace_back( + std::make_unique( + ComputeNode::Type::InList, + IR::StringList::build(container_list), + p_container_ref)) + .get(); + + return Andify(pred_nodes, + {p_size_pred_1, p_size_pred_2, p_brand_pred, p_container_pred}); + }; + + // Build a branch for a lineitem condition. + auto build_lineitem_branch = []( + std::vector& pred_nodes, + Node* l_quantity_ref, + std::pair + l_quantity_between) -> Node* { + auto l_quantity_pred_1 = pred_nodes.emplace_back( + std::make_unique( + ComputeNode::Type::GreaterEqual, + IR::F8::build(l_quantity_between.second + 0.001), + l_quantity_ref)) + .get(); + auto l_quantity_pred_2 = pred_nodes.emplace_back( + std::make_unique( + ComputeNode::Type::LessEqual, + IR::F8::build(l_quantity_between.first - 0.001), + l_quantity_ref)) + .get(); + + return Andify(pred_nodes, + {l_quantity_pred_1, l_quantity_pred_2}); + }; + + // 1. Scan part. + auto& rel_p = schema.at("part"); + std::vector cols_p{ + "p_partkey", + "p_brand", + "p_container", + "p_size", + }; + auto scan_p = TableScan::build(*rel_p, cols_p, "scan_part"); + auto& scan_p_ref = *scan_p; + + // 2. Pushed down filter on part. + std::vector pred_nodes_p; + auto p_partkey_ref = pred_nodes_p.emplace_back(std::make_unique( + scan_p_ref.getOutput()[getScanIndex("p_partkey", cols_p)])) + .get(); + auto p_brand_ref = pred_nodes_p.emplace_back(std::make_unique( + scan_p_ref.getOutput()[getScanIndex("p_brand", cols_p)])) + .get(); + auto p_container_ref = pred_nodes_p.emplace_back(std::make_unique( + scan_p_ref.getOutput()[getScanIndex("p_container", cols_p)])) + .get(); + auto p_size_ref = pred_nodes_p.emplace_back(std::make_unique( + scan_p_ref.getOutput()[getScanIndex("p_size", cols_p)])) + .get(); + + // OR branch 1 + Node* scan_p_branch_1 = build_part_branch( + pred_nodes_p, + p_brand_ref, + p_size_ref, + p_container_ref, + "Brand#12", + {1, 5}, + {"SM CASE", "SM BOX", "SM PACK", "SM PKG"}); + // OR branch 1 + Node* scan_p_branch_2 = build_part_branch( + pred_nodes_p, + p_brand_ref, + p_size_ref, + p_container_ref, + "Brand#23", + {1, 10}, + {"MED BAG", "MED BOX", "MED PKG", "MED PACK"}); + // OR branch 1 + Node* scan_p_branch_3 = build_part_branch( + pred_nodes_p, + p_brand_ref, + p_size_ref, + p_container_ref, + "Brand#34", + {1, 15}, + {"LG CASE", "LG BOX", "LG PACK", "LG PKG"}); + + // OR it all together. + auto scan_p_filter = Orify( + pred_nodes_p, {scan_p_branch_1, scan_p_branch_2, scan_p_branch_3}); + + std::vector expr_p_children; + expr_p_children.push_back(std::move(scan_p)); + auto expr_p_node = ExpressionOp::build( + std::move(expr_p_children), + "part_filter", + std::vector{scan_p_filter}, + std::move(pred_nodes_p)); + auto& expr_p_ref = *expr_p_node; + assert(expr_p_ref.getOutput().size() == 1); + + std::vector filter_p_children; + filter_p_children.push_back(std::move(expr_p_node)); + std::vector filter_p_redefined{ + scan_p_ref.getOutput()[0], + scan_p_ref.getOutput()[1], + scan_p_ref.getOutput()[2], + scan_p_ref.getOutput()[3], + }; + auto filter_p = Filter::build( + std::move(filter_p_children), + "filter_p", + std::move(filter_p_redefined), + *expr_p_ref.getOutput()[0]); + auto& filter_p_ref = *filter_p; + assert(filter_p->getOutput().size() == 4); + + // 3. Scan lineitem. + auto& rel_l = schema.at("lineitem"); + std::vector cols_l{ + "l_partkey", + "l_shipmode", + "l_quantity", + "l_shipinstruct", + "l_discount", + "l_extendedprice", + }; + + // 4. Pushed down lineitem filter. + auto scan_l = TableScan::build(*rel_l, cols_l, "scan_lineitem"); + auto& scan_l_ref = *scan_l; + + std::vector pred_nodes_l; + auto l_partkey_ref = pred_nodes_l.emplace_back(std::make_unique( + scan_l_ref.getOutput()[getScanIndex("l_partkey", cols_l)])) + .get(); + auto l_shipmode_ref = pred_nodes_l.emplace_back(std::make_unique( + scan_l_ref.getOutput()[getScanIndex("l_shipmode", cols_l)])) + .get(); + auto l_quantity_ref = pred_nodes_l.emplace_back(std::make_unique( + scan_l_ref.getOutput()[getScanIndex("l_quantity", cols_l)])) + .get(); + auto l_shipinstruct_ref = pred_nodes_l.emplace_back(std::make_unique( + scan_l_ref.getOutput()[getScanIndex("l_shipinstruct", cols_l)])) + .get(); + + auto l_shipinstruct_pred = pred_nodes_l.emplace_back( + std::make_unique( + ComputeNode::Type::StrEquals, + IR::StringVal::build("DELIVER IN PERSON"), + l_shipinstruct_ref)) + .get(); + auto l_shipmode_pred = pred_nodes_l.emplace_back( + std::make_unique( + ComputeNode::Type::InList, + IR::StringList::build({"AIR", "AIR REG"}), + l_shipmode_ref)) + .get(); + + // OR branch 1 + Node* scan_l_branch_1 = build_lineitem_branch( + pred_nodes_l, + l_quantity_ref, + {1, 11}); + // OR branch 1 + + // OR branch 2 + Node* scan_l_branch_2 = build_lineitem_branch( + pred_nodes_l, + l_quantity_ref, + {10, 20}); + + Node* scan_l_branch_3 = build_lineitem_branch( + pred_nodes_l, + l_quantity_ref, + {20, 30}); + + auto l_common_or = Orify(pred_nodes_l, {scan_l_branch_1, scan_l_branch_2, scan_l_branch_3}); + + auto l_common_and = Andify(pred_nodes_l, {l_shipinstruct_pred, l_shipmode_pred, l_common_or}); + + std::vector expr_l_children; + expr_l_children.push_back(std::move(scan_l)); + auto expr_l_node = ExpressionOp::build( + std::move(expr_l_children), + "lineitem_filter", + std::vector{l_common_and}, + std::move(pred_nodes_l)); + auto& expr_l_ref = *expr_l_node; + assert(expr_l_ref.getOutput().size() == 1); + + std::vector filter_l_children; + filter_l_children.push_back(std::move(expr_l_node)); + std::vector filter_l_redefined{ + scan_l_ref.getOutput()[0], + scan_l_ref.getOutput()[2], + // l_shipinstruct and l_shipmode are no longer needed + scan_l_ref.getOutput()[4], + scan_l_ref.getOutput()[5], + }; + auto filter_l = Filter::build( + std::move(filter_l_children), + "filter_l", + std::move(filter_l_redefined), + *expr_l_ref.getOutput()[0]); + auto& filter_l_ref = *filter_l; + assert(filter_l->getOutput().size() == 4); + + // 5. Join the two + std::vector p_l_join_children; + p_l_join_children.push_back(std::move(filter_p)); + p_l_join_children.push_back(std::move(filter_l)); + auto p_l_join = Join::build( + std::move(p_l_join_children), + "p_l_join", + // Keys left (p_partkey) + {filter_p_ref.getOutput()[0]}, + // Payload left (p_brand, p_container, p_size) + { + filter_p_ref.getOutput()[1], + filter_p_ref.getOutput()[2], + filter_p_ref.getOutput()[3], + }, + // Keys right (l_partkey) + {filter_l_ref.getOutput()[0]}, + // Payload right (l_quantity, l_discount, l_extendedprice) + { + filter_l_ref.getOutput()[1], + filter_l_ref.getOutput()[2], + filter_l_ref.getOutput()[3], + }, + JoinType::Inner, + true); + auto& p_l_join_ref = *p_l_join; + + // 6. Filter again, we need to make sure the right tuples survived. + std::vector pred_nodes_p_l; + auto p_l_brand_ref = pred_nodes_p_l.emplace_back(std::make_unique( + p_l_join_ref.getOutput()[1])) + .get(); + auto p_l_container_ref = pred_nodes_p_l.emplace_back(std::make_unique( + p_l_join_ref.getOutput()[2])) + .get(); + auto p_l_size_ref = pred_nodes_p_l.emplace_back(std::make_unique( + p_l_join_ref.getOutput()[3])) + .get(); + auto p_l_quantity_ref = pred_nodes_p_l.emplace_back(std::make_unique( + p_l_join_ref.getOutput()[5])) + .get(); + + // Or branch 1 + Node* p_l_branch_1_l = build_lineitem_branch( + pred_nodes_p_l, + p_l_quantity_ref, + {1, 11}); + Node* p_l_branch_1_p = build_part_branch( + pred_nodes_p_l, + p_l_brand_ref, + p_l_size_ref, + p_l_container_ref, + "Brand#12", + {1, 5}, + {"SM CASE", "SM BOX", "SM PACK", "SM PKG"}); + auto p_l_branch_1 = Andify(pred_nodes_p_l, {p_l_branch_1_l, p_l_branch_1_p}); + + // Or branch 2 + Node* p_l_branch_2_l = build_lineitem_branch( + pred_nodes_p_l, + p_l_quantity_ref, + {10, 20}); + Node* p_l_branch_2_p = build_part_branch( + pred_nodes_p_l, + p_l_brand_ref, + p_l_size_ref, + p_l_container_ref, + "Brand#23", + {1, 10}, + {"MED BAG", "MED BOX", "MED PKG", "MED PACK"}); + auto p_l_branch_2 = Andify(pred_nodes_p_l, {p_l_branch_2_l, p_l_branch_2_p}); + + // Or branch 3 + Node* p_l_branch_3_l = build_lineitem_branch( + pred_nodes_p_l, + p_l_quantity_ref, + {20, 30}); + Node* p_l_branch_3_p = build_part_branch( + pred_nodes_p_l, + p_l_brand_ref, + p_l_size_ref, + p_l_container_ref, + "Brand#34", + {1, 15}, + {"LG CASE", "LG BOX", "LG PACK", "LG PKG"}); + auto p_l_branch_3 = Andify(pred_nodes_p_l, {p_l_branch_3_l, p_l_branch_3_p}); + + auto p_l_common_or = Orify(pred_nodes_p_l, {p_l_branch_1, p_l_branch_2, p_l_branch_3}); + + std::vector expr_p_l_children; + expr_p_l_children.push_back(std::move(p_l_join)); + auto expr_p_l_node = ExpressionOp::build( + std::move(expr_p_l_children), + "final_filter", + std::vector{p_l_common_or}, + std::move(pred_nodes_p_l)); + auto& expr_p_l_ref = *expr_p_l_node; + assert(expr_p_l_ref.getOutput().size() == 1); + + std::vector filter_p_l_children; + filter_p_l_children.push_back(std::move(expr_p_l_node)); + std::vector filter_p_l_redefined{ + // l_discount, l_extendedprice + p_l_join_ref.getOutput()[6], + p_l_join_ref.getOutput()[7], + }; + auto filter_p_l = Filter::build( + std::move(filter_p_l_children), + "filter_p_l", + std::move(filter_p_l_redefined), + *expr_p_l_ref.getOutput()[0]); + auto& filter_p_l_ref = *filter_p_l; + assert(filter_p_l->getOutput().size() == 2); + + // 7. Aggregate the result. + // 7.1 Compute (l_extendedprice * (1 - l_discount)) + std::vector agg_nodes; + agg_nodes.emplace_back(std::make_unique(filter_p_l_ref.getOutput()[0])); + agg_nodes.emplace_back(std::make_unique(filter_p_l_ref.getOutput()[1])); + agg_nodes.emplace_back(std::make_unique(ComputeNode::Type::Subtract, IR::F8::build(1.0), agg_nodes[0].get())); + agg_nodes.emplace_back(std::make_unique(ComputeNode::Type::Multiply, std::vector{agg_nodes[1].get(), agg_nodes[2].get()})); + auto agg_nodes_root = agg_nodes[3].get(); + std::vector agg_e_children; + agg_e_children.push_back(std::move(filter_p_l)); + auto agg_e = ExpressionOp::build( + std::move(agg_e_children), + "agg_expr", + {agg_nodes_root}, + std::move(agg_nodes)); + auto& agg_e_ref = *agg_e; + + // 7.2 Perform the aggregation + std::vector agg_children; + agg_children.push_back(std::move(agg_e)); + // Empty group-by key + std::vector group_by{}; + std::vector aggregates{ + {*agg_e_ref.getOutput()[0], AggregateFunctions::Opcode::Sum}}; + auto agg = Aggregation::build( + std::move(agg_children), + "agg", + std::move(group_by), + std::move(aggregates)); + + // Print result. + std::vector out_ius{ + agg->getOutput()[0], + }; + std::vector colnames = { + "revenue", + }; + std::vector print_children; + print_children.push_back(std::move(agg)); + return Print::build(std::move(print_children), + std::move(out_ius), std::move(colnames)); + +} + std::unique_ptr l_count(const inkfuse::Schema& schema) { // 1. Scan from lineitem. auto& rel = schema.at("lineitem"); @@ -1436,5 +1860,4 @@ std::unique_ptr l_point(const inkfuse::Schema& schema) { return Print::build(std::move(print_children), std::move(out_ius), std::move(colnames)); } - } diff --git a/src/common/TPCH.h b/src/common/TPCH.h index 821ded2..0df550a 100644 --- a/src/common/TPCH.h +++ b/src/common/TPCH.h @@ -30,6 +30,8 @@ std::unique_ptr q6(const Schema& schema); std::unique_ptr q14(const Schema& schema); /// High cardinality aggregation. std::unique_ptr q18(const Schema& schema); +/// Large computational graphs. +std::unique_ptr q19(const Schema& schema); /// Some interesting custom queries. See /tpch for query text. std::unique_ptr l_count(const Schema& schema); diff --git a/src/interpreter/RuntimeExpressionFragmentizer.cpp b/src/interpreter/RuntimeExpressionFragmentizer.cpp index 1a976d0..d67c469 100644 --- a/src/interpreter/RuntimeExpressionFragmentizer.cpp +++ b/src/interpreter/RuntimeExpressionFragmentizer.cpp @@ -47,6 +47,16 @@ RuntimeExpressionFragmentizer::RuntimeExpressionFragmentizer() auto& op = pipe.attachSuboperator(RuntimeExpressionSubop::build(nullptr, {&iu_out}, {&iu_1}, Type::StrEquals, type)); name = op.id(); } + { + // in list strings + auto type_runtime_param = IR::Pointer::build(IR::Char::build()); + auto type = IR::String::build(); + auto& [name, pipe] = pipes.emplace_back(); + auto& iu_1 = generated_ius.emplace_back(type, ""); + auto& iu_out = generated_ius.emplace_back(ExpressionOp::derive(Type::StrEquals, {type}), ""); + auto& op = pipe.attachSuboperator(RuntimeExpressionSubop::build(nullptr, {&iu_out}, {&iu_1}, Type::InList, type_runtime_param)); + name = op.id(); + } } } diff --git a/test/tpch/test_queries.cpp b/test/tpch/test_queries.cpp index 5b9e04a..3abd1e2 100644 --- a/test/tpch/test_queries.cpp +++ b/test/tpch/test_queries.cpp @@ -37,6 +37,7 @@ const std::unordered_map generator_map{ {"q6", tpch::q6}, {"q14", tpch::q14}, {"q18", tpch::q18}, + {"q19", tpch::q19}, {"l_count", tpch::l_count}, {"l_point", tpch::l_point}, }; @@ -51,6 +52,7 @@ std::unordered_map expected_rows{ {"q6", 1}, {"q14", 1}, {"q18", 0}, + {"q19", 0}, {"l_count", 1}, {"l_point", 6}, }; @@ -74,7 +76,7 @@ INSTANTIATE_TEST_CASE_P( tpch_queries, TPCHQueriesTestT, ::testing::Combine( - ::testing::Values("q1", "q3", "q4", "q5", "q6", "q14", "q18", "l_count", "l_point"), + ::testing::Values("q1", "q3", "q4", "q5", "q6", "q14", "q18", "q19", "l_count", "l_point"), ::testing::Values( PipelineExecutor::ExecutionMode::Fused, PipelineExecutor::ExecutionMode::Interpreted, diff --git a/tools/inkfuse_bench.cpp b/tools/inkfuse_bench.cpp index c45243b..e4c58d4 100644 --- a/tools/inkfuse_bench.cpp +++ b/tools/inkfuse_bench.cpp @@ -42,6 +42,7 @@ const std::vector> queries = { {"q6", tpch::q6}, {"q14", tpch::q14}, {"q18", tpch::q18}, + {"q19", tpch::q19}, {"l_count", tpch::l_count}, {"l_point", tpch::l_point}, }; diff --git a/tools/inkfuse_runner.cpp b/tools/inkfuse_runner.cpp index 19524fb..f89af5a 100644 --- a/tools/inkfuse_runner.cpp +++ b/tools/inkfuse_runner.cpp @@ -195,6 +195,9 @@ int main(int argc, char* argv[]) { } else if (split[1] == "q18") { auto q = tpch::q18(*loaded); runQuery("q18", std::move(q), mode, thread_count); + } else if (split[1] == "q19") { + auto q = tpch::q19(*loaded); + runQuery("q19", std::move(q), mode, thread_count); } else if (split[1] == "l_count") { auto q = tpch::l_count(*loaded); runQuery("l_count", std::move(q), mode, thread_count); @@ -202,7 +205,7 @@ int main(int argc, char* argv[]) { auto q = tpch::l_point(*loaded); runQuery("l_point", std::move(q), mode, thread_count); } else { - std::cout << "Unrecognized query - we only support {q1, q3, q4, q5, q6, q14, q18, l_count, l_point}\n"; + std::cout << "Unrecognized query - we only support {q1, q3, q4, q5, q6, q14, q18, q19, l_count, l_point}\n"; } } } else {