1313#include < cudf/io/parquet.hpp>
1414#include < cudf/io/parquet_io_utils.hpp>
1515#include < cudf/io/text/byte_range_info.hpp>
16+ #include < cudf/scalar/scalar.hpp>
1617#include < cudf/utilities/default_stream.hpp>
18+ #include < cudf/utilities/traits.hpp>
19+ #include < cudf/utilities/type_dispatcher.hpp>
20+ #include < cudf/wrappers/durations.hpp>
1721
1822#include < nvbench/nvbench.cuh>
1923
24+ #include < algorithm>
25+ #include < limits>
2026#include < numeric>
2127
28+ namespace {
29+
2230constexpr cudf::size_type num_cols = 8 ;
2331
24- void BM_filter_string_row_groups_with_dicts_common (nvbench::state& state,
25- data_profile const & table_profile,
26- cudf::ast::operation const & filter_expr,
27- double average_str_length,
28- cudf::size_type cardinality)
32+ void filter_row_groups_with_dicts_common (nvbench::state& state,
33+ cudf::type_id dtype,
34+ data_profile const & table_profile,
35+ cudf::ast::literal const & literal)
2936{
3037 auto const num_row_groups = static_cast <cudf::size_type>(state.get_int64 (" num_row_groups" ));
38+ auto const cardinality = static_cast <cudf::size_type>(state.get_int64 (" cardinality" ));
39+ auto const is_inline_eval = static_cast <bool >(state.get_int64 (" is_inline" ));
3140 auto constexpr rows_per_row_group = 5'000 ; // < Chosen such that it is not ignored by the writer
3241 auto const num_rows = num_row_groups * rows_per_row_group;
3342
3443 std::vector<char > parquet_buffer;
3544
3645 // Write table to parquet
3746 {
38- auto const table = create_random_table (
39- cycle_dtypes ({cudf::type_id:: STRING }, num_cols), row_count{num_rows}, table_profile);
47+ auto const table =
48+ create_random_table ( cycle_dtypes ({dtype }, num_cols), row_count{num_rows}, table_profile);
4049
4150 cudf::io::parquet_writer_options write_opts =
4251 cudf::io::parquet_writer_options::builder (cudf::io::sink_info (&parquet_buffer), table->view ())
@@ -47,6 +56,17 @@ void BM_filter_string_row_groups_with_dicts_common(nvbench::state& state,
4756 cudf::io::write_parquet (write_opts);
4857 }
4958
59+ auto col_ref = cudf::ast::column_name_reference (" _col0" );
60+ auto expr1 = cudf::ast::operation (cudf::ast::ast_operator::EQUAL , col_ref, literal);
61+ auto expr2 = cudf::ast::operation (cudf::ast::ast_operator::NOT_EQUAL , col_ref, literal);
62+ auto expr3 = cudf::ast::operation (cudf::ast::ast_operator::EQUAL , col_ref, literal);
63+
64+ auto filter_expr_few_literals =
65+ cudf::ast::operation (cudf::ast::ast_operator::LOGICAL_AND , expr1, expr2);
66+ auto filter_expr_many_literals =
67+ cudf::ast::operation (cudf::ast::ast_operator::LOGICAL_OR , filter_expr_few_literals, expr3);
68+ auto const & filter_expr = is_inline_eval ? filter_expr_few_literals : filter_expr_many_literals;
69+
5070 auto const stream = cudf::get_default_stream ();
5171 auto const read_opts = cudf::io::parquet_reader_options::builder ().filter (filter_expr).build ();
5272
@@ -76,6 +96,13 @@ void BM_filter_string_row_groups_with_dicts_common(nvbench::state& state,
7696 auto input_row_group_indices = reader->all_row_groups (read_opts);
7797 auto dict_page_byte_ranges = std::vector<cudf::io::text::byte_range_info>{};
7898
99+ // Upper bound on the dictionary entries. Real dictionaries are smaller because the generator
100+ // repeats each value it draws for a run of rows, and may draw the same value again
101+ auto const num_dict_entries =
102+ static_cast <std::size_t >(std::min (cardinality, rows_per_row_group) * num_row_groups);
103+
104+ state.add_element_count (num_dict_entries, " num_dict_entries" );
105+
79106 auto mem_stats_logger = cudf::memory_stats_logger ();
80107
81108 state.exec (
@@ -106,12 +133,8 @@ void BM_filter_string_row_groups_with_dicts_common(nvbench::state& state,
106133 timer.stop ();
107134 });
108135
109- auto const time = state.get_summary (" nv/cold/time/gpu/mean" ).get_float64 (" value" );
110136 state.add_buffer_size (
111137 mem_stats_logger.peak_memory_usage (), " peak_memory_usage" , " peak_memory_usage" );
112- state.add_element_count (
113- static_cast <double >(cardinality * num_row_groups * average_str_length) / time,
114- " strings_per_second" );
115138 auto const total_dict_data_size =
116139 std::accumulate (dict_page_byte_ranges.begin (),
117140 dict_page_byte_ranges.end (),
@@ -120,44 +143,86 @@ void BM_filter_string_row_groups_with_dicts_common(nvbench::state& state,
120143 state.add_buffer_size (total_dict_data_size, " total_dict_data_size" , " total_dict_data_size" );
121144}
122145
123- void BM_filter_string_rowgroups_with_dicts (nvbench::state& state)
146+ } // namespace
147+
148+ void BM_hybrid_scan_dict_page_pruning_string (nvbench::state& state)
124149{
125- auto const min_length = static_cast <cudf::size_type>(state.get_int64 (" min_length" ));
126- auto const max_length = static_cast <cudf::size_type>(state.get_int64 (" max_length" ));
127- auto const cardinality = static_cast <cudf::size_type>(state.get_int64 (" cardinality" ));
128- auto const is_inline_eval = static_cast <bool >(state.get_int64 (" is_inline" ));
150+ auto const min_length = static_cast <cudf::size_type>(state.get_int64 (" min_length" ));
151+ auto const max_length = static_cast <cudf::size_type>(state.get_int64 (" max_length" ));
152+ auto const cardinality = static_cast <cudf::size_type>(state.get_int64 (" cardinality" ));
129153
130154 auto table_profile =
131155 data_profile_builder ()
132156 .distribution (cudf::type_id::STRING , distribution_id::NORMAL , min_length, max_length)
133157 .cardinality (cardinality);
134158
135- auto col_ref = cudf::ast::column_name_reference (" _col0" );
136- auto scalar = cudf::string_scalar (" 000010000" );
137- auto literal = cudf::ast::literal (scalar);
138- auto expr1 = cudf::ast::operation (cudf::ast::ast_operator::EQUAL , col_ref, literal);
139- auto expr2 = cudf::ast::operation (cudf::ast::ast_operator::NOT_EQUAL , col_ref, literal);
140- auto expr3 = cudf::ast::operation (cudf::ast::ast_operator::EQUAL , col_ref, literal);
159+ auto filter_value = cudf::string_scalar (" 000010000" );
160+ filter_row_groups_with_dicts_common (
161+ state, cudf::type_id::STRING , table_profile, cudf::ast::literal (filter_value));
162+ }
141163
142- auto filter_expr_few_literals =
143- cudf::ast::operation (cudf::ast::ast_operator::LOGICAL_AND , expr1, expr2);
164+ template <cudf::type_id DType>
165+ void BM_hybrid_scan_dict_page_pruning_fixed_width (nvbench::state& state,
166+ nvbench::type_list<nvbench::enum_type<DType>>)
167+ {
168+ using T = cudf::id_to_type<DType>;
169+ static_assert (cudf::is_numeric<T>() or cudf::is_chrono<T>(),
170+ " Filter literals are only generated for numeric and chrono types" );
171+
172+ auto const cardinality = static_cast <cudf::size_type>(state.get_int64 (" cardinality" ));
173+
174+ // Timestamps span 1970 to 2020 as the generator overflows past 2262
175+ // Durations span 24 days to fit
176+ auto constexpr max_value = [] {
177+ if constexpr (cudf::is_timestamp<T>()) {
178+ return cuda::std::chrono::duration_cast<typename T::duration>(cudf::duration_D{50 * 365 })
179+ .count ();
180+ } else if constexpr (cudf::is_duration<T>()) {
181+ return cuda::std::chrono::duration_cast<T>(cudf::duration_D{24 }).count ();
182+ } else {
183+ return std::numeric_limits<T>::max ();
184+ }
185+ }();
144186
145- auto filter_expr_many_literals =
146- cudf::ast::operation (cudf::ast::ast_operator::LOGICAL_OR , filter_expr_few_literals, expr3);
187+ auto table_profile =
188+ data_profile_builder ()
189+ .distribution (DType, distribution_id::UNIFORM , decltype (max_value){0 }, max_value)
190+ .cardinality (cardinality);
147191
148- return BM_filter_string_row_groups_with_dicts_common (
149- state,
150- table_profile,
151- is_inline_eval ? filter_expr_few_literals : filter_expr_many_literals,
152- (static_cast <double >(min_length) + static_cast <double >(max_length)) / 2 ,
153- cardinality);
192+ // The literal lies inside the value range, so min/max statistics cannot prune any row group
193+ auto filter_value = [&] {
194+ if constexpr (cudf::is_timestamp<T>()) {
195+ return cudf::timestamp_scalar<T>(T{typename T::duration{max_value / 2 }});
196+ } else if constexpr (cudf::is_duration<T>()) {
197+ return cudf::duration_scalar<T>(T{max_value / 2 });
198+ } else {
199+ return cudf::numeric_scalar<T>(static_cast <T>(max_value / 2 ));
200+ }
201+ }();
202+ filter_row_groups_with_dicts_common (
203+ state, DType, table_profile, cudf::ast::literal (filter_value));
154204}
155205
156- NVBENCH_BENCH (BM_filter_string_rowgroups_with_dicts )
157- .set_name(" hybrid_scan_filter_string_rowgroups_with_dicts " )
206+ NVBENCH_BENCH (BM_hybrid_scan_dict_page_pruning_string )
207+ .set_name(" hybrid_scan_dict_page_pruning_string " )
158208 .set_min_samples(4 )
159209 .add_int64_axis(" num_row_groups" , {32 , 64 , 128 })
160210 .add_int64_axis(" min_length" , {4 })
161211 .add_int64_axis(" max_length" , {64 , 128 })
162212 .add_int64_axis(" cardinality" , {1'000 , 10'000 })
163213 .add_int64_axis(" is_inline" , {true , false });
214+
215+ using dict_fixed_width_dtypes = nvbench::enum_type_list<cudf::type_id::INT8 ,
216+ cudf::type_id::FLOAT32 ,
217+ cudf::type_id::FLOAT64 ,
218+ cudf::type_id::DURATION_MILLISECONDS ,
219+ cudf::type_id::TIMESTAMP_MICROSECONDS >;
220+
221+ NVBENCH_BENCH_TYPES (BM_hybrid_scan_dict_page_pruning_fixed_width,
222+ NVBENCH_TYPE_AXES (dict_fixed_width_dtypes))
223+ .set_name(" hybrid_scan_dict_page_pruning_fixed_width" )
224+ .set_type_axes_names({" dtype" })
225+ .set_min_samples(4 )
226+ .add_int64_axis(" num_row_groups" , {32 , 64 , 128 })
227+ .add_int64_axis(" cardinality" , {1'000 , 10'000 })
228+ .add_int64_axis(" is_inline" , {true , false });
0 commit comments