Coverage Report

Created: 2026-08-07 00:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/benchmark/parquet/parquet_benchmark_scenarios.h
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#pragma once
19
20
#include <cstddef>
21
#include <cstdint>
22
#include <set>
23
#include <string>
24
#include <tuple>
25
#include <vector>
26
27
namespace doris::parquet_benchmark {
28
29
enum class Encoding {
30
    PLAIN,
31
    DICTIONARY,
32
    BYTE_STREAM_SPLIT,
33
    DELTA_BINARY_PACKED,
34
    DELTA_LENGTH_BYTE_ARRAY,
35
    DELTA_BYTE_ARRAY
36
};
37
enum class ValueType { INT32, INT64, FLOAT, DOUBLE, BYTE_ARRAY, FIXED_LEN_BYTE_ARRAY };
38
enum class Pattern { CLUSTERED, ALTERNATING };
39
enum class Projection { PREDICATE_ONLY, PREDICATE_PROJECTED };
40
enum class SelectionOperation { RESIZE_IDENTITY, ROW_FILTER, CASCADE_FILTER };
41
enum class ReaderOperation {
42
    OPEN_TO_FIRST_BLOCK,
43
    FULL_SCAN,
44
    PREDICATE_SCAN,
45
    COMPLEX_RESIDUAL_SCAN,
46
    LIMIT_1,
47
    LIMIT_1000
48
};
49
enum class Kernel {
50
    BYTE_STREAM_SPLIT,
51
    DELTA_PREFIX_SUM,
52
    DICTIONARY_GATHER,
53
    NULLABLE_EXPAND,
54
    RAW_PREDICATE,
55
    NESTED_SELECTION
56
};
57
enum class NestedSelectionImplementation { LEGACY, FUSED };
58
enum class NullableSelectionImplementation { LEGACY, FUSED };
59
60
struct DecoderScenario {
61
    Encoding encoding;
62
    ValueType value_type;
63
};
64
65
struct ReaderScenario {
66
    ReaderOperation operation;
67
    Encoding encoding;
68
    int null_percent;
69
    Pattern null_pattern;
70
    int selectivity_percent;
71
    Projection projection;
72
    int schema_width;
73
    int predicate_position;
74
    ValueType value_type = ValueType::INT32;
75
};
76
77
struct KernelScenario {
78
    Kernel kernel;
79
    ValueType value_type;
80
    int selectivity_percent;
81
    int null_percent;
82
    Pattern pattern;
83
    size_t dictionary_entries;
84
    NestedSelectionImplementation nested_implementation = NestedSelectionImplementation::FUSED;
85
};
86
87
struct SelectionScenario {
88
    SelectionOperation operation;
89
    int selectivity_percent;
90
    Pattern pattern;
91
};
92
93
struct NullableSelectionScenario {
94
    int selectivity_percent;
95
    int null_percent;
96
    Pattern selection_pattern;
97
    Pattern null_pattern;
98
    NullableSelectionImplementation implementation;
99
};
100
101
struct SelectionRange {
102
    size_t first;
103
    size_t count;
104
};
105
106
struct SelectionPlan {
107
    size_t total_rows = 0;
108
    size_t selected_rows = 0;
109
    std::vector<SelectionRange> ranges;
110
};
111
112
1
inline std::vector<DecoderScenario> decoder_scenarios() {
113
1
    return {
114
1
            {Encoding::PLAIN, ValueType::INT32},
115
1
            {Encoding::PLAIN, ValueType::INT64},
116
1
            {Encoding::PLAIN, ValueType::FLOAT},
117
1
            {Encoding::PLAIN, ValueType::DOUBLE},
118
1
            {Encoding::PLAIN, ValueType::BYTE_ARRAY},
119
1
            {Encoding::PLAIN, ValueType::FIXED_LEN_BYTE_ARRAY},
120
1
            {Encoding::DICTIONARY, ValueType::INT32},
121
1
            {Encoding::DICTIONARY, ValueType::INT64},
122
1
            {Encoding::DICTIONARY, ValueType::FLOAT},
123
1
            {Encoding::DICTIONARY, ValueType::DOUBLE},
124
1
            {Encoding::DICTIONARY, ValueType::BYTE_ARRAY},
125
1
            {Encoding::DICTIONARY, ValueType::FIXED_LEN_BYTE_ARRAY},
126
1
            {Encoding::BYTE_STREAM_SPLIT, ValueType::FLOAT},
127
1
            {Encoding::BYTE_STREAM_SPLIT, ValueType::DOUBLE},
128
1
            {Encoding::BYTE_STREAM_SPLIT, ValueType::FIXED_LEN_BYTE_ARRAY},
129
1
            {Encoding::DELTA_BINARY_PACKED, ValueType::INT32},
130
1
            {Encoding::DELTA_BINARY_PACKED, ValueType::INT64},
131
1
            {Encoding::DELTA_LENGTH_BYTE_ARRAY, ValueType::BYTE_ARRAY},
132
1
            {Encoding::DELTA_BYTE_ARRAY, ValueType::BYTE_ARRAY},
133
1
    };
134
1
}
135
136
2
inline std::vector<KernelScenario> kernel_scenarios() {
137
2
    std::vector<KernelScenario> scenarios;
138
4
    for (const auto value_type : {ValueType::FLOAT, ValueType::DOUBLE}) {
139
4
        scenarios.push_back(
140
4
                {Kernel::BYTE_STREAM_SPLIT, value_type, 100, 0, Pattern::CLUSTERED, 256});
141
4
    }
142
4
    for (const auto value_type : {ValueType::INT32, ValueType::INT64}) {
143
4
        scenarios.push_back(
144
4
                {Kernel::DELTA_PREFIX_SUM, value_type, 100, 0, Pattern::CLUSTERED, 256});
145
4
    }
146
2
    for (const auto value_type :
147
8
         {ValueType::INT32, ValueType::INT64, ValueType::FLOAT, ValueType::DOUBLE}) {
148
24
        for (const size_t dictionary_entries : {32, 4096, 262144}) {
149
24
            scenarios.push_back({Kernel::DICTIONARY_GATHER, value_type, 100, 0, Pattern::CLUSTERED,
150
24
                                 dictionary_entries});
151
24
        }
152
40
        for (const int null_percent : {0, 1, 10, 50, 90}) {
153
80
            for (const auto pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
154
80
                scenarios.push_back(
155
80
                        {Kernel::NULLABLE_EXPAND, value_type, 100, null_percent, pattern, 256});
156
80
            }
157
40
        }
158
48
        for (const int selectivity : {0, 1, 10, 50, 90, 100}) {
159
48
            scenarios.push_back(
160
48
                    {Kernel::RAW_PREDICATE, value_type, selectivity, 0, Pattern::ALTERNATING, 256});
161
48
        }
162
8
    }
163
6
    for (const int selectivity : {1, 10, 50}) {
164
12
        for (const auto pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
165
12
            for (const auto implementation :
166
24
                 {NestedSelectionImplementation::LEGACY, NestedSelectionImplementation::FUSED}) {
167
24
                scenarios.push_back({Kernel::NESTED_SELECTION, ValueType::INT32, selectivity, 10,
168
24
                                     pattern, 256, implementation});
169
24
            }
170
12
        }
171
6
    }
172
2
    return scenarios;
173
2
}
174
175
1
inline std::vector<SelectionScenario> selection_scenarios() {
176
1
    std::vector<SelectionScenario> scenarios {
177
1
            {SelectionOperation::RESIZE_IDENTITY, 100, Pattern::CLUSTERED}};
178
1
    for (const auto operation :
179
2
         {SelectionOperation::ROW_FILTER, SelectionOperation::CASCADE_FILTER}) {
180
12
        for (const int selectivity : {0, 1, 10, 50, 90, 100}) {
181
24
            for (const auto pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
182
24
                scenarios.push_back({operation, selectivity, pattern});
183
24
            }
184
12
        }
185
2
    }
186
1
    return scenarios;
187
1
}
188
189
1
inline std::vector<NullableSelectionScenario> nullable_selection_scenarios() {
190
1
    std::vector<NullableSelectionScenario> scenarios;
191
5
    for (const int selectivity : {1, 10, 50, 90, 99}) {
192
25
        for (const int null_percent : {0, 1, 10, 50, 90}) {
193
50
            for (const auto selection_pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
194
100
                for (const auto null_pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
195
100
                    for (const auto implementation : {NullableSelectionImplementation::LEGACY,
196
200
                                                      NullableSelectionImplementation::FUSED}) {
197
200
                        scenarios.push_back({selectivity, null_percent, selection_pattern,
198
200
                                             null_pattern, implementation});
199
200
                    }
200
100
                }
201
50
            }
202
25
        }
203
5
    }
204
1
    return scenarios;
205
1
}
206
207
6
inline std::vector<ReaderScenario> reader_scenarios() {
208
6
    std::vector<ReaderScenario> scenarios;
209
6
    std::set<std::tuple<ReaderOperation, Encoding, int, Pattern, int, Projection, int, int,
210
6
                        ValueType>>
211
6
            seen;
212
1.04k
    const auto add = [&](ReaderScenario scenario) {
213
1.04k
        const auto key = std::make_tuple(
214
1.04k
                scenario.operation, scenario.encoding, scenario.null_percent, scenario.null_pattern,
215
1.04k
                scenario.selectivity_percent, scenario.projection, scenario.schema_width,
216
1.04k
                scenario.predicate_position, scenario.value_type);
217
1.04k
        if (seen.insert(key).second) {
218
1.00k
            scenarios.push_back(scenario);
219
1.00k
        }
220
1.04k
    };
221
222
6
    const ReaderScenario baseline {.operation = ReaderOperation::FULL_SCAN,
223
6
                                   .encoding = Encoding::PLAIN,
224
6
                                   .null_percent = 10,
225
6
                                   .null_pattern = Pattern::ALTERNATING,
226
6
                                   .selectivity_percent = 10,
227
6
                                   .projection = Projection::PREDICATE_PROJECTED,
228
6
                                   .schema_width = 32,
229
6
                                   .predicate_position = 0};
230
6
    for (const auto operation :
231
6
         {ReaderOperation::OPEN_TO_FIRST_BLOCK, ReaderOperation::FULL_SCAN,
232
6
          ReaderOperation::PREDICATE_SCAN, ReaderOperation::COMPLEX_RESIDUAL_SCAN,
233
36
          ReaderOperation::LIMIT_1, ReaderOperation::LIMIT_1000}) {
234
36
        auto scenario = baseline;
235
36
        scenario.operation = operation;
236
36
        add(scenario);
237
36
    }
238
6
    for (const auto encoding : {Encoding::PLAIN, Encoding::DICTIONARY, Encoding::BYTE_STREAM_SPLIT,
239
24
                                Encoding::DELTA_BINARY_PACKED}) {
240
24
        auto scenario = baseline;
241
24
        scenario.encoding = encoding;
242
24
        add(scenario);
243
24
        scenario.operation = ReaderOperation::PREDICATE_SCAN;
244
24
        add(scenario);
245
24
    }
246
12
    for (const auto encoding : {Encoding::BYTE_STREAM_SPLIT, Encoding::DELTA_BINARY_PACKED}) {
247
48
        for (const int selectivity : {1, 10, 50, 90}) {
248
48
            for (const auto projection :
249
96
                 {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
250
96
                auto scenario = baseline;
251
96
                scenario.operation = ReaderOperation::PREDICATE_SCAN;
252
96
                scenario.encoding = encoding;
253
96
                scenario.selectivity_percent = selectivity;
254
96
                scenario.projection = projection;
255
96
                add(scenario);
256
96
            }
257
48
        }
258
12
    }
259
24
    for (const int selectivity : {1, 10, 50, 90}) {
260
24
        for (const auto projection :
261
48
             {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
262
48
            auto scenario = baseline;
263
48
            scenario.operation = ReaderOperation::PREDICATE_SCAN;
264
48
            scenario.encoding = Encoding::DICTIONARY;
265
48
            scenario.selectivity_percent = selectivity;
266
48
            scenario.projection = projection;
267
48
            add(scenario);
268
48
        }
269
24
    }
270
12
    for (const auto value_type : {ValueType::INT64, ValueType::BYTE_ARRAY}) {
271
24
        for (const int selectivity : {10, 50}) {
272
24
            for (const auto projection :
273
48
                 {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
274
48
                auto scenario = baseline;
275
48
                scenario.operation = ReaderOperation::PREDICATE_SCAN;
276
48
                scenario.encoding = Encoding::DICTIONARY;
277
48
                scenario.selectivity_percent = selectivity;
278
48
                scenario.projection = projection;
279
48
                scenario.value_type = value_type;
280
48
                add(scenario);
281
48
            }
282
24
        }
283
12
    }
284
24
    for (const int width : {4, 32, 128, 512}) {
285
48
        for (const int predicate_position : {0, width - 1}) {
286
48
            auto scenario = baseline;
287
48
            scenario.operation = ReaderOperation::PREDICATE_SCAN;
288
48
            scenario.schema_width = width;
289
48
            scenario.predicate_position = predicate_position;
290
48
            add(scenario);
291
48
        }
292
24
    }
293
30
    for (const int null_percent : {0, 1, 10, 50, 90}) {
294
60
        for (const auto pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
295
360
            for (const int selectivity : {0, 1, 10, 50, 90, 100}) {
296
360
                for (const auto projection :
297
720
                     {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
298
720
                    auto scenario = baseline;
299
720
                    scenario.operation = ReaderOperation::PREDICATE_SCAN;
300
720
                    scenario.null_percent = null_percent;
301
720
                    scenario.null_pattern = pattern;
302
720
                    scenario.selectivity_percent = selectivity;
303
720
                    scenario.projection = projection;
304
720
                    add(scenario);
305
720
                }
306
360
            }
307
60
        }
308
30
    }
309
6
    return scenarios;
310
6
}
311
312
inline SelectionPlan make_selection_plan(size_t total_rows, int selectivity_percent,
313
4
                                         Pattern pattern) {
314
4
    SelectionPlan plan {.total_rows = total_rows, .selected_rows = 0, .ranges = {}};
315
4
    if (total_rows == 0 || selectivity_percent <= 0) {
316
1
        return plan;
317
1
    }
318
3
    if (selectivity_percent >= 100) {
319
1
        plan.selected_rows = total_rows;
320
1
        plan.ranges.push_back({.first = 0, .count = total_rows});
321
1
        return plan;
322
1
    }
323
2
    plan.selected_rows = total_rows * static_cast<size_t>(selectivity_percent) / 100;
324
2
    if (plan.selected_rows == 0) {
325
0
        plan.selected_rows = 1;
326
0
    }
327
2
    if (pattern == Pattern::CLUSTERED) {
328
1
        plan.ranges.push_back({.first = 0, .count = plan.selected_rows});
329
1
        return plan;
330
1
    }
331
332
    // Evenly spaced rows deliberately maximize the number of physical ranges. This is the
333
    // adversarial sparse shape that exposes per-run decoder and cursor overhead.
334
101
    for (size_t selected = 0; selected < plan.selected_rows; ++selected) {
335
100
        const size_t row = selected * total_rows / plan.selected_rows;
336
100
        if (!plan.ranges.empty() && plan.ranges.back().first + plan.ranges.back().count == row) {
337
0
            ++plan.ranges.back().count;
338
100
        } else {
339
100
            plan.ranges.push_back({.first = row, .count = 1});
340
100
        }
341
100
    }
342
1
    return plan;
343
2
}
344
345
template <typename Visitor>
346
1
inline void visit_selected_rows(const SelectionPlan& plan, Visitor visitor) {
347
3
    for (const auto& range : plan.ranges) {
348
8
        for (size_t offset = 0; offset < range.count; ++offset) {
349
5
            visitor(range.first + offset);
350
5
        }
351
3
    }
352
1
}
353
354
167
inline std::string to_string(Encoding value) {
355
167
    switch (value) {
356
132
    case Encoding::PLAIN:
357
132
        return "plain";
358
17
    case Encoding::DICTIONARY:
359
17
        return "dictionary";
360
9
    case Encoding::BYTE_STREAM_SPLIT:
361
9
        return "byte_stream_split";
362
9
    case Encoding::DELTA_BINARY_PACKED:
363
9
        return "delta_binary_packed";
364
0
    case Encoding::DELTA_LENGTH_BYTE_ARRAY:
365
0
        return "delta_length_byte_array";
366
0
    case Encoding::DELTA_BYTE_ARRAY:
367
0
        return "delta_byte_array";
368
167
    }
369
0
    return "unknown";
370
167
}
371
372
167
inline std::string to_string(ValueType value) {
373
167
    switch (value) {
374
159
    case ValueType::INT32:
375
159
        return "int32";
376
4
    case ValueType::INT64:
377
4
        return "int64";
378
0
    case ValueType::FLOAT:
379
0
        return "float";
380
0
    case ValueType::DOUBLE:
381
0
        return "double";
382
4
    case ValueType::BYTE_ARRAY:
383
4
        return "byte_array";
384
0
    case ValueType::FIXED_LEN_BYTE_ARRAY:
385
0
        return "fixed_len_byte_array";
386
167
    }
387
0
    return "unknown";
388
167
}
389
390
167
inline std::string to_string(Pattern value) {
391
167
    return value == Pattern::CLUSTERED ? "clustered" : "alternating";
392
167
}
393
394
167
inline std::string to_string(Projection value) {
395
167
    return value == Projection::PREDICATE_ONLY ? "predicate_only" : "predicate_projected";
396
167
}
397
398
0
inline std::string to_string(SelectionOperation value) {
399
0
    switch (value) {
400
0
    case SelectionOperation::RESIZE_IDENTITY:
401
0
        return "resize_identity";
402
0
    case SelectionOperation::ROW_FILTER:
403
0
        return "row_filter";
404
0
    case SelectionOperation::CASCADE_FILTER:
405
0
        return "cascade_filter";
406
0
    }
407
0
    return "unknown";
408
0
}
409
410
167
inline std::string to_string(ReaderOperation value) {
411
167
    switch (value) {
412
1
    case ReaderOperation::OPEN_TO_FIRST_BLOCK:
413
1
        return "open_to_first_block";
414
4
    case ReaderOperation::FULL_SCAN:
415
4
        return "full_scan";
416
159
    case ReaderOperation::PREDICATE_SCAN:
417
159
        return "predicate_scan";
418
1
    case ReaderOperation::COMPLEX_RESIDUAL_SCAN:
419
1
        return "complex_residual_scan";
420
1
    case ReaderOperation::LIMIT_1:
421
1
        return "limit_1";
422
1
    case ReaderOperation::LIMIT_1000:
423
1
        return "limit_1000";
424
167
    }
425
0
    return "unknown";
426
167
}
427
428
167
inline std::string reader_scenario_name(const ReaderScenario& scenario) {
429
167
    return to_string(scenario.operation) + "/" + to_string(scenario.encoding) + "/" +
430
167
           to_string(scenario.value_type) + "/null_" + std::to_string(scenario.null_percent) + "/" +
431
167
           to_string(scenario.null_pattern) + "/sel_" +
432
167
           std::to_string(scenario.selectivity_percent) + "/" + to_string(scenario.projection) +
433
167
           "/width_" + std::to_string(scenario.schema_width) + "/predicate_" +
434
167
           std::to_string(scenario.predicate_position);
435
167
}
436
437
0
inline std::string to_string(Kernel value) {
438
0
    switch (value) {
439
0
    case Kernel::BYTE_STREAM_SPLIT:
440
0
        return "byte_stream_split";
441
0
    case Kernel::DELTA_PREFIX_SUM:
442
0
        return "delta_prefix_sum";
443
0
    case Kernel::DICTIONARY_GATHER:
444
0
        return "dictionary_gather";
445
0
    case Kernel::NULLABLE_EXPAND:
446
0
        return "nullable_expand";
447
0
    case Kernel::RAW_PREDICATE:
448
0
        return "raw_predicate";
449
0
    case Kernel::NESTED_SELECTION:
450
0
        return "nested_selection";
451
0
    }
452
0
    return "unknown";
453
0
}
454
455
0
inline std::string to_string(NestedSelectionImplementation value) {
456
0
    switch (value) {
457
0
    case NestedSelectionImplementation::LEGACY:
458
0
        return "legacy";
459
0
    case NestedSelectionImplementation::FUSED:
460
0
        return "fused";
461
0
    }
462
0
    return "unknown";
463
0
}
464
465
0
inline std::string to_string(NullableSelectionImplementation value) {
466
0
    switch (value) {
467
0
    case NullableSelectionImplementation::LEGACY:
468
0
        return "legacy";
469
0
    case NullableSelectionImplementation::FUSED:
470
0
        return "fused";
471
0
    }
472
0
    return "unknown";
473
0
}
474
475
} // namespace doris::parquet_benchmark