diff --git a/ci/run_cudf_benchmark_smoketests.sh b/ci/run_cudf_benchmark_smoketests.sh index c9e9873a6f3d..959003aec68f 100755 --- a/ci/run_cudf_benchmark_smoketests.sh +++ b/ci/run_cudf_benchmark_smoketests.sh @@ -1,9 +1,11 @@ #!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 set -euo pipefail +repo_root="$(dirname "$(realpath "${BASH_SOURCE[0]}")")/.." + # Support customizing the benchmarks' install location # First, try the installed location (CI/conda environments) installed_benchmark_location="${INSTALL_PREFIX:-${CONDA_PREFIX:-/usr}}/bin/benchmarks/libcudf/" @@ -22,12 +24,22 @@ else fi EXITCODE=0 +validation_dir="$(mktemp -d)" +ndsh_scale_factor=1 +trap 'rm -rf "${validation_dir}"' EXIT # Run all nvbench benchmarks with --profile and rmm_mode=cuda for bench in *_NVBENCH; do if [[ -x "$bench" && -f "$bench" ]]; then start_time=$(date +%s) echo "Running $bench with --profile..." - "./$bench" --profile --devices 0 -q --rmm_mode cuda + args=(--profile --devices 0 -q --rmm_mode cuda) + if [[ "$bench" == NDSH_* ]]; then + args+=(--axis "scale_factor=${ndsh_scale_factor}") + if [[ "$bench" =~ ^NDSH_Q([0-9]{2})_NVBENCH$ ]]; then + args+=(--output_directory "${validation_dir}/q${BASH_REMATCH[1]}") + fi + fi + "./$bench" "${args[@]}" SUITEERROR=$? end_time=$(date +%s) duration=$((end_time - start_time)) @@ -40,5 +52,10 @@ for bench in *_NVBENCH; do fi done +python "${repo_root}/ci/validate_ndsh_benchmarks.py" \ + --output-dir "${validation_dir}" \ + --sql-dir "${repo_root}/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql" \ + --scale-factor "${ndsh_scale_factor}" + echo "Test script exiting with value: $EXITCODE" exit ${EXITCODE} diff --git a/ci/validate_ndsh_benchmarks.py b/ci/validate_ndsh_benchmarks.py new file mode 100644 index 000000000000..f0ec0c722943 --- /dev/null +++ b/ci/validate_ndsh_benchmarks.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import argparse +import math +import numbers +from pathlib import Path + +import duckdb + +QUERIES = ( + "q01", + "q02", + "q03", + "q04", + "q05", + "q06", + "q07", + "q08", + "q09", + "q10", + "q11", + "q12", + "q13", + "q14", + "q15", + "q16", + "q17", + "q18", + "q19", + "q20", + "q21", + "q22", +) +EXPECTED_NAMES = { + "q18": [ + "c_name", + "c_custkey", + "o_orderkey", + "o_orderdate", + "o_totalprice", + "sum(l_quantity)", + ] +} + + +def values_equal(actual, expected): + if actual is None or expected is None: + return actual is expected + if isinstance(actual, numbers.Number) and isinstance( + expected, numbers.Number + ): + return math.isclose( + float(actual), float(expected), rel_tol=0.0, abs_tol=0.01 + ) + return actual == expected + + +def validate_query(query_name, sql_dir, output_dir, scale_factor=0.01): + connection = duckdb.connect() + for path in (output_dir / query_name / "input").glob("*.parquet"): + table_name = path.stem.replace('"', '""') + parquet_path = str(path).replace("'", "''") + connection.execute( + f'CREATE VIEW "{table_name}" AS ' + f"SELECT * FROM read_parquet('{parquet_path}')" + ) + + parameters = ( + {"scale_factor": scale_factor} if query_name == "q11" else None + ) + expected = connection.execute( + (sql_dir / f"{query_name}.sql").read_text(), parameters + ) + expected_names = [column[0] for column in expected.description] + expected_rows = expected.fetchall() + + result_path = output_dir / query_name / "results" / f"{query_name}.parquet" + actual = connection.execute( + "SELECT * FROM read_parquet(?)", [str(result_path)] + ) + actual_names = [column[0] for column in actual.description] + actual_rows = actual.fetchall() + + expected_names = EXPECTED_NAMES.get(query_name, expected_names) + if actual_names != expected_names: + return f"column names differ: {actual_names} != {expected_names}" + if len(actual_rows) != len(expected_rows): + return f"row counts differ: {len(actual_rows)} != {len(expected_rows)}" + + for row_index, (actual_row, expected_row) in enumerate( + zip(actual_rows, expected_rows, strict=True) + ): + for column_name, actual_value, expected_value in zip( + actual_names, actual_row, expected_row, strict=True + ): + if not values_equal(actual_value, expected_value): + return ( + f"row {row_index}, column {column_name} differs: " + f"{actual_value!r} != {expected_value!r}" + ) + return None + + +def main(): + parser = argparse.ArgumentParser( + description="Validate NDS-H benchmark Parquet results against DuckDB" + ) + parser.add_argument("--output-dir", type=Path, required=True) + parser.add_argument("--sql-dir", type=Path, required=True) + parser.add_argument("--scale-factor", type=float, default=0.01) + args = parser.parse_args() + + failed = False + for query_name in QUERIES: + error = validate_query( + query_name, args.sql_dir, args.output_dir, args.scale_factor + ) + if error is None: + print(f"{query_name}: PASSED") + else: + failed = True + print(f"{query_name}: FAILED: {error}") + + raise SystemExit(failed) + + +if __name__ == "__main__": + main() diff --git a/conda/environments/all_cuda-129_arch-aarch64.yaml b/conda/environments/all_cuda-129_arch-aarch64.yaml index 1a7cdc48ac7f..c9c0bab99d6e 100644 --- a/conda/environments/all_cuda-129_arch-aarch64.yaml +++ b/conda/environments/all_cuda-129_arch-aarch64.yaml @@ -32,6 +32,7 @@ dependencies: - dask-cuda==26.10.*,>=0.0.0a0 - dlpack>=0.8,<1.0 - doxygen=1.9.1 +- duckdb - fastavro>=0.22.9 - flatbuffers==24.3.25 - fsspec>=0.6.0 diff --git a/conda/environments/all_cuda-129_arch-x86_64.yaml b/conda/environments/all_cuda-129_arch-x86_64.yaml index a25e0dc81637..56b4907bbccd 100644 --- a/conda/environments/all_cuda-129_arch-x86_64.yaml +++ b/conda/environments/all_cuda-129_arch-x86_64.yaml @@ -32,6 +32,7 @@ dependencies: - dask-cuda==26.10.*,>=0.0.0a0 - dlpack>=0.8,<1.0 - doxygen=1.9.1 +- duckdb - fastavro>=0.22.9 - flatbuffers==24.3.25 - fsspec>=0.6.0 diff --git a/conda/environments/all_cuda-133_arch-aarch64.yaml b/conda/environments/all_cuda-133_arch-aarch64.yaml index 875088c05499..21cd169f820f 100644 --- a/conda/environments/all_cuda-133_arch-aarch64.yaml +++ b/conda/environments/all_cuda-133_arch-aarch64.yaml @@ -32,6 +32,7 @@ dependencies: - dask-cuda==26.10.*,>=0.0.0a0 - dlpack>=0.8,<1.0 - doxygen=1.9.1 +- duckdb - fastavro>=0.22.9 - flatbuffers==24.3.25 - fsspec>=0.6.0 diff --git a/conda/environments/all_cuda-133_arch-x86_64.yaml b/conda/environments/all_cuda-133_arch-x86_64.yaml index fcb76af8b6b2..dc365673eea5 100644 --- a/conda/environments/all_cuda-133_arch-x86_64.yaml +++ b/conda/environments/all_cuda-133_arch-x86_64.yaml @@ -32,6 +32,7 @@ dependencies: - dask-cuda==26.10.*,>=0.0.0a0 - dlpack>=0.8,<1.0 - doxygen=1.9.1 +- duckdb - fastavro>=0.22.9 - flatbuffers==24.3.25 - fsspec>=0.6.0 diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 3066fdf32e77..719f83997d48 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1600,6 +1600,10 @@ if(CUDF_BUILD_BENCHMARKS) add_subdirectory(benchmarks) endif() +if(CUDF_BUILD_TESTS) + rapids_test_install_relocatable(INSTALL_COMPONENT_SET testing DESTINATION bin/gtests/libcudf) +endif() + # ################################################################################################## # * install targets ------------------------------------------------------------------------------- rapids_cmake_install_lib_dir(lib_dir) diff --git a/cpp/benchmarks/CMakeLists.txt b/cpp/benchmarks/CMakeLists.txt index d03bf72ee9e8..76c99b075ff7 100644 --- a/cpp/benchmarks/CMakeLists.txt +++ b/cpp/benchmarks/CMakeLists.txt @@ -55,6 +55,29 @@ target_include_directories( "$" ) +if(CUDF_BUILD_TESTS) + add_executable(NDSH_DATA_GENERATOR_TEST common/ndsh_data_generator/ndsh_data_generator_test.cpp) + set_target_properties( + NDSH_DATA_GENERATOR_TEST + PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$" + INSTALL_RPATH "\$ORIGIN/../../../lib" + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + ) + target_link_libraries( + NDSH_DATA_GENERATOR_TEST PRIVATE ndsh_data_generator cudf::cudftestutil_objects + $ + ) + rapids_cuda_set_runtime(NDSH_DATA_GENERATOR_TEST USE_STATIC ON) + rapids_test_add( + NAME NDSH_DATA_GENERATOR_TEST + COMMAND NDSH_DATA_GENERATOR_TEST + GPUS 1 + PERCENT 15 + INSTALL_COMPONENT_SET testing + ) +endif() + # ################################################################################################## # * compiler function ----------------------------------------------------------------------------- @@ -125,10 +148,27 @@ ConfigureNVBench(TRANSPOSE_NVBENCH transpose/transpose.cpp) # ################################################################################################## # * nds-h benchmark -------------------------------------------------------------------------------- ConfigureNVBench(NDSH_Q01_NVBENCH ndsh/q01.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q02_NVBENCH ndsh/q02.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q03_NVBENCH ndsh/q03.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q04_NVBENCH ndsh/q04.cpp ndsh/utilities.cpp) ConfigureNVBench(NDSH_Q05_NVBENCH ndsh/q05.cpp ndsh/utilities.cpp) ConfigureNVBench(NDSH_Q06_NVBENCH ndsh/q06.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q07_NVBENCH ndsh/q07.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q08_NVBENCH ndsh/q08.cpp ndsh/utilities.cpp) ConfigureNVBench(NDSH_Q09_NVBENCH ndsh/q09.cpp ndsh/utilities.cpp) ConfigureNVBench(NDSH_Q10_NVBENCH ndsh/q10.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q11_NVBENCH ndsh/q11.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q12_NVBENCH ndsh/q12.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q13_NVBENCH ndsh/q13.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q14_NVBENCH ndsh/q14.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q15_NVBENCH ndsh/q15.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q16_NVBENCH ndsh/q16.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q17_NVBENCH ndsh/q17.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q18_NVBENCH ndsh/q18.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q19_NVBENCH ndsh/q19.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q20_NVBENCH ndsh/q20.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q21_NVBENCH ndsh/q21.cpp ndsh/utilities.cpp) +ConfigureNVBench(NDSH_Q22_NVBENCH ndsh/q22.cpp ndsh/utilities.cpp) # ################################################################################################## # * filter benchmark ------------------------------------------------------------------- diff --git a/cpp/benchmarks/common/ndsh_data_generator/ndsh_data_generator_test.cpp b/cpp/benchmarks/common/ndsh_data_generator/ndsh_data_generator_test.cpp new file mode 100644 index 000000000000..932c39a20539 --- /dev/null +++ b/cpp/benchmarks/common/ndsh_data_generator/ndsh_data_generator_test.cpp @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "ndsh_data_generator.hpp" + +#include + +#include +#include + +#include + +struct NDSHDataGeneratorTest : public cudf::test::BaseFixture {}; + +TEST_F(NDSHDataGeneratorTest, ScaleFactorPointZeroOne) +{ + constexpr double scale_factor = 0.01; + + auto [orders, lineitem, part] = cudf::datagen::generate_orders_lineitem_part(scale_factor); + auto partsupp = cudf::datagen::generate_partsupp(scale_factor); + auto supplier = cudf::datagen::generate_supplier(scale_factor); + auto customer = cudf::datagen::generate_customer(scale_factor); + auto nation = cudf::datagen::generate_nation(); + auto region = cudf::datagen::generate_region(); + + auto const expect_cardinality = + [](cudf::table const& table, cudf::size_type rows, cudf::size_type columns) { + EXPECT_EQ(table.num_rows(), rows); + EXPECT_EQ(table.num_columns(), columns); + }; + + expect_cardinality(*orders, 15'000, 9); + EXPECT_GE(lineitem->num_rows(), 15'000); + EXPECT_LE(lineitem->num_rows(), 105'000); + EXPECT_EQ(lineitem->num_columns(), 16); + expect_cardinality(*part, 2'000, 9); + expect_cardinality(*partsupp, 8'000, 5); + expect_cardinality(*supplier, 100, 7); + expect_cardinality(*customer, 1'500, 8); + expect_cardinality(*nation, 25, 4); + expect_cardinality(*region, 5, 3); + + auto const expect_supplier_key_range = [](cudf::column_view const& keys, + cudf::size_type supplier_rows) { + EXPECT_EQ(keys.null_count(), 0); + auto const [minimum, maximum] = cudf::minmax(keys); + auto const min_key = static_cast const*>(minimum.get()); + auto const max_key = static_cast const*>(maximum.get()); + EXPECT_GE(min_key->value(), 1); + EXPECT_LE(max_key->value(), supplier_rows); + }; + + expect_supplier_key_range(lineitem->view().column(2), supplier->num_rows()); + expect_supplier_key_range(partsupp->view().column(1), supplier->num_rows()); +} diff --git a/cpp/benchmarks/common/ndsh_data_generator/table_helpers.cpp b/cpp/benchmarks/common/ndsh_data_generator/table_helpers.cpp index 8510e9d6c623..a40de2d52c2f 100644 --- a/cpp/benchmarks/common/ndsh_data_generator/table_helpers.cpp +++ b/cpp/benchmarks/common/ndsh_data_generator/table_helpers.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -145,13 +145,14 @@ std::unique_ptr perform_left_join(cudf::table_view const& left_inpu * @param mr Device memory resource used to allocate the returned column's device memory */ [[nodiscard]] std::unique_ptr calculate_l_suppkey(cudf::column_view const& l_partkey, - cudf::size_type scale_factor, + double scale_factor, cudf::size_type num_rows, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) { CUDF_BENCHMARK_RANGE(); // Expression: (l_partkey + (i * (s/4 + (int)(l_partkey - 1)/s))) % s + 1 + auto const supplier_count = static_cast(scale_factor * 10'000); // Generate the `s` col auto s_empty = cudf::make_numeric_column( @@ -160,7 +161,7 @@ std::unique_ptr perform_left_join(cudf::table_view const& left_inpu auto s = cudf::fill(s_empty->view(), 0, num_rows, - cudf::numeric_scalar(scale_factor * 10'000), + cudf::numeric_scalar(supplier_count), stream, mr); @@ -217,13 +218,14 @@ std::unique_ptr perform_left_join(cudf::table_view const& left_inpu */ [[nodiscard]] std::unique_ptr calculate_ps_suppkey( cudf::column_view const& ps_partkey, - cudf::size_type scale_factor, + double scale_factor, cudf::size_type num_rows, rmm::cuda_stream_view stream, rmm::device_async_resource_ref mr) { CUDF_BENCHMARK_RANGE(); // Expression: ps_suppkey = (ps_partkey + (i * (s/4 + (int)(ps_partkey - 1)/s))) % s + 1 + auto const supplier_count = static_cast(scale_factor * 10'000); // Generate the `s` col auto s_empty = cudf::make_numeric_column( @@ -232,7 +234,7 @@ std::unique_ptr perform_left_join(cudf::table_view const& left_inpu auto s = cudf::fill(s_empty->view(), 0, num_rows, - cudf::numeric_scalar(scale_factor * 10'000), + cudf::numeric_scalar(supplier_count), stream, mr); diff --git a/cpp/benchmarks/common/ndsh_data_generator/table_helpers.hpp b/cpp/benchmarks/common/ndsh_data_generator/table_helpers.hpp index 055d1c915718..7b6572372e0b 100644 --- a/cpp/benchmarks/common/ndsh_data_generator/table_helpers.hpp +++ b/cpp/benchmarks/common/ndsh_data_generator/table_helpers.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -72,7 +72,7 @@ std::unique_ptr perform_left_join( */ [[nodiscard]] std::unique_ptr calculate_l_suppkey( cudf::column_view const& l_partkey, - cudf::size_type scale_factor, + double scale_factor, cudf::size_type num_rows, rmm::cuda_stream_view stream = cudf::get_default_stream(), rmm::device_async_resource_ref mr = cudf::get_current_device_resource_ref()); @@ -88,7 +88,7 @@ std::unique_ptr perform_left_join( */ [[nodiscard]] std::unique_ptr calculate_ps_suppkey( cudf::column_view const& ps_partkey, - cudf::size_type scale_factor, + double scale_factor, cudf::size_type num_rows, rmm::cuda_stream_view stream = cudf::get_default_stream(), rmm::device_async_resource_ref mr = cudf::get_current_device_resource_ref()); diff --git a/cpp/benchmarks/fixture/nvbench_fixture.hpp b/cpp/benchmarks/fixture/nvbench_fixture.hpp index ad938a358290..d6ed6c788bc4 100644 --- a/cpp/benchmarks/fixture/nvbench_fixture.hpp +++ b/cpp/benchmarks/fixture/nvbench_fixture.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ #pragma once @@ -27,8 +27,13 @@ namespace detail { static std::string rmm_mode_param{"--rmm_mode"}; ///< RMM mode command-line parameter name static std::string cuio_host_mem_param{ "--cuio_host_mem"}; ///< cuio host memory mode parameter name +static std::string output_directory_param{ + "--output_directory"}; ///< Benchmark output directory parameter name +inline std::string output_directory; } // namespace detail +inline std::string const& benchmark_output_directory() { return detail::output_directory; } + /** * Base fixture for cudf benchmarks using nvbench. * @@ -93,6 +98,9 @@ struct nvbench_base_fixture { } else if (arg == detail::cuio_host_mem_param) { i++; cuio_host_mode = argv[i]; + } else if (arg == detail::output_directory_param) { + i++; + detail::output_directory = argv[i]; } } diff --git a/cpp/benchmarks/fixture/nvbench_main.cpp b/cpp/benchmarks/fixture/nvbench_main.cpp index 7277e1869980..ee51703a6444 100644 --- a/cpp/benchmarks/fixture/nvbench_main.cpp +++ b/cpp/benchmarks/fixture/nvbench_main.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2023-2024, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2023-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -12,8 +12,7 @@ namespace cudf { -// strip off the rmm_mode and cuio_host_mem parameters before passing the -// remaining arguments to nvbench::option_parser +// Strip cudf-specific parameters before passing the remaining arguments to nvbench::option_parser. void benchmark_arg_handler(std::vector& args) { std::vector _cudf_tmp_args; @@ -24,6 +23,8 @@ void benchmark_arg_handler(std::vector& args) i++; // skip the next argument } else if (arg == cudf::detail::cuio_host_mem_param) { i++; // skip the next argument + } else if (arg == cudf::detail::output_directory_param) { + i++; // skip the next argument } else { _cudf_tmp_args.push_back(arg); } diff --git a/cpp/benchmarks/ndsh/README.md b/cpp/benchmarks/ndsh/README.md index 0a462e1684e5..99700ade2f7f 100644 --- a/cpp/benchmarks/ndsh/README.md +++ b/cpp/benchmarks/ndsh/README.md @@ -8,4 +8,12 @@ comply with the TPC-H Benchmarks. ## Current Status -For now, only Q1, Q5, Q6, Q9, and Q10 have been implemented +All 22 NDS-H queries are implemented. + +The standard benchmark modes are `end_to_end`, which includes Parquet reads and query execution, +and `compute_only`, which measures query execution using preloaded data. Q9 retains its additional +engine and expression variants. + +Pass `--output_directory ` to write the generated input tables and one query result to +Parquet for validation. Input export, the validation query execution, and result output are not +timed. diff --git a/cpp/benchmarks/ndsh/q01.cpp b/cpp/benchmarks/ndsh/q01.cpp index 0a17d91d0c29..4cf2d4d7b46f 100644 --- a/cpp/benchmarks/ndsh/q01.cpp +++ b/cpp/benchmarks/ndsh/q01.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -95,7 +95,7 @@ disc_price, one_plus_tax->view(), cudf::binary_operator::MUL, tax.type(), stream, mr); } -void run_ndsh_q1(nvbench::state& state, cudf::io::source_info const& source) +std::unique_ptr load_ndsh_q1(cudf::io::source_info const& source) { // Define the column projections and filter predicate for `lineitem` table std::vector const lineitem_cols = {"l_returnflag", @@ -115,7 +115,13 @@ void run_ndsh_q1(nvbench::state& state, cudf::io::source_info const& source) cudf::ast::ast_operator::LESS_EQUAL, shipdate_ref, shipdate_upper_literal); // Read out the `lineitem` table from parquet file - auto lineitem = read_parquet(source, lineitem_cols, std::move(lineitem_pred)); + return read_parquet(source, lineitem_cols, std::move(lineitem_pred)); +} + +std::unique_ptr execute_ndsh_q1(std::unique_ptr const& input) +{ + auto lineitem = std::make_unique(std::make_unique(input->table()), + input->column_names()); // Calculate the discount price and charge columns and append to lineitem table auto disc_price = @@ -147,13 +153,21 @@ void run_ndsh_q1(nvbench::state& state, cudf::io::source_info const& source) {cudf::aggregation::Kind::COUNT_ALL, "count_order"}}}, }}); - // Perform the order by operation - auto const orderedby_table = apply_orderby(groupedby_table, - {"l_returnflag", "l_linestatus"}, - {cudf::order::ASCENDING, cudf::order::ASCENDING}); + auto const projected = apply_projection(groupedby_table, + {"l_returnflag", + "l_linestatus", + "sum_qty", + "sum_base_price", + "sum_disc_price", + "sum_charge", + "avg_qty", + "avg_price", + "avg_disc", + "count_order"}); - // Write query result to a parquet file - orderedby_table->to_parquet("q1.parquet"); + // Perform the order by operation + return apply_orderby( + projected, {"l_returnflag", "l_linestatus"}, {cudf::order::ASCENDING, cudf::order::ASCENDING}); } void ndsh_q1(nvbench::state& state) @@ -161,6 +175,7 @@ void ndsh_q1(nvbench::state& state) // Generate the required parquet files in device buffers auto const scale_factor = state.get_float64("scale_factor"); auto const filename = state.get_string("filename"); + auto const mode = query_mode_from_string(state.get_string("mode")); if (!filename.empty() && scale_factor != 1.0) { state.skip("Only scale_factor=1 supported with filename input"); return; @@ -176,13 +191,32 @@ void ndsh_q1(nvbench::state& state) auto stream = cudf::get_default_stream(); state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + auto lineitem = mode == query_mode::COMPUTE_ONLY ? load_ndsh_q1(source) : nullptr; auto const mem_stats_logger = cudf::memory_stats_logger(); - state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { run_ndsh_q1(state, source); }); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q1(source); + result = execute_ndsh_q1(input); + } else { + result = execute_ndsh_q1(lineitem); + } + }); state.add_buffer_size( mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q1(source); + result = execute_ndsh_q1(input); + } else { + result = execute_ndsh_q1(lineitem); + } + write_ndsh_result(*result, "q01"); } NVBENCH_BENCH(ndsh_q1) .set_name("ndsh_q1") .add_string_axis("filename", {""}) + .add_string_axis("mode", {"end_to_end", "compute_only"}) .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q02.cpp b/cpp/benchmarks/ndsh/q02.cpp new file mode 100644 index 000000000000..a6d856ce64f0 --- /dev/null +++ b/cpp/benchmarks/ndsh/q02.cpp @@ -0,0 +1,173 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include + +#include + +#include + +/** + * @file q02.cpp + * @brief Implement query 2 of the NDS-H benchmark. + * + * select + * s_acctbal, + * s_name, + * n_name, + * p_partkey, + * p_mfgr, + * s_address, + * s_phone, + * s_comment + * from + * part, + * supplier, + * partsupp, + * nation, + * region + * where + * p_partkey = ps_partkey + * and s_suppkey = ps_suppkey + * and p_size = 15 + * and p_type like '%BRASS' + * and s_nationkey = n_nationkey + * and n_regionkey = r_regionkey + * and r_name = 'EUROPE' + * and ps_supplycost = ( + * select + * min(ps_supplycost) + * from + * partsupp, + * supplier, + * nation, + * region + * where + * p_partkey = ps_partkey + * and s_suppkey = ps_suppkey + * and s_nationkey = n_nationkey + * and n_regionkey = r_regionkey + * and r_name = 'EUROPE' + * ) + * order by + * s_acctbal desc, + * n_name, + * s_name, + * p_partkey + * limit 100; + */ + +struct q2_data { + std::unique_ptr nation; + std::unique_ptr part; + std::unique_ptr partsupp; + std::unique_ptr region; + std::unique_ptr supplier; +}; + +q2_data load_ndsh_q2(std::unordered_map& sources) +{ + std::vector const part_columns = {"p_partkey", "p_size", "p_type", "p_mfgr"}; + auto const p_size_ref = cudf::ast::column_reference{1}; + auto p_size = cudf::numeric_scalar{15}; + auto const p_size_literal = cudf::ast::literal{p_size}; + auto part_predicate = std::make_unique( + cudf::ast::ast_operator::EQUAL, p_size_ref, p_size_literal); + + std::vector const region_columns = {"r_regionkey", "r_name"}; + auto const r_name_ref = cudf::ast::column_reference{1}; + auto r_name = cudf::string_scalar{"EUROPE"}; + auto const r_name_literal = cudf::ast::literal{r_name}; + auto region_predicate = std::make_unique( + cudf::ast::ast_operator::EQUAL, r_name_ref, r_name_literal); + + return q2_data{ + read_parquet(sources.at("nation").make_source_info(), {"n_nationkey", "n_regionkey", "n_name"}), + read_parquet(sources.at("part").make_source_info(), part_columns, std::move(part_predicate)), + read_parquet(sources.at("partsupp").make_source_info(), + {"ps_partkey", "ps_suppkey", "ps_supplycost"}), + read_parquet( + sources.at("region").make_source_info(), region_columns, std::move(region_predicate)), + read_parquet( + sources.at("supplier").make_source_info(), + {"s_suppkey", "s_name", "s_address", "s_nationkey", "s_phone", "s_acctbal", "s_comment"})}; +} + +std::unique_ptr execute_ndsh_q2(q2_data const& data) +{ + auto const brass = cudf::string_scalar{"BRASS"}; + auto const mask = + cudf::strings::ends_with(cudf::strings_column_view{data.part->column("p_type")}, brass); + auto const part = apply_mask(data.part, mask); + + auto const join_a = apply_inner_join(part, data.partsupp, {"p_partkey"}, {"ps_partkey"}); + auto const join_b = apply_inner_join(join_a, data.supplier, {"ps_suppkey"}, {"s_suppkey"}); + auto const join_c = apply_inner_join(join_b, data.nation, {"s_nationkey"}, {"n_nationkey"}); + auto const joined = apply_inner_join(join_c, data.region, {"n_regionkey"}, {"r_regionkey"}); + + auto const minimum_cost = apply_groupby( + joined, + groupby_context_t{{"p_partkey"}, + {{"ps_supplycost", {{cudf::aggregation::Kind::MIN, "ps_supplycost"}}}}}); + auto const minimum_suppliers = apply_inner_join( + minimum_cost, joined, {"p_partkey", "ps_supplycost"}, {"p_partkey", "ps_supplycost"}); + auto const selected = apply_projection( + minimum_suppliers, + {"s_acctbal", "s_name", "n_name", "p_partkey", "p_mfgr", "s_address", "s_phone", "s_comment"}); + auto const ordered = apply_orderby(selected, + {"s_acctbal", "n_name", "s_name", "p_partkey"}, + {cudf::order::DESCENDING, + cudf::order::ASCENDING, + cudf::order::ASCENDING, + cudf::order::ASCENDING}); + return apply_slice(ordered, 0, 100); +} + +void ndsh_q2(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources( + scale_factor, {"nation", "part", "partsupp", "region", "supplier"}, sources); + + auto const stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + auto data = + mode == query_mode::COMPUTE_ONLY ? std::optional{load_ndsh_q2(sources)} : std::nullopt; + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q2(sources); + result = execute_ndsh_q2(input); + } else { + result = execute_ndsh_q2(*data); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q2(sources); + result = execute_ndsh_q2(input); + } else { + result = execute_ndsh_q2(*data); + } + write_ndsh_result(*result, "q02"); +} + +NVBENCH_BENCH(ndsh_q2) + .set_name("ndsh_q2") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q03.cpp b/cpp/benchmarks/ndsh/q03.cpp new file mode 100644 index 000000000000..e1f8437bae77 --- /dev/null +++ b/cpp/benchmarks/ndsh/q03.cpp @@ -0,0 +1,165 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include + +#include + +/** + * @file q03.cpp + * @brief Implement query 3 of the NDS-H benchmark. + * + * select + * l_orderkey, + * sum(l_extendedprice * (1 - l_discount)) as revenue, + * o_orderdate, + * o_shippriority + * from + * customer, + * orders, + * lineitem + * where + * c_mktsegment = 'BUILDING' + * and c_custkey = o_custkey + * and l_orderkey = o_orderkey + * and o_orderdate < '1995-03-15' + * and l_shipdate > '1995-03-15' + * group by + * l_orderkey, + * o_orderdate, + * o_shippriority + * order by + * revenue desc, + * o_orderdate + * limit 10; + */ + +[[nodiscard]] std::unique_ptr calculate_revenue( + cudf::column_view const& extendedprice, + cudf::column_view const& discount, + rmm::cuda_stream_view stream = cudf::get_default_stream(), + rmm::device_async_resource_ref mr = cudf::get_current_device_resource_ref()) +{ + auto const one = cudf::numeric_scalar(1); + auto const one_minus_discount = + cudf::binary_operation(one, discount, cudf::binary_operator::SUB, discount.type(), stream, mr); + return cudf::binary_operation(extendedprice, + one_minus_discount->view(), + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}, + stream, + mr); +} + +std::unordered_map> load_ndsh_q3( + std::unordered_map& sources) +{ + std::vector const customer_cols = {"c_custkey", "c_mktsegment"}; + auto const c_mktsegment_ref = cudf::ast::column_reference(1); + auto building = cudf::string_scalar("BUILDING"); + auto const building_literal = cudf::ast::literal(building); + auto const customer_pred = std::make_unique( + cudf::ast::ast_operator::EQUAL, c_mktsegment_ref, building_literal); + + std::vector const orders_cols = { + "o_custkey", "o_orderkey", "o_orderdate", "o_shippriority"}; + auto const o_orderdate_ref = cudf::ast::column_reference(2); + auto orderdate_limit = + cudf::timestamp_scalar(days_since_epoch(1995, 3, 15), true); + auto const orderdate_literal = cudf::ast::literal(orderdate_limit); + auto const orders_pred = std::make_unique( + cudf::ast::ast_operator::LESS, o_orderdate_ref, orderdate_literal); + + std::vector const lineitem_cols = { + "l_orderkey", "l_extendedprice", "l_discount", "l_shipdate"}; + auto const l_shipdate_ref = cudf::ast::column_reference(3); + auto shipdate_limit = + cudf::timestamp_scalar(days_since_epoch(1995, 3, 15), true); + auto const shipdate_literal = cudf::ast::literal(shipdate_limit); + auto const lineitem_pred = std::make_unique( + cudf::ast::ast_operator::GREATER, l_shipdate_ref, shipdate_literal); + + std::unordered_map> tables; + tables.emplace( + "customer", + read_parquet( + sources.at("customer").make_source_info(), customer_cols, std::move(customer_pred))); + tables.emplace( + "orders", + read_parquet(sources.at("orders").make_source_info(), orders_cols, std::move(orders_pred))); + tables.emplace( + "lineitem", + read_parquet( + sources.at("lineitem").make_source_info(), lineitem_cols, std::move(lineitem_pred))); + return tables; +} + +std::unique_ptr execute_ndsh_q3( + std::unordered_map> const& tables) +{ + auto const customer_orders = + apply_inner_join(tables.at("customer"), tables.at("orders"), {"c_custkey"}, {"o_custkey"}); + auto joined = + apply_inner_join(customer_orders, tables.at("lineitem"), {"o_orderkey"}, {"l_orderkey"}); + + auto revenue = calculate_revenue(joined->column("l_extendedprice"), joined->column("l_discount")); + joined->append(revenue, "revenue"); + + auto const grouped = + apply_groupby(joined, + groupby_context_t{{"l_orderkey", "o_orderdate", "o_shippriority"}, + {{"revenue", {{cudf::aggregation::Kind::SUM, "revenue"}}}}}); + auto const ordered = apply_orderby( + grouped, {"revenue", "o_orderdate"}, {cudf::order::DESCENDING, cudf::order::ASCENDING}); + auto const top_ten = apply_slice(ordered, 0, 10); + return apply_projection(top_ten, {"l_orderkey", "revenue", "o_orderdate", "o_shippriority"}); +} + +void ndsh_q3(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"customer", "orders", "lineitem"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q3(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q3(sources); + result = execute_ndsh_q3(input); + } else { + result = execute_ndsh_q3(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q3(sources); + result = execute_ndsh_q3(input); + } else { + result = execute_ndsh_q3(tables); + } + write_ndsh_result(*result, "q03"); +} + +NVBENCH_BENCH(ndsh_q3) + .set_name("ndsh_q3") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q04.cpp b/cpp/benchmarks/ndsh/q04.cpp new file mode 100644 index 000000000000..cf98238e0785 --- /dev/null +++ b/cpp/benchmarks/ndsh/q04.cpp @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include + +#include + +/** + * @file q04.cpp + * @brief Implement query 4 of the NDS-H benchmark. + * + * select + * o_orderpriority, + * count(*) as order_count + * from + * orders + * where + * o_orderdate >= timestamp '1993-07-01' + * and o_orderdate < timestamp '1993-07-01' + interval '3' month + * and exists ( + * select + * * + * from + * lineitem + * where + * l_orderkey = o_orderkey + * and l_commitdate < l_receiptdate + * ) + * group by + * o_orderpriority + * order by + * o_orderpriority; + */ + +struct q4_data { + std::unique_ptr lineitem; + std::unique_ptr orders; +}; + +q4_data load_ndsh_q4(std::unordered_map& sources) +{ + std::vector const lineitem_cols = {"l_orderkey", "l_commitdate", "l_receiptdate"}; + + std::vector const orders_cols = {"o_orderkey", "o_orderdate", "o_orderpriority"}; + auto const orderdate_ref = cudf::ast::column_reference(1); + auto orderdate_lower = + cudf::timestamp_scalar(days_since_epoch(1993, 7, 1), true); + auto orderdate_upper = + cudf::timestamp_scalar(days_since_epoch(1993, 10, 1), true); + auto const orderdate_lower_literal = cudf::ast::literal(orderdate_lower); + auto const orderdate_upper_literal = cudf::ast::literal(orderdate_upper); + auto const orderdate_lower_pred = cudf::ast::operation( + cudf::ast::ast_operator::GREATER_EQUAL, orderdate_ref, orderdate_lower_literal); + auto const orderdate_upper_pred = + cudf::ast::operation(cudf::ast::ast_operator::LESS, orderdate_ref, orderdate_upper_literal); + auto const orders_pred = std::make_unique( + cudf::ast::ast_operator::LOGICAL_AND, orderdate_lower_pred, orderdate_upper_pred); + + return q4_data{ + read_parquet(sources.at("lineitem").make_source_info(), lineitem_cols), + read_parquet(sources.at("orders").make_source_info(), orders_cols, std::move(orders_pred))}; +} + +std::unique_ptr execute_ndsh_q4(q4_data const& data) +{ + auto const commitdate_ref = cudf::ast::column_reference(data.lineitem->column_id("l_commitdate")); + auto const receiptdate_ref = + cudf::ast::column_reference(data.lineitem->column_id("l_receiptdate")); + auto const lineitem_pred = + cudf::ast::operation(cudf::ast::ast_operator::LESS, commitdate_ref, receiptdate_ref); + auto const lineitem = apply_filter(data.lineitem, lineitem_pred); + auto const qualifying_orders = + apply_left_semi_join(data.orders, lineitem, {"o_orderkey"}, {"l_orderkey"}); + auto const grouped_orders = apply_groupby( + qualifying_orders, + groupby_context_t{{"o_orderpriority"}, + {{"o_orderkey", {{cudf::aggregation::Kind::COUNT_ALL, "order_count"}}}}}); + return apply_orderby(grouped_orders, {"o_orderpriority"}, {cudf::order::ASCENDING}); +} + +void ndsh_q4(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"lineitem", "orders"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + q4_data data; + if (mode == query_mode::COMPUTE_ONLY) { data = load_ndsh_q4(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q4(sources); + result = execute_ndsh_q4(input); + } else { + result = execute_ndsh_q4(data); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q4(sources); + result = execute_ndsh_q4(input); + } else { + result = execute_ndsh_q4(data); + } + write_ndsh_result(*result, "q04"); +} + +NVBENCH_BENCH(ndsh_q4) + .set_name("ndsh_q4") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q05.cpp b/cpp/benchmarks/ndsh/q05.cpp index 482390a7bc18..048b3007dce1 100644 --- a/cpp/benchmarks/ndsh/q05.cpp +++ b/cpp/benchmarks/ndsh/q05.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -79,8 +79,8 @@ return revenue; } -void run_ndsh_q5(nvbench::state& state, - std::unordered_map& sources) +std::unordered_map> load_ndsh_q5( + std::unordered_map& sources) { // Define the column projection and filter predicate for the `orders` table std::vector const orders_cols = {"o_custkey", "o_orderkey", "o_orderdate"}; @@ -110,18 +110,37 @@ void run_ndsh_q5(nvbench::state& state, // Read out the tables from parquet files // while pushing down the column projections and filter predicates - auto const customer = - read_parquet(sources.at("customer").make_source_info(), {"c_custkey", "c_nationkey"}); - auto const orders = - read_parquet(sources.at("orders").make_source_info(), orders_cols, std::move(orders_pred)); - auto const lineitem = read_parquet(sources.at("lineitem").make_source_info(), - {"l_orderkey", "l_suppkey", "l_extendedprice", "l_discount"}); - auto const supplier = - read_parquet(sources.at("supplier").make_source_info(), {"s_suppkey", "s_nationkey"}); - auto const nation = - read_parquet(sources.at("nation").make_source_info(), {"n_nationkey", "n_regionkey", "n_name"}); - auto const region = - read_parquet(sources.at("region").make_source_info(), region_cols, std::move(region_pred)); + std::unordered_map> tables; + tables.emplace( + "customer", + read_parquet(sources.at("customer").make_source_info(), {"c_custkey", "c_nationkey"})); + tables.emplace( + "orders", + read_parquet(sources.at("orders").make_source_info(), orders_cols, std::move(orders_pred))); + tables.emplace("lineitem", + read_parquet(sources.at("lineitem").make_source_info(), + {"l_orderkey", "l_suppkey", "l_extendedprice", "l_discount"})); + tables.emplace( + "supplier", + read_parquet(sources.at("supplier").make_source_info(), {"s_suppkey", "s_nationkey"})); + tables.emplace("nation", + read_parquet(sources.at("nation").make_source_info(), + {"n_nationkey", "n_regionkey", "n_name"})); + tables.emplace( + "region", + read_parquet(sources.at("region").make_source_info(), region_cols, std::move(region_pred))); + return tables; +} + +std::unique_ptr execute_ndsh_q5( + std::unordered_map> const& tables) +{ + auto const& customer = tables.at("customer"); + auto const& orders = tables.at("orders"); + auto const& lineitem = tables.at("lineitem"); + auto const& supplier = tables.at("supplier"); + auto const& nation = tables.at("nation"); + auto const& region = tables.at("region"); // Perform the joins auto const join_a = apply_inner_join(region, nation, {"r_regionkey"}, {"n_regionkey"}); @@ -145,28 +164,46 @@ void run_ndsh_q5(nvbench::state& state, }}); // Perform the order by operation - auto const orderedby_table = - apply_orderby(groupedby_table, {"revenue"}, {cudf::order::DESCENDING}); - - // Write query result to a parquet file - orderedby_table->to_parquet("q5.parquet"); + return apply_orderby(groupedby_table, {"revenue"}, {cudf::order::DESCENDING}); } void ndsh_q5(nvbench::state& state) { // Generate the required parquet files in device buffers double const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); std::unordered_map sources; generate_parquet_data_sources( scale_factor, {"customer", "orders", "lineitem", "supplier", "nation", "region"}, sources); auto stream = cudf::get_default_stream(); state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q5(sources); } auto const mem_stats_logger = cudf::memory_stats_logger(); - state.exec(nvbench::exec_tag::sync, - [&](nvbench::launch& launch) { run_ndsh_q5(state, sources); }); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q5(sources); + result = execute_ndsh_q5(input); + } else { + result = execute_ndsh_q5(tables); + } + }); state.add_buffer_size( mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q5(sources); + result = execute_ndsh_q5(input); + } else { + result = execute_ndsh_q5(tables); + } + write_ndsh_result(*result, "q05"); } -NVBENCH_BENCH(ndsh_q5).set_name("ndsh_q5").add_float64_axis("scale_factor", {0.01, 0.1, 1}); +NVBENCH_BENCH(ndsh_q5) + .set_name("ndsh_q5") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q06.cpp b/cpp/benchmarks/ndsh/q06.cpp index 35a7a3727faf..791389e62599 100644 --- a/cpp/benchmarks/ndsh/q06.cpp +++ b/cpp/benchmarks/ndsh/q06.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -11,7 +11,6 @@ #include #include #include -#include #include #include @@ -54,8 +53,8 @@ return revenue; } -void run_ndsh_q6(nvbench::state& state, - std::unordered_map& sources) +std::unique_ptr load_ndsh_q6( + std::unordered_map& sources) { // Read out the `lineitem` table from parquet file std::vector const lineitem_cols = { @@ -74,26 +73,24 @@ void run_ndsh_q6(nvbench::state& state, cudf::ast::operation(cudf::ast::ast_operator::LESS, shipdate_ref, shipdate_upper_literal); auto const lineitem_pred = std::make_unique( cudf::ast::ast_operator::LOGICAL_AND, shipdate_pred_a, shipdate_pred_b); - auto lineitem = read_parquet( + return read_parquet( sources.at("lineitem").make_source_info(), lineitem_cols, std::move(lineitem_pred)); +} - // Cast the discount and quantity columns to float32 and append to lineitem table - auto discout_float = - cudf::cast(lineitem->column("l_discount"), cudf::data_type{cudf::type_id::FLOAT32}); - auto quantity_float = - cudf::cast(lineitem->column("l_quantity"), cudf::data_type{cudf::type_id::FLOAT32}); - - (*lineitem).append(discout_float, "l_discount_float").append(quantity_float, "l_quantity_float"); +std::unique_ptr execute_ndsh_q6(std::unique_ptr const& input) +{ + auto lineitem = std::make_unique(std::make_unique(input->table()), + input->column_names()); // Apply the filters - auto const discount_ref = cudf::ast::column_reference(lineitem->column_id("l_discount_float")); - auto const quantity_ref = cudf::ast::column_reference(lineitem->column_id("l_quantity_float")); + auto const discount_ref = cudf::ast::column_reference(lineitem->column_id("l_discount")); + auto const quantity_ref = cudf::ast::column_reference(lineitem->column_id("l_quantity")); - auto discount_lower = cudf::numeric_scalar(0.05); + auto discount_lower = cudf::numeric_scalar(0.05); auto const discount_lower_literal = cudf::ast::literal(discount_lower); - auto discount_upper = cudf::numeric_scalar(0.07); + auto discount_upper = cudf::numeric_scalar(0.07); auto const discount_upper_literal = cudf::ast::literal(discount_upper); - auto quantity_upper = cudf::numeric_scalar(24); + auto quantity_upper = cudf::numeric_scalar(24); auto const quantity_upper_literal = cudf::ast::literal(quantity_upper); auto const discount_pred_a = cudf::ast::operation( @@ -115,26 +112,44 @@ void run_ndsh_q6(nvbench::state& state, // Sum the `revenue` column auto const revenue_view = revenue->view(); - auto const result_table = apply_reduction(revenue_view, cudf::aggregation::Kind::SUM, "revenue"); - - // Write query result to a parquet file - result_table->to_parquet("q6.parquet"); + return apply_reduction(revenue_view, cudf::aggregation::Kind::SUM, "revenue"); } void ndsh_q6(nvbench::state& state) { // Generate the required parquet files in device buffers double const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); std::unordered_map sources; generate_parquet_data_sources(scale_factor, {"lineitem"}, sources); auto stream = cudf::get_default_stream(); state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + auto lineitem = mode == query_mode::COMPUTE_ONLY ? load_ndsh_q6(sources) : nullptr; auto const mem_stats_logger = cudf::memory_stats_logger(); - state.exec(nvbench::exec_tag::sync, - [&](nvbench::launch& launch) { run_ndsh_q6(state, sources); }); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q6(sources); + result = execute_ndsh_q6(input); + } else { + result = execute_ndsh_q6(lineitem); + } + }); state.add_buffer_size( mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q6(sources); + result = execute_ndsh_q6(input); + } else { + result = execute_ndsh_q6(lineitem); + } + write_ndsh_result(*result, "q06"); } -NVBENCH_BENCH(ndsh_q6).set_name("ndsh_q6").add_float64_axis("scale_factor", {0.01, 0.1, 1}); +NVBENCH_BENCH(ndsh_q6) + .set_name("ndsh_q6") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q07.cpp b/cpp/benchmarks/ndsh/q07.cpp new file mode 100644 index 000000000000..e56b07c267ef --- /dev/null +++ b/cpp/benchmarks/ndsh/q07.cpp @@ -0,0 +1,227 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/** + * @file q07.cpp + * @brief Implement query 7 of the NDS-H benchmark. + * + * select + * supp_nation, + * cust_nation, + * l_year, + * sum(volume) as revenue + * from + * ( + * select + * n1.n_name as supp_nation, + * n2.n_name as cust_nation, + * year(l_shipdate) as l_year, + * l_extendedprice * (1 - l_discount) as volume + * from + * supplier, + * lineitem, + * orders, + * customer, + * nation n1, + * nation n2 + * where + * s_suppkey = l_suppkey + * and o_orderkey = l_orderkey + * and c_custkey = o_custkey + * and s_nationkey = n1.n_nationkey + * and c_nationkey = n2.n_nationkey + * and ( + * (n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY') + * or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE') + * ) + * and l_shipdate between timestamp '1995-01-01' and timestamp '1996-12-31' + * ) as shipping + * group by + * supp_nation, + * cust_nation, + * l_year + * order by + * supp_nation, + * cust_nation, + * l_year; + */ + +[[nodiscard]] std::unique_ptr calculate_revenue( + cudf::column_view const& extendedprice, + cudf::column_view const& discount, + rmm::cuda_stream_view stream = cudf::get_default_stream(), + rmm::device_async_resource_ref mr = cudf::get_current_device_resource_ref()) +{ + auto const one = cudf::numeric_scalar(1); + auto const one_minus_discount = + cudf::binary_operation(one, discount, cudf::binary_operator::SUB, discount.type(), stream, mr); + return cudf::binary_operation(extendedprice, + one_minus_discount->view(), + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}, + stream, + mr); +} + +std::unordered_map> load_ndsh_q7( + std::unordered_map& sources) +{ + std::vector const lineitem_cols = { + "l_orderkey", "l_suppkey", "l_extendedprice", "l_discount", "l_shipdate"}; + auto const l_shipdate_ref = cudf::ast::column_reference(4); + auto l_shipdate_lower = + cudf::timestamp_scalar(days_since_epoch(1995, 1, 1), true); + auto const l_shipdate_lower_limit = cudf::ast::literal(l_shipdate_lower); + auto const l_shipdate_pred_lower = cudf::ast::operation( + cudf::ast::ast_operator::GREATER_EQUAL, l_shipdate_ref, l_shipdate_lower_limit); + auto l_shipdate_upper = + cudf::timestamp_scalar(days_since_epoch(1996, 12, 31), true); + auto const l_shipdate_upper_limit = cudf::ast::literal(l_shipdate_upper); + auto const l_shipdate_pred_upper = cudf::ast::operation( + cudf::ast::ast_operator::LESS_EQUAL, l_shipdate_ref, l_shipdate_upper_limit); + auto const lineitem_pred = std::make_unique( + cudf::ast::ast_operator::LOGICAL_AND, l_shipdate_pred_lower, l_shipdate_pred_upper); + + std::unordered_map> tables; + tables.emplace( + "customer", + read_parquet(sources.at("customer").make_source_info(), {"c_custkey", "c_nationkey"})); + tables.emplace( + "lineitem", + read_parquet( + sources.at("lineitem").make_source_info(), lineitem_cols, std::move(lineitem_pred))); + tables.emplace("nation", + read_parquet(sources.at("nation").make_source_info(), {"n_nationkey", "n_name"})); + tables.emplace( + "orders", read_parquet(sources.at("orders").make_source_info(), {"o_custkey", "o_orderkey"})); + tables.emplace( + "supplier", + read_parquet(sources.at("supplier").make_source_info(), {"s_suppkey", "s_nationkey"})); + return tables; +} + +std::unique_ptr filter_nation(std::unique_ptr const& nation, + std::string const& name, + std::string const& name_column) +{ + auto name_value = cudf::string_scalar(name); + auto const name_ref = cudf::ast::column_reference(nation->column_id("n_name")); + auto const name_literal = cudf::ast::literal(name_value); + auto const predicate = + cudf::ast::operation(cudf::ast::ast_operator::EQUAL, name_ref, name_literal); + auto const boolean_mask = cudf::compute_column(nation->table(), predicate); + auto filtered = cudf::apply_boolean_mask(nation->table(), boolean_mask->view()); + return std::make_unique(std::move(filtered), + std::vector{"n_nationkey", name_column}); +} + +std::unique_ptr make_shipping( + std::unordered_map> const& tables, + std::string const& customer_nation_name, + std::string const& supplier_nation_name) +{ + auto const& customer = tables.at("customer"); + auto const& lineitem = tables.at("lineitem"); + auto const& nation = tables.at("nation"); + auto const& orders = tables.at("orders"); + auto const& supplier = tables.at("supplier"); + + auto const customer_nation = filter_nation(nation, customer_nation_name, "cust_nation"); + auto const supplier_nation = filter_nation(nation, supplier_nation_name, "supp_nation"); + auto const customers = + apply_inner_join(customer, customer_nation, {"c_nationkey"}, {"n_nationkey"}); + auto const suppliers = + apply_inner_join(supplier, supplier_nation, {"s_nationkey"}, {"n_nationkey"}); + auto const items = apply_inner_join(orders, lineitem, {"o_orderkey"}, {"l_orderkey"}); + auto const customer_items = apply_inner_join(customers, items, {"c_custkey"}, {"o_custkey"}); + auto const joined = apply_inner_join(customer_items, suppliers, {"l_suppkey"}, {"s_suppkey"}); + + std::vector> columns; + columns.push_back(std::make_unique(joined->column("supp_nation"))); + columns.push_back(std::make_unique(joined->column("cust_nation"))); + columns.push_back(cudf::datetime::extract_datetime_component( + joined->column("l_shipdate"), cudf::datetime::datetime_component::YEAR)); + columns.push_back( + calculate_revenue(joined->column("l_extendedprice"), joined->column("l_discount"))); + return std::make_unique( + std::make_unique(std::move(columns)), + std::vector{"supp_nation", "cust_nation", "l_year", "volume"}); +} + +std::unique_ptr execute_ndsh_q7( + std::unordered_map> const& tables) +{ + auto const france_to_germany = make_shipping(tables, "GERMANY", "FRANCE"); + auto const germany_to_france = make_shipping(tables, "FRANCE", "GERMANY"); + std::array const shipping_views = {france_to_germany->table(), + germany_to_france->table()}; + auto shipping = std::make_unique(cudf::concatenate(shipping_views), + france_to_germany->column_names()); + auto const grouped = + apply_groupby(shipping, + groupby_context_t{{"supp_nation", "cust_nation", "l_year"}, + {{"volume", {{cudf::aggregation::Kind::SUM, "revenue"}}}}}); + return apply_orderby(grouped, + {"supp_nation", "cust_nation", "l_year"}, + {cudf::order::ASCENDING, cudf::order::ASCENDING, cudf::order::ASCENDING}); +} + +void ndsh_q7(nvbench::state& state) +{ + double const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources( + scale_factor, {"customer", "lineitem", "nation", "orders", "supplier"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q7(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q7(sources); + result = execute_ndsh_q7(input); + } else { + result = execute_ndsh_q7(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q7(sources); + result = execute_ndsh_q7(input); + } else { + result = execute_ndsh_q7(tables); + } + write_ndsh_result(*result, "q07"); +} + +NVBENCH_BENCH(ndsh_q7) + .set_name("ndsh_q7") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q08.cpp b/cpp/benchmarks/ndsh/q08.cpp new file mode 100644 index 000000000000..3a79d6285f1e --- /dev/null +++ b/cpp/benchmarks/ndsh/q08.cpp @@ -0,0 +1,225 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * @file q08.cpp + * @brief Implement query 8 of the NDS-H benchmark. + * + * select + * o_year, + * round( + * sum(case + * when nation = 'BRAZIL' then volume + * else 0 + * end) / sum(volume) + * , 2) as mkt_share + * from + * ( + * select + * extract(year from o_orderdate) as o_year, + * l_extendedprice * (1 - l_discount) as volume, + * n2.n_name as nation + * from + * part, + * supplier, + * lineitem, + * orders, + * customer, + * nation n1, + * nation n2, + * region + * where + * p_partkey = l_partkey + * and s_suppkey = l_suppkey + * and l_orderkey = o_orderkey + * and o_custkey = c_custkey + * and c_nationkey = n1.n_nationkey + * and n1.n_regionkey = r_regionkey + * and r_name = 'AMERICA' + * and s_nationkey = n2.n_nationkey + * and o_orderdate between timestamp '1995-01-01' and timestamp '1996-12-31' + * and p_type = 'ECONOMY ANODIZED STEEL' + * ) as all_nations + * group by + * o_year + * order by + * o_year; + */ + +std::unordered_map> load_ndsh_q8( + std::unordered_map& sources) +{ + std::vector const orders_cols = {"o_orderkey", "o_custkey", "o_orderdate"}; + auto const orderdate_ref = cudf::ast::column_reference(2); + auto orderdate_lower = + cudf::timestamp_scalar(days_since_epoch(1995, 1, 1), true); + auto const orderdate_lower_literal = cudf::ast::literal(orderdate_lower); + auto orderdate_upper = + cudf::timestamp_scalar(days_since_epoch(1996, 12, 31), true); + auto const orderdate_upper_literal = cudf::ast::literal(orderdate_upper); + auto const orderdate_pred_lower = cudf::ast::operation( + cudf::ast::ast_operator::GREATER_EQUAL, orderdate_ref, orderdate_lower_literal); + auto const orderdate_pred_upper = cudf::ast::operation( + cudf::ast::ast_operator::LESS_EQUAL, orderdate_ref, orderdate_upper_literal); + auto const orders_pred = std::make_unique( + cudf::ast::ast_operator::LOGICAL_AND, orderdate_pred_lower, orderdate_pred_upper); + + std::vector const part_cols = {"p_partkey", "p_type"}; + auto const part_type_ref = cudf::ast::column_reference(1); + auto part_type = cudf::string_scalar("ECONOMY ANODIZED STEEL"); + auto const part_type_literal = cudf::ast::literal(part_type); + auto const part_pred = std::make_unique( + cudf::ast::ast_operator::EQUAL, part_type_ref, part_type_literal); + + std::vector const region_cols = {"r_regionkey", "r_name"}; + auto const region_name_ref = cudf::ast::column_reference(1); + auto region_name = cudf::string_scalar("AMERICA"); + auto const region_name_literal = cudf::ast::literal(region_name); + auto const region_pred = std::make_unique( + cudf::ast::ast_operator::EQUAL, region_name_ref, region_name_literal); + + std::unordered_map> tables; + tables.emplace( + "customer", + read_parquet(sources.at("customer").make_source_info(), {"c_custkey", "c_nationkey"})); + tables.emplace( + "lineitem", + read_parquet(sources.at("lineitem").make_source_info(), + {"l_partkey", "l_suppkey", "l_orderkey", "l_extendedprice", "l_discount"})); + tables.emplace("nation", + read_parquet(sources.at("nation").make_source_info(), + {"n_nationkey", "n_regionkey", "n_name"})); + tables.emplace( + "orders", + read_parquet(sources.at("orders").make_source_info(), orders_cols, std::move(orders_pred))); + tables.emplace( + "part", read_parquet(sources.at("part").make_source_info(), part_cols, std::move(part_pred))); + tables.emplace( + "region", + read_parquet(sources.at("region").make_source_info(), region_cols, std::move(region_pred))); + tables.emplace( + "supplier", + read_parquet(sources.at("supplier").make_source_info(), {"s_suppkey", "s_nationkey"})); + return tables; +} + +std::unique_ptr execute_ndsh_q8( + std::unordered_map> const& tables) +{ + auto const customer_nation = + apply_projection(tables.at("nation"), {"n_nationkey", "n_regionkey"}); + auto const supplier_nation = apply_projection(tables.at("nation"), {"n_nationkey", "n_name"}); + auto const america_nations = + apply_inner_join(customer_nation, tables.at("region"), {"n_regionkey"}, {"r_regionkey"}); + + auto const join_a = + apply_inner_join(tables.at("part"), tables.at("lineitem"), {"p_partkey"}, {"l_partkey"}); + auto const join_b = apply_inner_join(join_a, tables.at("supplier"), {"l_suppkey"}, {"s_suppkey"}); + auto const join_c = apply_inner_join(join_b, tables.at("orders"), {"l_orderkey"}, {"o_orderkey"}); + auto const join_d = apply_inner_join(join_c, tables.at("customer"), {"o_custkey"}, {"c_custkey"}); + auto const join_e = apply_inner_join(join_d, america_nations, {"c_nationkey"}, {"n_nationkey"}); + auto joined = apply_inner_join(join_e, supplier_nation, {"s_nationkey"}, {"n_nationkey"}); + + auto o_year = cudf::datetime::extract_datetime_component( + joined->column("o_orderdate"), cudf::datetime::datetime_component::YEAR); + auto const one = cudf::numeric_scalar(1); + auto const one_minus_discount = cudf::binary_operation(one, + joined->column("l_discount"), + cudf::binary_operator::SUB, + cudf::data_type{cudf::type_id::FLOAT64}); + auto volume = cudf::binary_operation(joined->column("l_extendedprice"), + one_minus_discount->view(), + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}); + + auto const nation_ref = cudf::ast::column_reference(joined->column_id("n_name")); + auto brazil = cudf::string_scalar("BRAZIL"); + auto const brazil_literal = cudf::ast::literal(brazil); + auto const brazil_pred = + cudf::ast::operation(cudf::ast::ast_operator::EQUAL, nation_ref, brazil_literal); + auto const brazil_mask = cudf::compute_column(joined->table(), brazil_pred); + auto const zero = cudf::numeric_scalar(0); + auto brazil_volume = cudf::copy_if_else(volume->view(), zero, brazil_mask->view()); + + joined->append(o_year, "o_year").append(volume, "volume").append(brazil_volume, "brazil_volume"); + auto const grouped = apply_groupby( + joined, + groupby_context_t{{"o_year"}, + {{"volume", {{cudf::aggregation::Kind::SUM, "volume"}}}, + {"brazil_volume", {{cudf::aggregation::Kind::SUM, "brazil_volume"}}}}}); + auto market_share = cudf::binary_operation(grouped->column("brazil_volume"), + grouped->column("volume"), + cudf::binary_operator::DIV, + cudf::data_type{cudf::type_id::FLOAT64}); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + auto rounded_market_share = cudf::round(market_share->view(), 2, cudf::rounding_method::HALF_UP); +#pragma GCC diagnostic pop + + std::vector> result_columns; + result_columns.push_back(std::make_unique(grouped->column("o_year"))); + result_columns.push_back(std::move(rounded_market_share)); + auto result = + std::make_unique(std::make_unique(std::move(result_columns)), + std::vector{"o_year", "mkt_share"}); + return apply_orderby(result, {"o_year"}, {cudf::order::ASCENDING}); +} + +void ndsh_q8(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources( + scale_factor, + {"customer", "lineitem", "nation", "orders", "part", "region", "supplier"}, + sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q8(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q8(sources); + result = execute_ndsh_q8(input); + } else { + result = execute_ndsh_q8(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q8(sources); + result = execute_ndsh_q8(input); + } else { + result = execute_ndsh_q8(tables); + } + write_ndsh_result(*result, "q08"); +} + +NVBENCH_BENCH(ndsh_q8) + .set_name("ndsh_q8") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q09.cpp b/cpp/benchmarks/ndsh/q09.cpp index 1f90a307fdb9..004ef5747370 100644 --- a/cpp/benchmarks/ndsh/q09.cpp +++ b/cpp/benchmarks/ndsh/q09.cpp @@ -324,10 +324,13 @@ void ndsh_q9(nvbench::state& state) data, launch.get_stream().get_stream(), cudf::get_current_device_resource_ref()); - result->to_parquet("q9.parquet"); }); state.add_buffer_size( mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + q9_data const data = load_data(sources); + auto const result = compute_profit(state, engine, data); + write_ndsh_result(*result, "q09"); } void ndsh_q9_noio(nvbench::state& state) @@ -353,8 +356,6 @@ void ndsh_q9_noio(nvbench::state& state) }); state.add_buffer_size( mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); - - if (result) { result->to_parquet("q9_noio.parquet"); } } // unlike `ndsh_q9`, `ndsh_q9_amount` benchmarks only the amount calculation part of the benchmark diff --git a/cpp/benchmarks/ndsh/q10.cpp b/cpp/benchmarks/ndsh/q10.cpp index 567b6a0df884..9d014439c61b 100644 --- a/cpp/benchmarks/ndsh/q10.cpp +++ b/cpp/benchmarks/ndsh/q10.cpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -54,7 +54,8 @@ * c_address, * c_comment * order by - * revenue desc; + * revenue desc + * limit 20; */ /** @@ -84,8 +85,8 @@ return revenue; } -void run_ndsh_q10(nvbench::state& state, - std::unordered_map& sources) +std::unordered_map> load_ndsh_q10( + std::unordered_map& sources) { // Define the column projection and filter predicate for the `orders` table std::vector const orders_cols = {"o_custkey", "o_orderkey", "o_orderdate"}; @@ -112,17 +113,31 @@ void run_ndsh_q10(nvbench::state& state, // Read out the tables from parquet files // while pushing down the column projections and filter predicates - auto const customer = read_parquet( - sources.at("customer").make_source_info(), - {"c_custkey", "c_name", "c_nationkey", "c_acctbal", "c_address", "c_phone", "c_comment"}); - auto const orders = - read_parquet(sources.at("orders").make_source_info(), orders_cols, std::move(orders_pred)); - auto const lineitem = - read_parquet(sources.at("lineitem").make_source_info(), - {"l_extendedprice", "l_discount", "l_orderkey", "l_returnflag"}, - std::move(lineitem_pred)); - auto const nation = - read_parquet(sources.at("nation").make_source_info(), {"n_name", "n_nationkey"}); + std::unordered_map> tables; + tables.emplace( + "customer", + read_parquet( + sources.at("customer").make_source_info(), + {"c_custkey", "c_name", "c_nationkey", "c_acctbal", "c_address", "c_phone", "c_comment"})); + tables.emplace( + "orders", + read_parquet(sources.at("orders").make_source_info(), orders_cols, std::move(orders_pred))); + tables.emplace("lineitem", + read_parquet(sources.at("lineitem").make_source_info(), + {"l_extendedprice", "l_discount", "l_orderkey", "l_returnflag"}, + std::move(lineitem_pred))); + tables.emplace("nation", + read_parquet(sources.at("nation").make_source_info(), {"n_name", "n_nationkey"})); + return tables; +} + +std::unique_ptr execute_ndsh_q10( + std::unordered_map> const& tables) +{ + auto const& customer = tables.at("customer"); + auto const& orders = tables.at("orders"); + auto const& lineitem = tables.at("lineitem"); + auto const& nation = tables.at("nation"); // Perform the joins auto const join_a = apply_inner_join(customer, nation, {"c_nationkey"}, {"n_nationkey"}); @@ -142,30 +157,52 @@ void run_ndsh_q10(nvbench::state& state, { {"revenue", {{cudf::aggregation::Kind::SUM, "revenue"}}}, }}); + auto const projected = apply_projection( + groupedby_table, + {"c_custkey", "c_name", "revenue", "c_acctbal", "n_name", "c_address", "c_phone", "c_comment"}); // Perform the order by operation - auto const orderedby_table = - apply_orderby(groupedby_table, {"revenue"}, {cudf::order::DESCENDING}); - - // Write query result to a parquet file - orderedby_table->to_parquet("q10.parquet"); + auto const ordered = apply_orderby(projected, {"revenue"}, {cudf::order::DESCENDING}); + return apply_slice(ordered, 0, 20); } void ndsh_q10(nvbench::state& state) { // Generate the required parquet files in device buffers double const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); std::unordered_map sources; generate_parquet_data_sources( scale_factor, {"customer", "orders", "lineitem", "nation"}, sources); auto stream = cudf::get_default_stream(); state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q10(sources); } auto const mem_stats_logger = cudf::memory_stats_logger(); - state.exec(nvbench::exec_tag::sync, - [&](nvbench::launch& launch) { run_ndsh_q10(state, sources); }); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q10(sources); + result = execute_ndsh_q10(input); + } else { + result = execute_ndsh_q10(tables); + } + }); state.add_buffer_size( mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q10(sources); + result = execute_ndsh_q10(input); + } else { + result = execute_ndsh_q10(tables); + } + write_ndsh_result(*result, "q10"); } -NVBENCH_BENCH(ndsh_q10).set_name("ndsh_q10").add_float64_axis("scale_factor", {0.01, 0.1, 1}); +NVBENCH_BENCH(ndsh_q10) + .set_name("ndsh_q10") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q11.cpp b/cpp/benchmarks/ndsh/q11.cpp new file mode 100644 index 000000000000..5a4bcc809be0 --- /dev/null +++ b/cpp/benchmarks/ndsh/q11.cpp @@ -0,0 +1,158 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * @file q11.cpp + * @brief Implement query 11 of the NDS-H benchmark. + * + * select + * ps_partkey, + * round(sum(ps_supplycost * ps_availqty), 2) as value + * from + * partsupp, supplier, nation + * where + * ps_suppkey = s_suppkey + * and s_nationkey = n_nationkey + * and n_name = 'GERMANY' + * group by + * ps_partkey + * having + * sum(ps_supplycost * ps_availqty) > ( + * select + * sum(ps_supplycost * ps_availqty) * (0.0001 / scale_factor) + * from + * partsupp, supplier, nation + * where + * ps_suppkey = s_suppkey + * and s_nationkey = n_nationkey + * and n_name = 'GERMANY' + * ) + * order by + * value desc; + */ + +std::unordered_map> load_ndsh_q11( + std::unordered_map& sources) +{ + std::vector const nation_columns = {"n_nationkey", "n_name"}; + auto const nation_name = cudf::ast::column_reference{1}; + auto germany = cudf::string_scalar{"GERMANY"}; + auto const germany_literal = cudf::ast::literal{germany}; + auto nation_filter = std::make_unique( + cudf::ast::ast_operator::EQUAL, nation_name, germany_literal); + + std::unordered_map> tables; + tables.emplace( + "nation", + read_parquet( + sources.at("nation").make_source_info(), nation_columns, std::move(nation_filter))); + tables.emplace("partsupp", + read_parquet(sources.at("partsupp").make_source_info(), + {"ps_suppkey", "ps_supplycost", "ps_availqty", "ps_partkey"})); + tables.emplace( + "supplier", + read_parquet(sources.at("supplier").make_source_info(), {"s_suppkey", "s_nationkey"})); + return tables; +} + +std::unique_ptr execute_ndsh_q11( + std::unordered_map> const& tables, + double scale_factor) +{ + auto const partsupp_supplier = + apply_inner_join(tables.at("partsupp"), tables.at("supplier"), {"ps_suppkey"}, {"s_suppkey"}); + auto joined = + apply_inner_join(partsupp_supplier, tables.at("nation"), {"s_nationkey"}, {"n_nationkey"}); + + auto value = cudf::binary_operation(joined->column("ps_supplycost"), + joined->column("ps_availqty"), + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}); + joined->append(value, "value"); + + auto const sum_aggregation = cudf::make_sum_aggregation(); + auto total = cudf::reduce( + joined->column("value"), *sum_aggregation, cudf::data_type{cudf::type_id::FLOAT64}); + auto total_column = cudf::make_column_from_scalar(*total, 1); + auto const threshold_factor = cudf::numeric_scalar{0.0001 / scale_factor}; + auto threshold_column = cudf::binary_operation(total_column->view(), + threshold_factor, + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}); + auto threshold = cudf::get_element(threshold_column->view(), 0); + + auto grouped = apply_groupby( + joined, + groupby_context_t{{"ps_partkey"}, {{"value", {{cudf::aggregation::Kind::SUM, "value"}}}}}); + auto mask = cudf::binary_operation(grouped->column("value"), + *threshold, + cudf::binary_operator::GREATER, + cudf::data_type{cudf::type_id::BOOL8}); + auto filtered = apply_mask(grouped, mask); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + auto rounded = cudf::round(filtered->column("value"), 2, cudf::rounding_method::HALF_UP); +#pragma GCC diagnostic pop + std::vector> columns; + columns.push_back(std::make_unique(filtered->column("ps_partkey"))); + columns.push_back(std::move(rounded)); + auto result = + std::make_unique(std::make_unique(std::move(columns)), + std::vector{"ps_partkey", "value"}); + return apply_orderby(result, {"value"}, {cudf::order::DESCENDING}); +} + +void ndsh_q11(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"nation", "partsupp", "supplier"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q11(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q11(sources); + result = execute_ndsh_q11(input, scale_factor); + } else { + result = execute_ndsh_q11(tables, scale_factor); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q11(sources); + result = execute_ndsh_q11(input, scale_factor); + } else { + result = execute_ndsh_q11(tables, scale_factor); + } + write_ndsh_result(*result, "q11"); +} + +NVBENCH_BENCH(ndsh_q11) + .set_name("ndsh_q11") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q12.cpp b/cpp/benchmarks/ndsh/q12.cpp new file mode 100644 index 000000000000..42653b7e1aba --- /dev/null +++ b/cpp/benchmarks/ndsh/q12.cpp @@ -0,0 +1,191 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * @file q12.cpp + * @brief Implement query 12 of the NDS-H benchmark. + * + * select + * l_shipmode, + * sum(case + * when o_orderpriority = '1-URGENT' + * or o_orderpriority = '2-HIGH' + * then 1 + * else 0 + * end) as high_line_count, + * sum(case + * when o_orderpriority <> '1-URGENT' + * and o_orderpriority <> '2-HIGH' + * then 1 + * else 0 + * end) as low_line_count + * from + * orders, + * lineitem + * where + * o_orderkey = l_orderkey + * and l_shipmode in ('MAIL', 'SHIP') + * and l_commitdate < l_receiptdate + * and l_shipdate < l_commitdate + * and l_receiptdate >= date '1994-01-01' + * and l_receiptdate < date '1994-01-01' + interval '1' year + * group by + * l_shipmode + * order by + * l_shipmode; + */ + +std::unordered_map> load_ndsh_q12( + std::unordered_map& sources) +{ + std::unordered_map> tables; + tables.emplace( + "lineitem", + read_parquet(sources.at("lineitem").make_source_info(), + {"l_orderkey", "l_shipmode", "l_commitdate", "l_receiptdate", "l_shipdate"})); + tables.emplace( + "orders", + read_parquet(sources.at("orders").make_source_info(), {"o_orderkey", "o_orderpriority"})); + return tables; +} + +std::unique_ptr execute_ndsh_q12( + std::unordered_map> const& tables) +{ + auto const& lineitem = tables.at("lineitem"); + auto const& orders = tables.at("orders"); + + auto const shipmode_ref = cudf::ast::column_reference(lineitem->column_id("l_shipmode")); + auto const commit_ref = cudf::ast::column_reference(lineitem->column_id("l_commitdate")); + auto const receipt_ref = cudf::ast::column_reference(lineitem->column_id("l_receiptdate")); + auto const ship_ref = cudf::ast::column_reference(lineitem->column_id("l_shipdate")); + + auto mail = cudf::string_scalar("MAIL"); + auto ship = cudf::string_scalar("SHIP"); + auto const mail_literal = cudf::ast::literal(mail); + auto const ship_literal = cudf::ast::literal(ship); + auto const mail_predicate = + cudf::ast::operation(cudf::ast::ast_operator::EQUAL, shipmode_ref, mail_literal); + auto const ship_predicate = + cudf::ast::operation(cudf::ast::ast_operator::EQUAL, shipmode_ref, ship_literal); + auto const shipmode_predicate = + cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_OR, mail_predicate, ship_predicate); + + auto const commit_before_receipt = + cudf::ast::operation(cudf::ast::ast_operator::LESS, commit_ref, receipt_ref); + auto const ship_before_commit = + cudf::ast::operation(cudf::ast::ast_operator::LESS, ship_ref, commit_ref); + + auto receipt_lower = + cudf::timestamp_scalar(days_since_epoch(1994, 1, 1), true); + auto receipt_upper = + cudf::timestamp_scalar(days_since_epoch(1995, 1, 1), true); + auto const receipt_lower_literal = cudf::ast::literal(receipt_lower); + auto const receipt_upper_literal = cudf::ast::literal(receipt_upper); + auto const receipt_after_lower = cudf::ast::operation( + cudf::ast::ast_operator::GREATER_EQUAL, receipt_ref, receipt_lower_literal); + auto const receipt_before_upper = + cudf::ast::operation(cudf::ast::ast_operator::LESS, receipt_ref, receipt_upper_literal); + + auto const date_order_predicate = cudf::ast::operation( + cudf::ast::ast_operator::LOGICAL_AND, commit_before_receipt, ship_before_commit); + auto const receipt_predicate = cudf::ast::operation( + cudf::ast::ast_operator::LOGICAL_AND, receipt_after_lower, receipt_before_upper); + auto const date_predicate = cudf::ast::operation( + cudf::ast::ast_operator::LOGICAL_AND, date_order_predicate, receipt_predicate); + auto const lineitem_predicate = + cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_AND, shipmode_predicate, date_predicate); + auto const filtered_lineitem = apply_filter(lineitem, lineitem_predicate); + + auto joined = apply_inner_join(orders, filtered_lineitem, {"o_orderkey"}, {"l_orderkey"}); + auto const priority_ref = cudf::ast::column_reference(joined->column_id("o_orderpriority")); + auto urgent = cudf::string_scalar("1-URGENT"); + auto high = cudf::string_scalar("2-HIGH"); + auto const urgent_literal = cudf::ast::literal(urgent); + auto const high_literal = cudf::ast::literal(high); + auto const urgent_predicate = + cudf::ast::operation(cudf::ast::ast_operator::EQUAL, priority_ref, urgent_literal); + auto const high_predicate = + cudf::ast::operation(cudf::ast::ast_operator::EQUAL, priority_ref, high_literal); + auto const high_priority_predicate = + cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_OR, urgent_predicate, high_predicate); + auto const high_priority = cudf::compute_column(joined->table(), high_priority_predicate); + + auto const false_scalar = cudf::numeric_scalar{false}; + auto normalized_priority = cudf::replace_nulls(high_priority->view(), false_scalar); + auto not_high_priority = + cudf::unary_operation(normalized_priority->view(), cudf::unary_operator::NOT); + auto valid_priority = cudf::is_valid(joined->column("o_orderpriority")); + auto low_priority = cudf::binary_operation(not_high_priority->view(), + valid_priority->view(), + cudf::binary_operator::LOGICAL_AND, + cudf::data_type{cudf::type_id::BOOL8}); + auto const one = cudf::numeric_scalar(1); + auto const zero = cudf::numeric_scalar(0); + auto high_line_count = cudf::copy_if_else(one, zero, normalized_priority->view()); + auto low_line_count = cudf::copy_if_else(one, zero, low_priority->view()); + joined->append(high_line_count, "high_line_count").append(low_line_count, "low_line_count"); + + auto grouped = apply_groupby( + joined, + groupby_context_t{{"l_shipmode"}, + {{"high_line_count", {{cudf::aggregation::Kind::SUM, "high_line_count"}}}, + {"low_line_count", {{cudf::aggregation::Kind::SUM, "low_line_count"}}}}}); + auto projected = apply_projection(grouped, {"l_shipmode", "high_line_count", "low_line_count"}); + return apply_orderby(projected, {"l_shipmode"}, {cudf::order::ASCENDING}); +} + +void ndsh_q12(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"lineitem", "orders"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q12(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q12(sources); + result = execute_ndsh_q12(input); + } else { + result = execute_ndsh_q12(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q12(sources); + result = execute_ndsh_q12(input); + } else { + result = execute_ndsh_q12(tables); + } + write_ndsh_result(*result, "q12"); +} + +NVBENCH_BENCH(ndsh_q12) + .set_name("ndsh_q12") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q13.cpp b/cpp/benchmarks/ndsh/q13.cpp new file mode 100644 index 000000000000..fc9f9d3b4389 --- /dev/null +++ b/cpp/benchmarks/ndsh/q13.cpp @@ -0,0 +1,122 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +/** + * @file q13.cpp + * @brief Implement query 13 of the NDS-H benchmark. + * + * select + * c_count, + * count(*) as custdist + * from ( + * select + * c_custkey, + * count(o_orderkey) as c_count + * from customer left outer join orders on + * c_custkey = o_custkey + * and o_comment not like '%special%requests%' + * group by c_custkey + * ) as c_orders + * group by c_count + * order by custdist desc, c_count desc; + */ + +std::unordered_map> load_ndsh_q13( + std::unordered_map& sources) +{ + std::unordered_map> tables; + tables.emplace("customer", + read_parquet(sources.at("customer").make_source_info(), {"c_custkey"})); + tables.emplace("orders", + read_parquet(sources.at("orders").make_source_info(), + {"o_custkey", "o_orderkey", "o_comment"})); + return tables; +} + +std::unique_ptr execute_ndsh_q13( + std::unordered_map> const& tables) +{ + auto const& customer = tables.at("customer"); + auto const& orders = tables.at("orders"); + + auto const program = cudf::strings::regex_program::create("special.*requests"); + auto matches = + cudf::strings::contains_re(cudf::strings_column_view{orders->column("o_comment")}, *program); + auto keep_orders = cudf::unary_operation(matches->view(), cudf::unary_operator::NOT); + auto const filtered_orders = apply_mask(orders, keep_orders); + + auto const joined = apply_left_join(customer, filtered_orders, {"c_custkey"}, {"o_custkey"}); + + cudf::groupby::groupby customer_groups(joined->select({"c_custkey"})); + std::vector requests(1); + requests[0].values = joined->column("o_orderkey"); + requests[0].aggregations.push_back( + cudf::make_count_aggregation(cudf::null_policy::EXCLUDE)); + auto [customer_keys, customer_counts] = customer_groups.aggregate(requests); + auto count_columns = customer_keys->release(); + count_columns.push_back(std::move(customer_counts[0].results[0])); + auto counts = + std::make_unique(std::make_unique(std::move(count_columns)), + std::vector{"c_custkey", "c_count"}); + + auto const distribution = apply_groupby( + counts, + groupby_context_t{{"c_count"}, + {{"c_custkey", {{cudf::aggregation::Kind::COUNT_ALL, "custdist"}}}}}); + return apply_orderby( + distribution, {"custdist", "c_count"}, {cudf::order::DESCENDING, cudf::order::DESCENDING}); +} + +void ndsh_q13(nvbench::state& state) +{ + double const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"customer", "orders"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q13(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q13(sources); + result = execute_ndsh_q13(input); + } else { + result = execute_ndsh_q13(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q13(sources); + result = execute_ndsh_q13(input); + } else { + result = execute_ndsh_q13(tables); + } + write_ndsh_result(*result, "q13"); +} + +NVBENCH_BENCH(ndsh_q13) + .set_name("ndsh_q13") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q14.cpp b/cpp/benchmarks/ndsh/q14.cpp new file mode 100644 index 000000000000..360b3f38bdf9 --- /dev/null +++ b/cpp/benchmarks/ndsh/q14.cpp @@ -0,0 +1,149 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * @file q14.cpp + * @brief Implement query 14 of the NDS-H benchmark. + * + * select + * round(100.00 * sum(case + * when p_type like 'PROMO%' + * then l_extendedprice * (1 - l_discount) + * else 0 + * end) / sum(l_extendedprice * (1 - l_discount)), 2) as promo_revenue + * from + * lineitem, + * part + * where + * l_partkey = p_partkey + * and l_shipdate >= date '1995-09-01' + * and l_shipdate < date '1995-09-01' + interval '1' month; + */ + +std::unordered_map> load_ndsh_q14( + std::unordered_map& sources) +{ + std::vector const lineitem_columns = { + "l_partkey", "l_shipdate", "l_extendedprice", "l_discount"}; + auto const shipdate_ref = cudf::ast::column_reference{1}; + auto lower = cudf::timestamp_scalar(days_since_epoch(1995, 9, 1), true); + auto upper = cudf::timestamp_scalar(days_since_epoch(1995, 10, 1), true); + auto const lower_literal = cudf::ast::literal{lower}; + auto const upper_literal = cudf::ast::literal{upper}; + auto const after_lower = + cudf::ast::operation{cudf::ast::ast_operator::GREATER_EQUAL, shipdate_ref, lower_literal}; + auto const before_upper = + cudf::ast::operation{cudf::ast::ast_operator::LESS, shipdate_ref, upper_literal}; + auto shipdate_filter = std::make_unique( + cudf::ast::ast_operator::LOGICAL_AND, after_lower, before_upper); + + std::unordered_map> tables; + tables.emplace( + "lineitem", + read_parquet( + sources.at("lineitem").make_source_info(), lineitem_columns, std::move(shipdate_filter))); + tables.emplace("part", + read_parquet(sources.at("part").make_source_info(), {"p_partkey", "p_type"})); + return tables; +} + +std::unique_ptr execute_ndsh_q14( + std::unordered_map> const& tables) +{ + auto const joined = + apply_inner_join(tables.at("lineitem"), tables.at("part"), {"l_partkey"}, {"p_partkey"}); + + auto const one = cudf::numeric_scalar{1}; + auto one_minus_discount = cudf::binary_operation(one, + joined->column("l_discount"), + cudf::binary_operator::SUB, + cudf::data_type{cudf::type_id::FLOAT64}); + auto revenue = cudf::binary_operation(joined->column("l_extendedprice"), + one_minus_discount->view(), + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}); + + auto promo = cudf::strings::starts_with(cudf::strings_column_view{joined->column("p_type")}, + cudf::string_scalar{"PROMO"}); + auto const zero = cudf::numeric_scalar{0}; + auto promo_revenue = cudf::copy_if_else(revenue->view(), zero, promo->view()); + auto const promo_total = + apply_reduction(promo_revenue->view(), cudf::aggregation::Kind::SUM, "promo_total"); + auto const revenue_total = + apply_reduction(revenue->view(), cudf::aggregation::Kind::SUM, "revenue_total"); + + auto const hundred = cudf::numeric_scalar{100}; + auto scaled_promo = cudf::binary_operation(promo_total->column("promo_total"), + hundred, + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}); + auto percentage = cudf::binary_operation(scaled_promo->view(), + revenue_total->column("revenue_total"), + cudf::binary_operator::DIV, + cudf::data_type{cudf::type_id::FLOAT64}); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + auto rounded = cudf::round(percentage->view(), 2, cudf::rounding_method::HALF_UP); +#pragma GCC diagnostic pop + + std::vector> result_columns; + result_columns.push_back(std::move(rounded)); + return std::make_unique( + std::make_unique(std::move(result_columns)), + std::vector{"promo_revenue"}); +} + +void ndsh_q14(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"lineitem", "part"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q14(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q14(sources); + result = execute_ndsh_q14(input); + } else { + result = execute_ndsh_q14(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q14(sources); + result = execute_ndsh_q14(input); + } else { + result = execute_ndsh_q14(tables); + } + write_ndsh_result(*result, "q14"); +} + +NVBENCH_BENCH(ndsh_q14) + .set_name("ndsh_q14") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q15.cpp b/cpp/benchmarks/ndsh/q15.cpp new file mode 100644 index 000000000000..4b76855d50c6 --- /dev/null +++ b/cpp/benchmarks/ndsh/q15.cpp @@ -0,0 +1,152 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include + +#include + +/** + * @file q15.cpp + * @brief Implement query 15 of the NDS-H benchmark. + * + * with revenue (supplier_no, total_revenue) as ( + * select + * l_suppkey, + * sum(l_extendedprice * (1 - l_discount)) + * from + * lineitem + * where + * l_shipdate >= date '1996-01-01' + * and l_shipdate < date '1996-01-01' + interval '3' month + * group by + * l_suppkey + * ) + * select + * s_suppkey, + * s_name, + * s_address, + * s_phone, + * total_revenue + * from + * supplier, + * revenue + * where + * s_suppkey = supplier_no + * and total_revenue = ( + * select + * max(total_revenue) + * from + * revenue + * ) + * order by + * s_suppkey; + */ + +std::unordered_map> load_ndsh_q15( + std::unordered_map& sources) +{ + std::unordered_map> tables; + tables.emplace("lineitem", + read_parquet(sources.at("lineitem").make_source_info(), + {"l_suppkey", "l_shipdate", "l_extendedprice", "l_discount"})); + tables.emplace("supplier", + read_parquet(sources.at("supplier").make_source_info(), + {"s_suppkey", "s_name", "s_address", "s_phone"})); + return tables; +} + +std::unique_ptr execute_ndsh_q15( + std::unordered_map> const& tables) +{ + auto const& lineitem = tables.at("lineitem"); + + auto const shipdate_ref = cudf::ast::column_reference(lineitem->column_id("l_shipdate")); + auto lower = cudf::timestamp_scalar(days_since_epoch(1996, 1, 1), true); + auto upper = cudf::timestamp_scalar(days_since_epoch(1996, 4, 1), true); + auto const lower_literal = cudf::ast::literal(lower); + auto const upper_literal = cudf::ast::literal(upper); + auto const after_lower = + cudf::ast::operation(cudf::ast::ast_operator::GREATER_EQUAL, shipdate_ref, lower_literal); + auto const before_upper = + cudf::ast::operation(cudf::ast::ast_operator::LESS, shipdate_ref, upper_literal); + auto const date_predicate = + cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_AND, after_lower, before_upper); + auto filtered_lineitem = apply_filter(lineitem, date_predicate); + + auto const one = cudf::numeric_scalar(1); + auto one_minus_discount = cudf::binary_operation(one, + filtered_lineitem->column("l_discount"), + cudf::binary_operator::SUB, + cudf::data_type{cudf::type_id::FLOAT64}); + auto revenue = cudf::binary_operation(filtered_lineitem->column("l_extendedprice"), + one_minus_discount->view(), + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}); + filtered_lineitem->append(revenue, "revenue"); + auto const grouped = apply_groupby( + filtered_lineitem, + groupby_context_t{{"l_suppkey"}, + {{"revenue", {{cudf::aggregation::Kind::SUM, "total_revenue"}}}}}); + + auto const max_aggregation = cudf::make_max_aggregation(); + auto max_revenue = cudf::reduce( + grouped->column("total_revenue"), *max_aggregation, cudf::data_type{cudf::type_id::FLOAT64}); + auto max_mask = cudf::binary_operation(grouped->column("total_revenue"), + *max_revenue, + cudf::binary_operator::EQUAL, + cudf::data_type{cudf::type_id::BOOL8}); + auto const maximum_suppliers = apply_mask(grouped, max_mask); + auto const joined = + apply_inner_join(tables.at("supplier"), maximum_suppliers, {"s_suppkey"}, {"l_suppkey"}); + auto const selected = + apply_projection(joined, {"s_suppkey", "s_name", "s_address", "s_phone", "total_revenue"}); + return apply_orderby(selected, {"s_suppkey"}, {cudf::order::ASCENDING}); +} + +void ndsh_q15(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"lineitem", "supplier"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q15(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q15(sources); + result = execute_ndsh_q15(input); + } else { + result = execute_ndsh_q15(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q15(sources); + result = execute_ndsh_q15(input); + } else { + result = execute_ndsh_q15(tables); + } + write_ndsh_result(*result, "q15"); +} + +NVBENCH_BENCH(ndsh_q15) + .set_name("ndsh_q15") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q16.cpp b/cpp/benchmarks/ndsh/q16.cpp new file mode 100644 index 000000000000..db7d128285ad --- /dev/null +++ b/cpp/benchmarks/ndsh/q16.cpp @@ -0,0 +1,180 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * @file q16.cpp + * @brief Implement query 16 of the NDS-H benchmark. + * + * select + * p_brand, + * p_type, + * p_size, + * count(distinct ps_suppkey) as supplier_cnt + * from + * partsupp, + * part + * where + * p_partkey = ps_partkey + * and p_brand <> 'Brand#45' + * and p_type not like 'MEDIUM POLISHED%' + * and p_size in (49, 14, 23, 45, 19, 3, 36, 9) + * and ps_suppkey not in ( + * select + * s_suppkey + * from + * supplier + * where + * s_comment like '%Customer%Complaints%' + * ) + * group by + * p_brand, + * p_type, + * p_size + * order by + * supplier_cnt desc, + * p_brand, + * p_type, + * p_size; + */ + +std::unordered_map> load_ndsh_q16( + std::unordered_map& sources) +{ + std::unordered_map> tables; + tables.emplace("part", + read_parquet(sources.at("part").make_source_info(), + {"p_partkey", "p_brand", "p_type", "p_size"})); + tables.emplace( + "partsupp", + read_parquet(sources.at("partsupp").make_source_info(), {"ps_partkey", "ps_suppkey"})); + tables.emplace( + "supplier", + read_parquet(sources.at("supplier").make_source_info(), {"s_suppkey", "s_comment"})); + return tables; +} + +std::unique_ptr execute_ndsh_q16( + std::unordered_map> const& tables) +{ + auto const& part = tables.at("part"); + + auto const excluded_brand = cudf::string_scalar{"Brand#45"}; + auto brand_mask = cudf::binary_operation(part->column("p_brand"), + excluded_brand, + cudf::binary_operator::NOT_EQUAL, + cudf::data_type{cudf::type_id::BOOL8}); + + auto const excluded_type = cudf::string_scalar{"MEDIUM POLISHED"}; + auto excluded_type_mask = + cudf::strings::starts_with(cudf::strings_column_view{part->column("p_type")}, excluded_type); + auto type_mask = cudf::unary_operation(excluded_type_mask->view(), cudf::unary_operator::NOT); + + std::unique_ptr size_mask; + for (auto const size : {int8_t{49}, + int8_t{14}, + int8_t{23}, + int8_t{45}, + int8_t{19}, + int8_t{3}, + int8_t{36}, + int8_t{9}}) { + auto const value = cudf::numeric_scalar{size}; + auto match = cudf::binary_operation(part->column("p_size"), + value, + cudf::binary_operator::EQUAL, + cudf::data_type{cudf::type_id::BOOL8}); + if (size_mask) { + size_mask = cudf::binary_operation(size_mask->view(), + match->view(), + cudf::binary_operator::LOGICAL_OR, + cudf::data_type{cudf::type_id::BOOL8}); + } else { + size_mask = std::move(match); + } + } + + auto part_mask = cudf::binary_operation(brand_mask->view(), + type_mask->view(), + cudf::binary_operator::LOGICAL_AND, + cudf::data_type{cudf::type_id::BOOL8}); + part_mask = cudf::binary_operation(part_mask->view(), + size_mask->view(), + cudf::binary_operator::LOGICAL_AND, + cudf::data_type{cudf::type_id::BOOL8}); + auto const filtered_part = apply_mask(part, part_mask); + + auto const program = cudf::strings::regex_program::create("Customer.*Complaints"); + auto complaint_mask = cudf::strings::contains_re( + cudf::strings_column_view{tables.at("supplier")->column("s_comment")}, *program); + auto const complaint_suppliers = apply_mask(tables.at("supplier"), complaint_mask); + + auto const joined = + apply_inner_join(filtered_part, tables.at("partsupp"), {"p_partkey"}, {"ps_partkey"}); + auto const eligible = + apply_left_anti_join(joined, complaint_suppliers, {"ps_suppkey"}, {"s_suppkey"}); + auto const grouped = apply_groupby( + eligible, + groupby_context_t{{"p_brand", "p_type", "p_size"}, + {{"ps_suppkey", {{cudf::aggregation::Kind::NUNIQUE, "supplier_cnt"}}}}}); + return apply_orderby(grouped, + {"supplier_cnt", "p_brand", "p_type", "p_size"}, + {cudf::order::DESCENDING, + cudf::order::ASCENDING, + cudf::order::ASCENDING, + cudf::order::ASCENDING}); +} + +void ndsh_q16(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"part", "partsupp", "supplier"}, sources); + + auto const stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q16(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q16(sources); + result = execute_ndsh_q16(input); + } else { + result = execute_ndsh_q16(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q16(sources); + result = execute_ndsh_q16(input); + } else { + result = execute_ndsh_q16(tables); + } + write_ndsh_result(*result, "q16"); +} + +NVBENCH_BENCH(ndsh_q16) + .set_name("ndsh_q16") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q17.cpp b/cpp/benchmarks/ndsh/q17.cpp new file mode 100644 index 000000000000..7ed685ba3250 --- /dev/null +++ b/cpp/benchmarks/ndsh/q17.cpp @@ -0,0 +1,145 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include + +#include + +/** + * @file q17.cpp + * @brief Implement query 17 of the NDS-H benchmark. + * + * select + * round(sum(l_extendedprice) / 7.0, 2) as avg_yearly + * from + * lineitem, + * part + * where + * p_partkey = l_partkey + * and p_brand = 'Brand#23' + * and p_container = 'MED BOX' + * and l_quantity < ( + * select + * 0.2 * avg(l_quantity) + * from + * lineitem + * where + * l_partkey = p_partkey + * ); + */ + +std::unordered_map> load_ndsh_q17( + std::unordered_map& sources) +{ + std::vector const part_columns = {"p_partkey", "p_brand", "p_container"}; + auto const brand_ref = cudf::ast::column_reference{1}; + auto const container_ref = cudf::ast::column_reference{2}; + auto brand = cudf::string_scalar{"Brand#23"}; + auto container = cudf::string_scalar{"MED BOX"}; + auto const brand_literal = cudf::ast::literal{brand}; + auto const container_literal = cudf::ast::literal{container}; + auto const brand_predicate = + cudf::ast::operation{cudf::ast::ast_operator::EQUAL, brand_ref, brand_literal}; + auto const container_predicate = + cudf::ast::operation{cudf::ast::ast_operator::EQUAL, container_ref, container_literal}; + auto part_predicate = std::make_unique( + cudf::ast::ast_operator::LOGICAL_AND, brand_predicate, container_predicate); + + std::unordered_map> tables; + tables.emplace("lineitem", + read_parquet(sources.at("lineitem").make_source_info(), + {"l_partkey", "l_quantity", "l_extendedprice"})); + tables.emplace( + "part", + read_parquet(sources.at("part").make_source_info(), part_columns, std::move(part_predicate))); + return tables; +} + +std::unique_ptr execute_ndsh_q17( + std::unordered_map> const& tables) +{ + auto const joined = + apply_inner_join(tables.at("part"), tables.at("lineitem"), {"p_partkey"}, {"l_partkey"}); + auto grouped = apply_groupby( + joined, + groupby_context_t{{"p_partkey"}, + {{"l_quantity", {{cudf::aggregation::Kind::MEAN, "avg_quantity"}}}}}); + + auto const threshold_factor = cudf::numeric_scalar{0.2}; + auto threshold = cudf::binary_operation(grouped->column("avg_quantity"), + threshold_factor, + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}); + grouped->append(threshold, "quantity_threshold"); + auto const with_threshold = apply_inner_join(joined, grouped, {"p_partkey"}, {"p_partkey"}); + + auto mask = cudf::binary_operation(with_threshold->column("l_quantity"), + with_threshold->column("quantity_threshold"), + cudf::binary_operator::LESS, + cudf::data_type{cudf::type_id::BOOL8}); + auto const filtered = apply_mask(with_threshold, mask); + auto const total = + apply_reduction(filtered->column("l_extendedprice"), cudf::aggregation::Kind::SUM, "total"); + auto const seven = cudf::numeric_scalar{7.0}; + auto avg_yearly = cudf::binary_operation(total->column("total"), + seven, + cudf::binary_operator::DIV, + cudf::data_type{cudf::type_id::FLOAT64}); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + auto rounded = cudf::round(avg_yearly->view(), 2, cudf::rounding_method::HALF_UP); +#pragma GCC diagnostic pop + + std::vector> columns; + columns.push_back(std::move(rounded)); + return std::make_unique(std::make_unique(std::move(columns)), + std::vector{"avg_yearly"}); +} + +void ndsh_q17(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"lineitem", "part"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q17(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q17(sources); + result = execute_ndsh_q17(input); + } else { + result = execute_ndsh_q17(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q17(sources); + result = execute_ndsh_q17(input); + } else { + result = execute_ndsh_q17(tables); + } + write_ndsh_result(*result, "q17"); +} + +NVBENCH_BENCH(ndsh_q17) + .set_name("ndsh_q17") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q18.cpp b/cpp/benchmarks/ndsh/q18.cpp new file mode 100644 index 000000000000..0fc0a7baf768 --- /dev/null +++ b/cpp/benchmarks/ndsh/q18.cpp @@ -0,0 +1,135 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include + +#include + +/** + * @file q18.cpp + * @brief Implement query 18 of the NDS-H benchmark. + * + * select + * c_name, + * c_custkey, + * o_orderkey, + * o_orderdate, + * o_totalprice, + * sum(l_quantity) + * from + * customer, + * orders, + * lineitem + * where + * o_orderkey in ( + * select + * l_orderkey + * from + * lineitem + * group by + * l_orderkey having + * sum(l_quantity) > 300 + * ) + * and c_custkey = o_custkey + * and o_orderkey = l_orderkey + * group by + * c_name, + * c_custkey, + * o_orderkey, + * o_orderdate, + * o_totalprice + * order by + * o_totalprice desc, + * o_orderdate + * limit 100; + */ + +std::unordered_map> load_ndsh_q18( + std::unordered_map& sources) +{ + std::unordered_map> tables; + tables.emplace("customer", + read_parquet(sources.at("customer").make_source_info(), {"c_custkey", "c_name"})); + tables.emplace( + "lineitem", + read_parquet(sources.at("lineitem").make_source_info(), {"l_orderkey", "l_quantity"})); + tables.emplace("orders", + read_parquet(sources.at("orders").make_source_info(), + {"o_orderkey", "o_custkey", "o_orderdate", "o_totalprice"})); + return tables; +} + +std::unique_ptr execute_ndsh_q18( + std::unordered_map> const& tables) +{ + auto const quantity_by_order = apply_groupby( + tables.at("lineitem"), + groupby_context_t{{"l_orderkey"}, + {{"l_quantity", {{cudf::aggregation::Kind::SUM, "sum_quantity"}}}}}); + auto const threshold = cudf::numeric_scalar{300}; + auto mask = cudf::binary_operation(quantity_by_order->column("sum_quantity"), + threshold, + cudf::binary_operator::GREATER, + cudf::data_type{cudf::type_id::BOOL8}); + auto const large_orders = apply_mask(quantity_by_order, mask); + + auto const qualifying_orders = + apply_left_semi_join(tables.at("orders"), large_orders, {"o_orderkey"}, {"l_orderkey"}); + auto const orders_lineitem = + apply_inner_join(qualifying_orders, tables.at("lineitem"), {"o_orderkey"}, {"l_orderkey"}); + auto const joined = + apply_inner_join(tables.at("customer"), orders_lineitem, {"c_custkey"}, {"o_custkey"}); + auto const grouped = apply_groupby( + joined, + groupby_context_t{{"c_name", "c_custkey", "o_orderkey", "o_orderdate", "o_totalprice"}, + {{"l_quantity", {{cudf::aggregation::Kind::SUM, "sum(l_quantity)"}}}}}); + auto const ordered = apply_orderby( + grouped, {"o_totalprice", "o_orderdate"}, {cudf::order::DESCENDING, cudf::order::ASCENDING}); + return apply_slice(ordered, 0, 100); +} + +void ndsh_q18(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"customer", "orders", "lineitem"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q18(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch& launch) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q18(sources); + result = execute_ndsh_q18(input); + } else { + result = execute_ndsh_q18(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q18(sources); + result = execute_ndsh_q18(input); + } else { + result = execute_ndsh_q18(tables); + } + write_ndsh_result(*result, "q18"); +} + +NVBENCH_BENCH(ndsh_q18) + .set_name("ndsh_q18") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q19.cpp b/cpp/benchmarks/ndsh/q19.cpp new file mode 100644 index 000000000000..4ada57338e8b --- /dev/null +++ b/cpp/benchmarks/ndsh/q19.cpp @@ -0,0 +1,228 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +/** + * @file q19.cpp + * @brief Implement query 19 of the NDS-H benchmark. + * + * select + * round(sum(l_extendedprice* (1 - l_discount)), 2) 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 <= 20 + * 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 <= 30 + * and p_size between 1 and 15 + * and l_shipmode in ('AIR', 'AIR REG') + * and l_shipinstruct = 'DELIVER IN PERSON' + * ) + */ + +std::unordered_map> load_ndsh_q19( + std::unordered_map& sources) +{ + std::unordered_map> tables; + tables.emplace("lineitem", + read_parquet(sources.at("lineitem").make_source_info(), + {"l_partkey", + "l_shipmode", + "l_shipinstruct", + "l_quantity", + "l_extendedprice", + "l_discount"})); + tables.emplace("part", + read_parquet(sources.at("part").make_source_info(), + {"p_partkey", "p_brand", "p_container", "p_size"})); + return tables; +} + +std::unique_ptr execute_ndsh_q19( + std::unordered_map> const& tables) +{ + auto const& lineitem = tables.at("lineitem"); + auto const& part = tables.at("part"); + + auto const shipmode_ref = cudf::ast::column_reference(lineitem->column_id("l_shipmode")); + auto const shipinstruct_ref = cudf::ast::column_reference(lineitem->column_id("l_shipinstruct")); + auto air = cudf::string_scalar("AIR"); + auto air_reg = cudf::string_scalar("AIR REG"); + auto deliver_in_person = cudf::string_scalar("DELIVER IN PERSON"); + auto const air_literal = cudf::ast::literal(air); + auto const air_reg_literal = cudf::ast::literal(air_reg); + auto const instruct_literal = cudf::ast::literal(deliver_in_person); + auto const air_predicate = + cudf::ast::operation(cudf::ast::ast_operator::EQUAL, shipmode_ref, air_literal); + auto const air_reg_predicate = + cudf::ast::operation(cudf::ast::ast_operator::EQUAL, shipmode_ref, air_reg_literal); + auto const shipmode_predicate = + cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_OR, air_predicate, air_reg_predicate); + auto const instruct_predicate = + cudf::ast::operation(cudf::ast::ast_operator::EQUAL, shipinstruct_ref, instruct_literal); + auto const shipping_predicate = cudf::ast::operation( + cudf::ast::ast_operator::LOGICAL_AND, shipmode_predicate, instruct_predicate); + auto filtered_lineitem = apply_filter(lineitem, shipping_predicate); + + auto joined = apply_inner_join(part, filtered_lineitem, {"p_partkey"}, {"l_partkey"}); + auto const brand_ref = cudf::ast::column_reference(joined->column_id("p_brand")); + auto const container_ref = cudf::ast::column_reference(joined->column_id("p_container")); + auto const quantity_ref = cudf::ast::column_reference(joined->column_id("l_quantity")); + auto const size_ref = cudf::ast::column_reference(joined->column_id("p_size")); + + cudf::ast::tree tree; + std::vector> strings; + std::vector>> numbers; + auto equals = [&](cudf::ast::column_reference const& column, + std::string_view value) -> cudf::ast::expression const& { + strings.push_back(std::make_unique(value)); + auto& literal = tree.push(cudf::ast::literal(*strings.back())); + return tree.push(cudf::ast::operation(cudf::ast::ast_operator::EQUAL, column, literal)); + }; + auto between = [&](cudf::ast::column_reference const& column, + int8_t lower, + int8_t upper) -> cudf::ast::expression const& { + numbers.push_back(std::make_unique>(lower)); + auto& lower_literal = tree.push(cudf::ast::literal(*numbers.back())); + numbers.push_back(std::make_unique>(upper)); + auto& upper_literal = tree.push(cudf::ast::literal(*numbers.back())); + auto& lower_predicate = tree.push( + cudf::ast::operation(cudf::ast::ast_operator::GREATER_EQUAL, column, lower_literal)); + auto& upper_predicate = + tree.push(cudf::ast::operation(cudf::ast::ast_operator::LESS_EQUAL, column, upper_literal)); + return tree.push( + cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_AND, lower_predicate, upper_predicate)); + }; + auto branch = [&](std::string_view brand, + std::array const& containers, + int8_t quantity_lower, + int8_t quantity_upper, + int8_t size_upper) -> cudf::ast::expression const& { + auto& brand_predicate = equals(brand_ref, brand); + auto& container0 = equals(container_ref, containers[0]); + auto& container1 = equals(container_ref, containers[1]); + auto& container2 = equals(container_ref, containers[2]); + auto& container3 = equals(container_ref, containers[3]); + auto& containers01 = + tree.push(cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_OR, container0, container1)); + auto& containers23 = + tree.push(cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_OR, container2, container3)); + auto& container_predicate = tree.push( + cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_OR, containers01, containers23)); + auto& quantity_predicate = between(quantity_ref, quantity_lower, quantity_upper); + auto& size_predicate = between(size_ref, int8_t{1}, size_upper); + auto& brand_container = tree.push(cudf::ast::operation( + cudf::ast::ast_operator::LOGICAL_AND, brand_predicate, container_predicate)); + auto& quantity_size = tree.push(cudf::ast::operation( + cudf::ast::ast_operator::LOGICAL_AND, quantity_predicate, size_predicate)); + return tree.push( + cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_AND, brand_container, quantity_size)); + }; + + auto& branch1 = branch("Brand#12", {"SM CASE", "SM BOX", "SM PACK", "SM PKG"}, 1, 11, 5); + auto& branch2 = branch("Brand#23", {"MED BAG", "MED BOX", "MED PKG", "MED PACK"}, 10, 20, 10); + auto& branch3 = branch("Brand#34", {"LG CASE", "LG BOX", "LG PACK", "LG PKG"}, 20, 30, 15); + auto& branches12 = + tree.push(cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_OR, branch1, branch2)); + auto& branches = + tree.push(cudf::ast::operation(cudf::ast::ast_operator::LOGICAL_OR, branches12, branch3)); + auto filtered = apply_filter(joined, branches); + + auto const one = cudf::numeric_scalar(1); + auto one_minus_discount = cudf::binary_operation(one, + filtered->column("l_discount"), + cudf::binary_operator::SUB, + cudf::data_type{cudf::type_id::FLOAT64}); + auto revenue = cudf::binary_operation(filtered->column("l_extendedprice"), + one_minus_discount->view(), + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}); + auto reduced = apply_reduction(revenue->view(), cudf::aggregation::Kind::SUM, "revenue"); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + auto rounded = cudf::round(reduced->column("revenue"), 2, cudf::rounding_method::HALF_UP); +#pragma GCC diagnostic pop + std::vector> result_columns; + result_columns.push_back(std::move(rounded)); + return std::make_unique( + std::make_unique(std::move(result_columns)), std::vector{"revenue"}); +} + +void ndsh_q19(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"lineitem", "part"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q19(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q19(sources); + result = execute_ndsh_q19(input); + } else { + result = execute_ndsh_q19(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q19(sources); + result = execute_ndsh_q19(input); + } else { + result = execute_ndsh_q19(tables); + } + write_ndsh_result(*result, "q19"); +} + +NVBENCH_BENCH(ndsh_q19) + .set_name("ndsh_q19") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q20.cpp b/cpp/benchmarks/ndsh/q20.cpp new file mode 100644 index 000000000000..5b1efb8b194b --- /dev/null +++ b/cpp/benchmarks/ndsh/q20.cpp @@ -0,0 +1,182 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include + +#include + +/** + * @file q20.cpp + * @brief Implement query 20 of the NDS-H benchmark. + * + * select + * s_name, + * s_address + * from + * supplier, + * nation + * where + * s_suppkey in ( + * select + * ps_suppkey + * from + * partsupp + * where + * ps_partkey in ( + * select + * p_partkey + * from + * part + * where + * p_name like 'forest%' + * ) + * and ps_availqty > ( + * select + * 0.5 * sum(l_quantity) + * from + * lineitem + * where + * l_partkey = ps_partkey + * and l_suppkey = ps_suppkey + * and l_shipdate >= date '1994-01-01' + * and l_shipdate < date '1994-01-01' + interval '1' year + * ) + * ) + * and s_nationkey = n_nationkey + * and n_name = 'CANADA' + * order by + * s_name; + */ + +std::unordered_map> load_ndsh_q20( + std::unordered_map& sources) +{ + std::vector const lineitem_columns = { + "l_partkey", "l_suppkey", "l_quantity", "l_shipdate"}; + auto const shipdate = cudf::ast::column_reference{3}; + auto start = cudf::timestamp_scalar{days_since_epoch(1994, 1, 1), true}; + auto end = cudf::timestamp_scalar{days_since_epoch(1995, 1, 1), true}; + auto const start_literal = cudf::ast::literal{start}; + auto const end_literal = cudf::ast::literal{end}; + auto const after_start = + cudf::ast::operation{cudf::ast::ast_operator::GREATER_EQUAL, shipdate, start_literal}; + auto const before_end = + cudf::ast::operation{cudf::ast::ast_operator::LESS, shipdate, end_literal}; + auto lineitem_filter = std::make_unique( + cudf::ast::ast_operator::LOGICAL_AND, after_start, before_end); + + std::vector const nation_columns = {"n_nationkey", "n_name"}; + auto const nation_name = cudf::ast::column_reference{1}; + auto canada = cudf::string_scalar{"CANADA"}; + auto const canada_literal = cudf::ast::literal{canada}; + auto nation_filter = std::make_unique( + cudf::ast::ast_operator::EQUAL, nation_name, canada_literal); + + std::unordered_map> tables; + tables.emplace( + "lineitem", + read_parquet( + sources.at("lineitem").make_source_info(), lineitem_columns, std::move(lineitem_filter))); + tables.emplace( + "nation", + read_parquet( + sources.at("nation").make_source_info(), nation_columns, std::move(nation_filter))); + tables.emplace("part", + read_parquet(sources.at("part").make_source_info(), {"p_partkey", "p_name"})); + tables.emplace("partsupp", + read_parquet(sources.at("partsupp").make_source_info(), + {"ps_partkey", "ps_suppkey", "ps_availqty"})); + tables.emplace("supplier", + read_parquet(sources.at("supplier").make_source_info(), + {"s_suppkey", "s_nationkey", "s_name", "s_address"})); + return tables; +} + +std::unique_ptr execute_ndsh_q20( + std::unordered_map> const& tables) +{ + auto quantities = apply_groupby( + tables.at("lineitem"), + groupby_context_t{{"l_partkey", "l_suppkey"}, + {{"l_quantity", {{cudf::aggregation::Kind::SUM, "sum_quantity"}}}}}); + auto const half = cudf::numeric_scalar{0.5}; + auto threshold = cudf::binary_operation(quantities->column("sum_quantity"), + half, + cudf::binary_operator::MUL, + cudf::data_type{cudf::type_id::FLOAT64}); + quantities->append(threshold, "threshold"); + + auto const forest = cudf::string_scalar{"forest"}; + auto forest_mask = cudf::strings::starts_with( + cudf::strings_column_view{tables.at("part")->column("p_name")}, forest); + auto const forest_parts = + apply_distinct(apply_projection(apply_mask(tables.at("part"), forest_mask), {"p_partkey"})); + + auto const inventory = + apply_inner_join(forest_parts, tables.at("partsupp"), {"p_partkey"}, {"ps_partkey"}); + auto const inventory_quantity = apply_inner_join( + inventory, quantities, {"ps_suppkey", "p_partkey"}, {"l_suppkey", "l_partkey"}); + auto available = cudf::binary_operation(inventory_quantity->column("ps_availqty"), + inventory_quantity->column("threshold"), + cudf::binary_operator::GREATER, + cudf::data_type{cudf::type_id::BOOL8}); + auto const eligible_suppliers = + apply_distinct(apply_projection(apply_mask(inventory_quantity, available), {"ps_suppkey"})); + + auto const canadian_suppliers = + apply_inner_join(tables.at("supplier"), tables.at("nation"), {"s_nationkey"}, {"n_nationkey"}); + auto const result = + apply_inner_join(eligible_suppliers, canadian_suppliers, {"ps_suppkey"}, {"s_suppkey"}); + return apply_orderby( + apply_projection(result, {"s_name", "s_address"}), {"s_name"}, {cudf::order::ASCENDING}); +} + +void ndsh_q20(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources( + scale_factor, {"lineitem", "nation", "part", "partsupp", "supplier"}, sources); + + auto stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q20(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q20(sources); + result = execute_ndsh_q20(input); + } else { + result = execute_ndsh_q20(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q20(sources); + result = execute_ndsh_q20(input); + } else { + result = execute_ndsh_q20(tables); + } + write_ndsh_result(*result, "q20"); +} + +NVBENCH_BENCH(ndsh_q20) + .set_name("ndsh_q20") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q21.cpp b/cpp/benchmarks/ndsh/q21.cpp new file mode 100644 index 000000000000..ba05e553ce26 --- /dev/null +++ b/cpp/benchmarks/ndsh/q21.cpp @@ -0,0 +1,195 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include + +#include + +/** + * @file q21.cpp + * @brief Implement query 21 of the NDS-H benchmark. + * + * select + * s_name, + * count(*) as numwait + * from + * supplier, + * lineitem l1, + * orders, + * nation + * where + * s_suppkey = l1.l_suppkey + * and o_orderkey = l1.l_orderkey + * and o_orderstatus = 'F' + * and l1.l_receiptdate > l1.l_commitdate + * and exists ( + * select + * * + * from + * lineitem l2 + * where + * l2.l_orderkey = l1.l_orderkey + * and l2.l_suppkey <> l1.l_suppkey + * ) + * and not exists ( + * select + * * + * from + * lineitem l3 + * where + * l3.l_orderkey = l1.l_orderkey + * and l3.l_suppkey <> l1.l_suppkey + * and l3.l_receiptdate > l3.l_commitdate + * ) + * and s_nationkey = n_nationkey + * and n_name = 'SAUDI ARABIA' + * group by + * s_name + * order by + * numwait desc, + * s_name + * limit 100; + */ + +std::unordered_map> load_ndsh_q21( + std::unordered_map& sources) +{ + std::vector const nation_columns = {"n_nationkey", "n_name"}; + auto const n_name_ref = cudf::ast::column_reference{1}; + auto saudi_arabia = cudf::string_scalar{"SAUDI ARABIA"}; + auto const saudi_arabia_literal = cudf::ast::literal{saudi_arabia}; + auto nation_predicate = std::make_unique( + cudf::ast::ast_operator::EQUAL, n_name_ref, saudi_arabia_literal); + + std::vector const orders_columns = {"o_orderkey", "o_orderstatus"}; + auto const o_orderstatus_ref = cudf::ast::column_reference{1}; + auto final_status = cudf::string_scalar{"F"}; + auto const final_status_literal = cudf::ast::literal{final_status}; + auto orders_predicate = std::make_unique( + cudf::ast::ast_operator::EQUAL, o_orderstatus_ref, final_status_literal); + + std::unordered_map> tables; + tables.emplace("lineitem", + read_parquet(sources.at("lineitem").make_source_info(), + {"l_orderkey", "l_suppkey", "l_receiptdate", "l_commitdate"})); + tables.emplace( + "nation", + read_parquet( + sources.at("nation").make_source_info(), nation_columns, std::move(nation_predicate))); + tables.emplace( + "orders", + read_parquet( + sources.at("orders").make_source_info(), orders_columns, std::move(orders_predicate))); + tables.emplace("supplier", + read_parquet(sources.at("supplier").make_source_info(), + {"s_suppkey", "s_nationkey", "s_name"})); + return tables; +} + +std::unique_ptr execute_ndsh_q21( + std::unordered_map> const& tables) +{ + auto const& lineitem = tables.at("lineitem"); + + auto const late_mask = cudf::binary_operation(lineitem->column("l_receiptdate"), + lineitem->column("l_commitdate"), + cudf::binary_operator::GREATER, + cudf::data_type{cudf::type_id::BOOL8}); + auto const late_lineitem = apply_mask(lineitem, late_mask); + + auto const supplier_pairs = + apply_distinct(apply_projection(lineitem, {"l_orderkey", "l_suppkey"})); + auto const suppliers_per_order = apply_groupby( + supplier_pairs, + groupby_context_t{{"l_orderkey"}, + {{"l_suppkey", {{cudf::aggregation::Kind::COUNT_ALL, "supplier_count"}}}}}); + auto const one = cudf::numeric_scalar{1}; + auto const multiple_mask = cudf::binary_operation(suppliers_per_order->column("supplier_count"), + one, + cudf::binary_operator::GREATER, + cudf::data_type{cudf::type_id::BOOL8}); + auto const multiple_orders = + apply_projection(apply_mask(suppliers_per_order, multiple_mask), {"l_orderkey"}); + + auto const late_supplier_pairs = + apply_distinct(apply_projection(late_lineitem, {"l_orderkey", "l_suppkey"})); + auto const late_suppliers_per_order = apply_groupby( + late_supplier_pairs, + groupby_context_t{{"l_orderkey"}, + {{"l_suppkey", {{cudf::aggregation::Kind::COUNT_ALL, "supplier_count"}}}}}); + auto const single_late_mask = + cudf::binary_operation(late_suppliers_per_order->column("supplier_count"), + one, + cudf::binary_operator::EQUAL, + cudf::data_type{cudf::type_id::BOOL8}); + auto const single_late_orders = + apply_projection(apply_mask(late_suppliers_per_order, single_late_mask), {"l_orderkey"}); + + auto const qualifying_late = + apply_left_semi_join(late_lineitem, multiple_orders, {"l_orderkey"}, {"l_orderkey"}); + auto const qualifying_lineitem = + apply_left_semi_join(qualifying_late, single_late_orders, {"l_orderkey"}, {"l_orderkey"}); + + auto const saudi_suppliers = + apply_inner_join(tables.at("supplier"), tables.at("nation"), {"s_nationkey"}, {"n_nationkey"}); + auto const supplier_lineitem = + apply_inner_join(qualifying_lineitem, saudi_suppliers, {"l_suppkey"}, {"s_suppkey"}); + auto const joined = + apply_inner_join(supplier_lineitem, tables.at("orders"), {"l_orderkey"}, {"o_orderkey"}); + + auto const grouped = apply_groupby( + joined, + groupby_context_t{{"s_name"}, + {{"l_orderkey", {{cudf::aggregation::Kind::COUNT_ALL, "numwait"}}}}}); + auto const ordered = apply_orderby( + grouped, {"numwait", "s_name"}, {cudf::order::DESCENDING, cudf::order::ASCENDING}); + return apply_slice(ordered, 0, 100); +} + +void ndsh_q21(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources( + scale_factor, {"lineitem", "nation", "orders", "supplier"}, sources); + + auto const stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q21(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q21(sources); + result = execute_ndsh_q21(input); + } else { + result = execute_ndsh_q21(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q21(sources); + result = execute_ndsh_q21(input); + } else { + result = execute_ndsh_q21(tables); + } + write_ndsh_result(*result, "q21"); +} + +NVBENCH_BENCH(ndsh_q21) + .set_name("ndsh_q21") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/q22.cpp b/cpp/benchmarks/ndsh/q22.cpp new file mode 100644 index 000000000000..4cf5aa8cf8c7 --- /dev/null +++ b/cpp/benchmarks/ndsh/q22.cpp @@ -0,0 +1,155 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "utilities.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/** + * @file q22.cpp + * @brief Implement query 22 of the NDS-H benchmark. + * + * select + * cntrycode, + * count(*) as numcust, + * sum(c_acctbal) as totacctbal + * from ( + * select + * substring(c_phone from 1 for 2) as cntrycode, + * c_acctbal + * from + * customer + * where + * substring(c_phone from 1 for 2) in + * (13, 31, 23, 29, 30, 18, 17) + * and c_acctbal > ( + * select + * avg(c_acctbal) + * from + * customer + * where + * c_acctbal > 0.00 + * and substring (c_phone from 1 for 2) in + * (13, 31, 23, 29, 30, 18, 17) + * ) + * and not exists ( + * select + * * + * from + * orders + * where + * o_custkey = c_custkey + * ) + * ) as custsale + * group by + * cntrycode + * order by + * cntrycode; + */ + +std::unordered_map> load_ndsh_q22( + std::unordered_map& sources) +{ + std::unordered_map> tables; + tables.emplace( + "customer", + read_parquet(sources.at("customer").make_source_info(), {"c_custkey", "c_phone", "c_acctbal"})); + tables.emplace("orders", read_parquet(sources.at("orders").make_source_info(), {"o_custkey"})); + return tables; +} + +std::unique_ptr execute_ndsh_q22( + std::unordered_map> const& tables) +{ + auto customer = apply_projection(tables.at("customer"), {"c_custkey", "c_phone", "c_acctbal"}); + auto country_code = + cudf::strings::slice_strings(cudf::strings_column_view{customer->column("c_phone")}, + std::optional{0}, + std::optional{2}); + customer->append(country_code, "cntrycode"); + + auto const country_pattern = cudf::strings::regex_program::create("^(13|31|23|29|30|18|17)$"); + auto country_mask = cudf::strings::contains_re( + cudf::strings_column_view{customer->column("cntrycode")}, *country_pattern); + auto selected = apply_mask(customer, country_mask); + selected = apply_projection(selected, {"c_acctbal", "c_custkey", "cntrycode"}); + + auto const zero = cudf::numeric_scalar{0.0}; + auto positive_mask = cudf::binary_operation(selected->column("c_acctbal"), + zero, + cudf::binary_operator::GREATER, + cudf::data_type{cudf::type_id::BOOL8}); + auto const positive = apply_mask(selected, positive_mask); + auto const mean = cudf::make_mean_aggregation(); + auto average = + cudf::reduce(positive->column("c_acctbal"), *mean, cudf::data_type{cudf::type_id::FLOAT64}); + + auto const without_orders = + apply_left_anti_join(selected, tables.at("orders"), {"c_custkey"}, {"o_custkey"}); + auto above_average_mask = cudf::binary_operation(without_orders->column("c_acctbal"), + *average, + cudf::binary_operator::GREATER, + cudf::data_type{cudf::type_id::BOOL8}); + auto const above_average = apply_mask(without_orders, above_average_mask); + + auto const grouped = + apply_groupby(above_average, + groupby_context_t{{"cntrycode"}, + {{"c_acctbal", + {{cudf::aggregation::Kind::COUNT_ALL, "numcust"}, + {cudf::aggregation::Kind::SUM, "totacctbal"}}}}}); + return apply_orderby(grouped, {"cntrycode"}, {cudf::order::ASCENDING}); +} + +void ndsh_q22(nvbench::state& state) +{ + auto const scale_factor = state.get_float64("scale_factor"); + auto const mode = query_mode_from_string(state.get_string("mode")); + std::unordered_map sources; + generate_parquet_data_sources(scale_factor, {"customer", "orders"}, sources); + + auto const stream = cudf::get_default_stream(); + state.set_cuda_stream(nvbench::make_cuda_stream_view(stream.value())); + std::unordered_map> tables; + if (mode == query_mode::COMPUTE_ONLY) { tables = load_ndsh_q22(sources); } + auto const mem_stats_logger = cudf::memory_stats_logger(); + state.exec(nvbench::exec_tag::sync, [&](nvbench::launch&) { + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q22(sources); + result = execute_ndsh_q22(input); + } else { + result = execute_ndsh_q22(tables); + } + }); + state.add_buffer_size( + mem_stats_logger.peak_memory_usage(), "peak_memory_usage", "peak_memory_usage"); + if (not write_ndsh_results()) { return; } + std::unique_ptr result; + if (mode == query_mode::END_TO_END) { + auto input = load_ndsh_q22(sources); + result = execute_ndsh_q22(input); + } else { + result = execute_ndsh_q22(tables); + } + write_ndsh_result(*result, "q22"); +} + +NVBENCH_BENCH(ndsh_q22) + .set_name("ndsh_q22") + .add_string_axis("mode", {"end_to_end", "compute_only"}) + .add_float64_axis("scale_factor", {0.01, 0.1, 1}); diff --git a/cpp/benchmarks/ndsh/utilities.cpp b/cpp/benchmarks/ndsh/utilities.cpp index 324ce8e9a88f..2a7f09fe944b 100644 --- a/cpp/benchmarks/ndsh/utilities.cpp +++ b/cpp/benchmarks/ndsh/utilities.cpp @@ -8,11 +8,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -27,7 +29,9 @@ #include #include #include +#include #include +#include #include namespace { @@ -91,8 +95,50 @@ std::unordered_map const> const SCHEMAS = {"customer", CUSTOMER_SCHEMA}, {"nation", NATION_SCHEMA}, {"region", REGION_SCHEMA}}; + +std::vector column_ids(std::unique_ptr const& table, + std::vector const& columns) +{ + std::vector result; + std::transform( + columns.begin(), columns.end(), std::back_inserter(result), [&](auto const& column) { + return table->column_id(column); + }); + return result; +} + +std::unique_ptr apply_left_filter_join( + std::unique_ptr const& left_input, + std::unique_ptr const& right_input, + std::vector const& left_on, + std::vector const& right_on, + cudf::null_equality compare_nulls, + bool semi_join) +{ + auto const stream = cudf::get_default_stream(); + auto const left_selected = left_input->table().select(column_ids(left_input, left_on)); + auto const right_selected = right_input->table().select(column_ids(right_input, right_on)); + cudf::filtered_join join{ + right_selected, compare_nulls, stream, cudf::get_current_device_resource_ref()}; + auto indices = semi_join ? join.semi_join(left_selected) : join.anti_join(left_selected); + auto const indices_view = cudf::column_view{cudf::device_span{*indices}}; + auto result = + cudf::gather(left_input->table(), indices_view, cudf::out_of_bounds_policy::DONT_CHECK); + return std::make_unique(std::move(result), left_input->column_names()); +} } // namespace +query_mode query_mode_from_string(std::string const& str) +{ + if (str == "end_to_end") { + return query_mode::END_TO_END; + } else if (str == "compute_only") { + return query_mode::COMPUTE_ONLY; + } else { + CUDF_FAIL("unrecognized query mode: " + str); + } +} + cudf::table_view table_with_names::table() const { return tbl->view(); } cudf::column_view table_with_names::column(std::string const& col_name) const @@ -146,6 +192,14 @@ void table_with_names::to_parquet(std::string const& filepath) const cudf::io::write_parquet(options); } +std::unique_ptr apply_projection(std::unique_ptr const& table, + std::vector const& columns) +{ + CUDF_BENCHMARK_RANGE(); + auto result = std::make_unique(table->select(columns)); + return std::make_unique(std::move(result), columns); +} + std::unique_ptr join_and_gather(cudf::table_view const& left_input, cudf::table_view const& right_input, std::vector const& left_on, @@ -213,6 +267,60 @@ std::unique_ptr apply_inner_join( return std::make_unique(std::move(table), merged_column_names); } +std::unique_ptr apply_left_join( + std::unique_ptr const& left_input, + std::unique_ptr const& right_input, + std::vector const& left_on, + std::vector const& right_on, + cudf::null_equality compare_nulls) +{ + CUDF_BENCHMARK_RANGE(); + auto const left_selected = left_input->table().select(column_ids(left_input, left_on)); + auto const right_selected = right_input->table().select(column_ids(right_input, right_on)); + auto const [left_indices, right_indices] = + cudf::left_join(left_selected, right_selected, compare_nulls); + auto const left_indices_view = + cudf::column_view{cudf::device_span{*left_indices}}; + auto const right_indices_view = + cudf::column_view{cudf::device_span{*right_indices}}; + auto left_result = + cudf::gather(left_input->table(), left_indices_view, cudf::out_of_bounds_policy::DONT_CHECK); + auto right_result = + cudf::gather(right_input->table(), right_indices_view, cudf::out_of_bounds_policy::NULLIFY); + auto joined_columns = left_result->release(); + auto right_columns = right_result->release(); + joined_columns.insert(joined_columns.end(), + std::make_move_iterator(right_columns.begin()), + std::make_move_iterator(right_columns.end())); + std::vector column_names = left_input->column_names(); + column_names.insert( + column_names.end(), right_input->column_names().begin(), right_input->column_names().end()); + return std::make_unique( + std::make_unique(std::move(joined_columns)), std::move(column_names)); +} + +std::unique_ptr apply_left_semi_join( + std::unique_ptr const& left_input, + std::unique_ptr const& right_input, + std::vector const& left_on, + std::vector const& right_on, + cudf::null_equality compare_nulls) +{ + CUDF_BENCHMARK_RANGE(); + return apply_left_filter_join(left_input, right_input, left_on, right_on, compare_nulls, true); +} + +std::unique_ptr apply_left_anti_join( + std::unique_ptr const& left_input, + std::unique_ptr const& right_input, + std::vector const& left_on, + std::vector const& right_on, + cudf::null_equality compare_nulls) +{ + CUDF_BENCHMARK_RANGE(); + return apply_left_filter_join(left_input, right_input, left_on, right_on, compare_nulls, false); +} + std::unique_ptr apply_filter(std::unique_ptr const& table, cudf::ast::operation const& predicate) { @@ -230,6 +338,28 @@ std::unique_ptr apply_mask(std::unique_ptr c return std::make_unique(std::move(result_table), table->column_names()); } +std::unique_ptr apply_distinct(std::unique_ptr const& table) +{ + CUDF_BENCHMARK_RANGE(); + std::vector keys(table->table().num_columns()); + std::iota(keys.begin(), keys.end(), 0); + auto result = cudf::distinct(table->table(), keys); + return std::make_unique(std::move(result), table->column_names()); +} + +std::unique_ptr apply_slice(std::unique_ptr const& table, + cudf::size_type begin, + cudf::size_type end) +{ + CUDF_BENCHMARK_RANGE(); + auto const num_rows = table->table().num_rows(); + begin = std::clamp(begin, cudf::size_type{0}, num_rows); + end = std::clamp(end, begin, num_rows); + auto const view = cudf::slice(table->table(), {begin, end}).front(); + auto result = std::make_unique(view); + return std::make_unique(std::move(result), table->column_names()); +} + std::unique_ptr apply_groupby(std::unique_ptr const& table, groupby_context_t const& ctx) { @@ -245,12 +375,21 @@ std::unique_ptr apply_groupby(std::unique_ptr()); + } else if (agg.first == cudf::aggregation::Kind::MIN) { + requests.back().aggregations.push_back( + cudf::make_min_aggregation()); + } else if (agg.first == cudf::aggregation::Kind::MAX) { + requests.back().aggregations.push_back( + cudf::make_max_aggregation()); } else if (agg.first == cudf::aggregation::Kind::MEAN) { requests.back().aggregations.push_back( cudf::make_mean_aggregation()); } else if (agg.first == cudf::aggregation::Kind::COUNT_ALL) { requests.back().aggregations.push_back( - cudf::make_count_aggregation()); + cudf::make_count_aggregation(cudf::null_policy::INCLUDE)); + } else if (agg.first == cudf::aggregation::Kind::NUNIQUE) { + requests.back().aggregations.push_back( + cudf::make_nunique_aggregation()); } else { throw std::runtime_error("Unsupported aggregation"); } @@ -292,8 +431,24 @@ std::unique_ptr apply_reduction(cudf::column_view const& colum std::string const& col_name) { CUDF_BENCHMARK_RANGE(); - auto const agg = cudf::make_sum_aggregation(); - auto const result = cudf::reduce(column, *agg, column.type()); + std::unique_ptr agg; + auto output_type = column.type(); + if (agg_kind == cudf::aggregation::Kind::SUM) { + agg = cudf::make_sum_aggregation(); + } else if (agg_kind == cudf::aggregation::Kind::MIN) { + agg = cudf::make_min_aggregation(); + } else if (agg_kind == cudf::aggregation::Kind::MAX) { + agg = cudf::make_max_aggregation(); + } else if (agg_kind == cudf::aggregation::Kind::MEAN) { + agg = cudf::make_mean_aggregation(); + output_type = cudf::data_type{cudf::type_id::FLOAT64}; + } else if (agg_kind == cudf::aggregation::Kind::COUNT_ALL) { + agg = cudf::make_count_aggregation(cudf::null_policy::INCLUDE); + output_type = cudf::data_type{cudf::type_id::INT32}; + } else { + throw std::runtime_error("Unsupported aggregation"); + } + auto const result = cudf::reduce(column, *agg, output_type); cudf::size_type const len = 1; auto col = cudf::make_column_from_scalar(*result, len); std::vector> columns; @@ -452,4 +607,28 @@ void generate_parquet_data_sources(double scale_factor, auto region = cudf::datagen::generate_region(stream, managed_pool_mr); write_to_parquet_device_buffer(region, SCHEMAS.at("region"), sources.at("region")); } + + if (write_ndsh_results()) { + auto const input_directory = + std::filesystem::path{cudf::benchmark_output_directory()} / "input"; + std::filesystem::create_directories(input_directory); + for (auto& [name, source] : sources) { + auto const path = input_directory / (name + ".parquet"); + if (not std::filesystem::exists(path)) { + read_parquet(source.make_source_info())->to_parquet(path.string()); + } + } + } +} + +bool write_ndsh_results() { return not cudf::benchmark_output_directory().empty(); } + +void write_ndsh_result(table_with_names const& result, std::string const& query_name) +{ + static std::unordered_set written_results; + if (not written_results.insert(query_name).second) { return; } + auto const result_directory = + std::filesystem::path{cudf::benchmark_output_directory()} / "results"; + std::filesystem::create_directories(result_directory); + result.to_parquet((result_directory / (query_name + ".parquet")).string()); } diff --git a/cpp/benchmarks/ndsh/utilities.hpp b/cpp/benchmarks/ndsh/utilities.hpp index 7864e547b225..19ab34739ef1 100644 --- a/cpp/benchmarks/ndsh/utilities.hpp +++ b/cpp/benchmarks/ndsh/utilities.hpp @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION. + * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ @@ -64,6 +64,19 @@ class table_with_names { std::vector col_names; }; +enum class query_mode : int32_t { END_TO_END = 0, COMPUTE_ONLY = 1 }; + +[[nodiscard]] query_mode query_mode_from_string(std::string const& str); + +/** + * @brief Select and copy named columns from a table + * + * @param table The input table + * @param columns The columns to copy + */ +[[nodiscard]] std::unique_ptr apply_projection( + std::unique_ptr const& table, std::vector const& columns); + /** * @brief Inner join two tables and gather the result * @@ -96,6 +109,54 @@ class table_with_names { std::vector const& right_on, cudf::null_equality compare_nulls = cudf::null_equality::EQUAL); +/** + * @brief Apply a left join operation to two tables + * + * @param left_input The left input table + * @param right_input The right input table + * @param left_on The columns to join on in the left table + * @param right_on The columns to join on in the right table + * @param compare_nulls The null equality policy + */ +[[nodiscard]] std::unique_ptr apply_left_join( + std::unique_ptr const& left_input, + std::unique_ptr const& right_input, + std::vector const& left_on, + std::vector const& right_on, + cudf::null_equality compare_nulls = cudf::null_equality::EQUAL); + +/** + * @brief Apply a left semi join operation to two tables + * + * @param left_input The left input table + * @param right_input The right input table + * @param left_on The columns to join on in the left table + * @param right_on The columns to join on in the right table + * @param compare_nulls The null equality policy + */ +[[nodiscard]] std::unique_ptr apply_left_semi_join( + std::unique_ptr const& left_input, + std::unique_ptr const& right_input, + std::vector const& left_on, + std::vector const& right_on, + cudf::null_equality compare_nulls = cudf::null_equality::EQUAL); + +/** + * @brief Apply a left anti join operation to two tables + * + * @param left_input The left input table + * @param right_input The right input table + * @param left_on The columns to join on in the left table + * @param right_on The columns to join on in the right table + * @param compare_nulls The null equality policy + */ +[[nodiscard]] std::unique_ptr apply_left_anti_join( + std::unique_ptr const& left_input, + std::unique_ptr const& right_input, + std::vector const& left_on, + std::vector const& right_on, + cudf::null_equality compare_nulls = cudf::null_equality::EQUAL); + /** * @brief Apply a filter predicate to a table * @@ -114,6 +175,24 @@ class table_with_names { [[nodiscard]] std::unique_ptr apply_mask( std::unique_ptr const& table, std::unique_ptr const& mask); +/** + * @brief Remove duplicate rows from a table + * + * @param table The input table + */ +[[nodiscard]] std::unique_ptr apply_distinct( + std::unique_ptr const& table); + +/** + * @brief Copy a range of rows from a table + * + * @param table The input table + * @param begin The first row to copy + * @param end One past the last row to copy + */ +[[nodiscard]] std::unique_ptr apply_slice( + std::unique_ptr const& table, cudf::size_type begin, cudf::size_type end); + /** * Struct representing group by key columns, value columns, and the type of aggregations to perform * on the value columns @@ -208,3 +287,7 @@ void write_to_parquet_device_buffer(std::unique_ptr const& table, void generate_parquet_data_sources(double scale_factor, std::vector const& table_names, std::unordered_map& sources); + +[[nodiscard]] bool write_ndsh_results(); + +void write_ndsh_result(table_with_names const& result, std::string const& query_name); diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q02.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q02.sql new file mode 100644 index 000000000000..56aee8e414c9 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q02.sql @@ -0,0 +1,44 @@ +select + s_acctbal, + s_name, + n_name, + p_partkey, + p_mfgr, + s_address, + s_phone, + s_comment +from + part, + supplier, + partsupp, + nation, + region +where + p_partkey = ps_partkey + and s_suppkey = ps_suppkey + and p_size = 15 + and p_type like '%BRASS' + and s_nationkey = n_nationkey + and n_regionkey = r_regionkey + and r_name = 'EUROPE' + and ps_supplycost = ( + select + min(ps_supplycost) + from + partsupp, + supplier, + nation, + region + where + p_partkey = ps_partkey + and s_suppkey = ps_suppkey + and s_nationkey = n_nationkey + and n_regionkey = r_regionkey + and r_name = 'EUROPE' + ) +order by + s_acctbal desc, + n_name, + s_name, + p_partkey +limit 100 diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q05.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q05.sql new file mode 100644 index 000000000000..3250d55f7503 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q05.sql @@ -0,0 +1,24 @@ +select + n_name, + sum(l_extendedprice * (1 - l_discount)) as revenue +from + customer, + orders, + lineitem, + supplier, + nation, + region +where + c_custkey = o_custkey + and l_orderkey = o_orderkey + and l_suppkey = s_suppkey + and c_nationkey = s_nationkey + and s_nationkey = n_nationkey + and n_regionkey = r_regionkey + and r_name = 'ASIA' + and o_orderdate >= date '1994-01-01' + and o_orderdate < date '1995-01-01' +group by + n_name +order by + revenue desc diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q06.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q06.sql new file mode 100644 index 000000000000..4db3531e9c40 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q06.sql @@ -0,0 +1,10 @@ +select + sum(l_extendedprice * l_discount) as revenue +from + lineitem +where + l_shipdate >= date '1994-01-01' + and l_shipdate < date '1995-01-01' + and l_discount >= 0.05 + and l_discount <= 0.07 + and l_quantity < 24 diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q07.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q07.sql new file mode 100644 index 000000000000..280bc51f7640 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q07.sql @@ -0,0 +1,39 @@ +select + supp_nation, + cust_nation, + l_year, + sum(volume) as revenue +from + ( + select + n1.n_name as supp_nation, + n2.n_name as cust_nation, + year(l_shipdate)::INT16 as l_year, + l_extendedprice * (1 - l_discount) as volume + from + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2 + where + s_suppkey = l_suppkey + and o_orderkey = l_orderkey + and c_custkey = o_custkey + and s_nationkey = n1.n_nationkey + and c_nationkey = n2.n_nationkey + and ( + (n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY') + or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE') + ) + and l_shipdate between date '1995-01-01' and date '1996-12-31' + ) as shipping +group by + supp_nation, + cust_nation, + l_year +order by + supp_nation, + cust_nation, + l_year diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q08.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q08.sql new file mode 100644 index 000000000000..6b675ebb4389 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q08.sql @@ -0,0 +1,39 @@ +select + o_year, + round( + sum(case + when nation = 'BRAZIL' then volume + else 0 + end) / sum(volume) + , 2) as mkt_share +from + ( + select + year(o_orderdate)::INT16 as o_year, + l_extendedprice * (1 - l_discount) as volume, + n2.n_name as nation + from + part, + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2, + region + where + p_partkey = l_partkey + and s_suppkey = l_suppkey + and l_orderkey = o_orderkey + and o_custkey = c_custkey + and c_nationkey = n1.n_nationkey + and n1.n_regionkey = r_regionkey + and r_name = 'AMERICA' + and s_nationkey = n2.n_nationkey + and o_orderdate between date '1995-01-01' and date '1996-12-31' + and p_type = 'ECONOMY ANODIZED STEEL' + ) as all_nations +group by + o_year +order by + o_year diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q10.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q10.sql new file mode 100644 index 000000000000..39c16df8c8f2 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q10.sql @@ -0,0 +1,32 @@ +select + c_custkey, + c_name, + sum(l_extendedprice * (1 - l_discount)) as revenue, + c_acctbal, + n_name, + c_address, + c_phone, + c_comment +from + customer, + orders, + lineitem, + nation +where + c_custkey = o_custkey + and l_orderkey = o_orderkey + and o_orderdate >= date '1993-10-01' + and o_orderdate < date '1994-01-01' + and l_returnflag = 'R' + and c_nationkey = n_nationkey +group by + c_custkey, + c_name, + c_acctbal, + c_phone, + n_name, + c_address, + c_comment +order by + revenue desc +limit 20 diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q11.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q11.sql new file mode 100644 index 000000000000..8e2f9699ae91 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q11.sql @@ -0,0 +1,28 @@ +select + ps_partkey, + round(sum(ps_supplycost * ps_availqty), 2) as value +from + partsupp, + supplier, + nation +where + ps_suppkey = s_suppkey + and s_nationkey = n_nationkey + and n_name = 'GERMANY' +group by + ps_partkey +having + sum(ps_supplycost * ps_availqty) > ( + select + sum(ps_supplycost * ps_availqty) * (0.0001 / $scale_factor) + from + partsupp, + supplier, + nation + where + ps_suppkey = s_suppkey + and s_nationkey = n_nationkey + and n_name = 'GERMANY' + ) +order by + value desc diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q12.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q12.sql new file mode 100644 index 000000000000..b1ac775cb673 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q12.sql @@ -0,0 +1,28 @@ +select + l_shipmode, + sum(case + when o_orderpriority = '1-URGENT' + or o_orderpriority = '2-HIGH' + then 1 + else 0 + end) as high_line_count, + sum(case + when o_orderpriority <> '1-URGENT' + and o_orderpriority <> '2-HIGH' + then 1 + else 0 + end) as low_line_count +from + orders, + lineitem +where + o_orderkey = l_orderkey + and l_shipmode in ('MAIL', 'SHIP') + and l_commitdate < l_receiptdate + and l_shipdate < l_commitdate + and l_receiptdate >= date '1994-01-01' + and l_receiptdate < date '1995-01-01' +group by + l_shipmode +order by + l_shipmode diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q13.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q13.sql new file mode 100644 index 000000000000..785a1588240b --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q13.sql @@ -0,0 +1,20 @@ +select + c_count, + count(*) as custdist +from + ( + select + c_custkey, + count(o_orderkey) as c_count + from + customer left outer join orders on + c_custkey = o_custkey + and o_comment not like '%special%requests%' + group by + c_custkey + ) as c_orders +group by + c_count +order by + custdist desc, + c_count desc diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q14.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q14.sql new file mode 100644 index 000000000000..4ec565533069 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q14.sql @@ -0,0 +1,13 @@ +select + round(100.00 * sum(case + when p_type like 'PROMO%' + then l_extendedprice * (1 - l_discount) + else 0 + end) / sum(l_extendedprice * (1 - l_discount)), 2) as promo_revenue +from + lineitem, + part +where + l_partkey = p_partkey + and l_shipdate >= date '1995-09-01' + and l_shipdate < date '1995-10-01' diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q15.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q15.sql new file mode 100644 index 000000000000..cc1253fe67d7 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q15.sql @@ -0,0 +1,31 @@ +with revenue (supplier_no, total_revenue) as ( + select + l_suppkey, + sum(l_extendedprice * (1 - l_discount)) + from + lineitem + where + l_shipdate >= date '1996-01-01' + and l_shipdate < date '1996-04-01' + group by + l_suppkey +) +select + s_suppkey, + s_name, + s_address, + s_phone, + total_revenue +from + supplier, + revenue +where + s_suppkey = supplier_no + and total_revenue = ( + select + max(total_revenue) + from + revenue + ) +order by + s_suppkey diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q16.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q16.sql new file mode 100644 index 000000000000..aa736b2c2062 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q16.sql @@ -0,0 +1,30 @@ +select + p_brand, + p_type, + p_size, + count(distinct ps_suppkey) as supplier_cnt +from + partsupp, + part +where + p_partkey = ps_partkey + and p_brand <> 'Brand#45' + and p_type not like 'MEDIUM POLISHED%' + and p_size in (49, 14, 23, 45, 19, 3, 36, 9) + and ps_suppkey not in ( + select + s_suppkey + from + supplier + where + s_comment like '%Customer%Complaints%' + ) +group by + p_brand, + p_type, + p_size +order by + supplier_cnt desc, + p_brand, + p_type, + p_size diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q19.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q19.sql new file mode 100644 index 000000000000..21e8e49c55b6 --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q19.sql @@ -0,0 +1,35 @@ +select + round(sum(l_extendedprice * (1 - l_discount)), 2) 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 between 1 and 11 + 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 between 10 and 20 + 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 between 20 and 30 + and p_size between 1 and 15 + and l_shipmode in ('AIR', 'AIR REG') + and l_shipinstruct = 'DELIVER IN PERSON' + ) diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q20.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q20.sql new file mode 100644 index 000000000000..9a6081f08f5e --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q20.sql @@ -0,0 +1,37 @@ +select + s_name, + s_address +from + supplier, + nation +where + s_suppkey in ( + select + ps_suppkey + from + partsupp + where + ps_partkey in ( + select + p_partkey + from + part + where + p_name like 'forest%' + ) + and ps_availqty > ( + select + 0.5 * sum(l_quantity) + from + lineitem + where + l_partkey = ps_partkey + and l_suppkey = ps_suppkey + and l_shipdate >= date '1994-01-01' + and l_shipdate < date '1995-01-01' + ) + ) + and s_nationkey = n_nationkey + and n_name = 'CANADA' +order by + s_name diff --git a/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q22.sql b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q22.sql new file mode 100644 index 000000000000..0bc6725fa5fb --- /dev/null +++ b/cpp/libcudf_streaming/benchmarks/streaming/ndsh/sql/q22.sql @@ -0,0 +1,37 @@ +select + cntrycode, + count(*) as numcust, + sum(c_acctbal) as totacctbal +from + ( + select + substring(c_phone from 1 for 2) as cntrycode, + c_acctbal + from + customer + where + substring(c_phone from 1 for 2) in + ('13', '31', '23', '29', '30', '18', '17') + and c_acctbal > ( + select + avg(c_acctbal) + from + customer + where + c_acctbal > 0.00 + and substring(c_phone from 1 for 2) in + ('13', '31', '23', '29', '30', '18', '17') + ) + and not exists ( + select + * + from + orders + where + o_custkey = c_custkey + ) + ) as custsale +group by + cntrycode +order by + cntrycode diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 06c0462ebed9..c637b4a1aa98 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -863,8 +863,3 @@ ConfigureTest( ROARING_DISABLE_AVX=1 CROARING_COMPILER_SUPPORTS_AVX512=0 ) - -# ################################################################################################## -# Install tests #################################################################################### -# ################################################################################################## -rapids_test_install_relocatable(INSTALL_COMPONENT_SET testing DESTINATION bin/gtests/libcudf) diff --git a/dependencies.yaml b/dependencies.yaml index 35044134c583..37933f6f6ed2 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1152,6 +1152,7 @@ dependencies: packages: - *cmake_ver - cuda-sanitizer-api + - duckdb # packages we want in the 'test_cpp' group in 'files', for CI, but which # shouldn't be added to 'all' for building a development environment test_cpp_cudf: