Coverage Report

Created: 2026-08-06 16:42

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
59
struct DecoderScenario {
60
    Encoding encoding;
61
    ValueType value_type;
62
};
63
64
struct ReaderScenario {
65
    ReaderOperation operation;
66
    Encoding encoding;
67
    int null_percent;
68
    Pattern null_pattern;
69
    int selectivity_percent;
70
    Projection projection;
71
    int schema_width;
72
    int predicate_position;
73
    ValueType value_type = ValueType::INT32;
74
};
75
76
struct KernelScenario {
77
    Kernel kernel;
78
    ValueType value_type;
79
    int selectivity_percent;
80
    int null_percent;
81
    Pattern pattern;
82
    size_t dictionary_entries;
83
    NestedSelectionImplementation nested_implementation = NestedSelectionImplementation::FUSED;
84
};
85
86
struct SelectionScenario {
87
    SelectionOperation operation;
88
    int selectivity_percent;
89
    Pattern pattern;
90
};
91
92
struct SelectionRange {
93
    size_t first;
94
    size_t count;
95
};
96
97
struct SelectionPlan {
98
    size_t total_rows = 0;
99
    size_t selected_rows = 0;
100
    std::vector<SelectionRange> ranges;
101
};
102
103
1
inline std::vector<DecoderScenario> decoder_scenarios() {
104
1
    return {
105
1
            {Encoding::PLAIN, ValueType::INT32},
106
1
            {Encoding::PLAIN, ValueType::INT64},
107
1
            {Encoding::PLAIN, ValueType::FLOAT},
108
1
            {Encoding::PLAIN, ValueType::DOUBLE},
109
1
            {Encoding::PLAIN, ValueType::BYTE_ARRAY},
110
1
            {Encoding::PLAIN, ValueType::FIXED_LEN_BYTE_ARRAY},
111
1
            {Encoding::DICTIONARY, ValueType::INT32},
112
1
            {Encoding::DICTIONARY, ValueType::INT64},
113
1
            {Encoding::DICTIONARY, ValueType::FLOAT},
114
1
            {Encoding::DICTIONARY, ValueType::DOUBLE},
115
1
            {Encoding::DICTIONARY, ValueType::BYTE_ARRAY},
116
1
            {Encoding::DICTIONARY, ValueType::FIXED_LEN_BYTE_ARRAY},
117
1
            {Encoding::BYTE_STREAM_SPLIT, ValueType::FLOAT},
118
1
            {Encoding::BYTE_STREAM_SPLIT, ValueType::DOUBLE},
119
1
            {Encoding::BYTE_STREAM_SPLIT, ValueType::FIXED_LEN_BYTE_ARRAY},
120
1
            {Encoding::DELTA_BINARY_PACKED, ValueType::INT32},
121
1
            {Encoding::DELTA_BINARY_PACKED, ValueType::INT64},
122
1
            {Encoding::DELTA_LENGTH_BYTE_ARRAY, ValueType::BYTE_ARRAY},
123
1
            {Encoding::DELTA_BYTE_ARRAY, ValueType::BYTE_ARRAY},
124
1
    };
125
1
}
126
127
2
inline std::vector<KernelScenario> kernel_scenarios() {
128
2
    std::vector<KernelScenario> scenarios;
129
4
    for (const auto value_type : {ValueType::FLOAT, ValueType::DOUBLE}) {
130
4
        scenarios.push_back(
131
4
                {Kernel::BYTE_STREAM_SPLIT, value_type, 100, 0, Pattern::CLUSTERED, 256});
132
4
    }
133
4
    for (const auto value_type : {ValueType::INT32, ValueType::INT64}) {
134
4
        scenarios.push_back(
135
4
                {Kernel::DELTA_PREFIX_SUM, value_type, 100, 0, Pattern::CLUSTERED, 256});
136
4
    }
137
2
    for (const auto value_type :
138
8
         {ValueType::INT32, ValueType::INT64, ValueType::FLOAT, ValueType::DOUBLE}) {
139
24
        for (const size_t dictionary_entries : {32, 4096, 262144}) {
140
24
            scenarios.push_back({Kernel::DICTIONARY_GATHER, value_type, 100, 0, Pattern::CLUSTERED,
141
24
                                 dictionary_entries});
142
24
        }
143
40
        for (const int null_percent : {0, 1, 10, 50, 90}) {
144
80
            for (const auto pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
145
80
                scenarios.push_back(
146
80
                        {Kernel::NULLABLE_EXPAND, value_type, 100, null_percent, pattern, 256});
147
80
            }
148
40
        }
149
48
        for (const int selectivity : {0, 1, 10, 50, 90, 100}) {
150
48
            scenarios.push_back(
151
48
                    {Kernel::RAW_PREDICATE, value_type, selectivity, 0, Pattern::ALTERNATING, 256});
152
48
        }
153
8
    }
154
6
    for (const int selectivity : {1, 10, 50}) {
155
12
        for (const auto pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
156
12
            for (const auto implementation :
157
24
                 {NestedSelectionImplementation::LEGACY, NestedSelectionImplementation::FUSED}) {
158
24
                scenarios.push_back({Kernel::NESTED_SELECTION, ValueType::INT32, selectivity, 10,
159
24
                                     pattern, 256, implementation});
160
24
            }
161
12
        }
162
6
    }
163
2
    return scenarios;
164
2
}
165
166
1
inline std::vector<SelectionScenario> selection_scenarios() {
167
1
    std::vector<SelectionScenario> scenarios {
168
1
            {SelectionOperation::RESIZE_IDENTITY, 100, Pattern::CLUSTERED}};
169
1
    for (const auto operation :
170
2
         {SelectionOperation::ROW_FILTER, SelectionOperation::CASCADE_FILTER}) {
171
12
        for (const int selectivity : {0, 1, 10, 50, 90, 100}) {
172
24
            for (const auto pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
173
24
                scenarios.push_back({operation, selectivity, pattern});
174
24
            }
175
12
        }
176
2
    }
177
1
    return scenarios;
178
1
}
179
180
6
inline std::vector<ReaderScenario> reader_scenarios() {
181
6
    std::vector<ReaderScenario> scenarios;
182
6
    std::set<std::tuple<ReaderOperation, Encoding, int, Pattern, int, Projection, int, int,
183
6
                        ValueType>>
184
6
            seen;
185
1.04k
    const auto add = [&](ReaderScenario scenario) {
186
1.04k
        const auto key = std::make_tuple(
187
1.04k
                scenario.operation, scenario.encoding, scenario.null_percent, scenario.null_pattern,
188
1.04k
                scenario.selectivity_percent, scenario.projection, scenario.schema_width,
189
1.04k
                scenario.predicate_position, scenario.value_type);
190
1.04k
        if (seen.insert(key).second) {
191
1.00k
            scenarios.push_back(scenario);
192
1.00k
        }
193
1.04k
    };
194
195
6
    const ReaderScenario baseline {.operation = ReaderOperation::FULL_SCAN,
196
6
                                   .encoding = Encoding::PLAIN,
197
6
                                   .null_percent = 10,
198
6
                                   .null_pattern = Pattern::ALTERNATING,
199
6
                                   .selectivity_percent = 10,
200
6
                                   .projection = Projection::PREDICATE_PROJECTED,
201
6
                                   .schema_width = 32,
202
6
                                   .predicate_position = 0};
203
6
    for (const auto operation :
204
6
         {ReaderOperation::OPEN_TO_FIRST_BLOCK, ReaderOperation::FULL_SCAN,
205
6
          ReaderOperation::PREDICATE_SCAN, ReaderOperation::COMPLEX_RESIDUAL_SCAN,
206
36
          ReaderOperation::LIMIT_1, ReaderOperation::LIMIT_1000}) {
207
36
        auto scenario = baseline;
208
36
        scenario.operation = operation;
209
36
        add(scenario);
210
36
    }
211
6
    for (const auto encoding : {Encoding::PLAIN, Encoding::DICTIONARY, Encoding::BYTE_STREAM_SPLIT,
212
24
                                Encoding::DELTA_BINARY_PACKED}) {
213
24
        auto scenario = baseline;
214
24
        scenario.encoding = encoding;
215
24
        add(scenario);
216
24
        scenario.operation = ReaderOperation::PREDICATE_SCAN;
217
24
        add(scenario);
218
24
    }
219
12
    for (const auto encoding : {Encoding::BYTE_STREAM_SPLIT, Encoding::DELTA_BINARY_PACKED}) {
220
48
        for (const int selectivity : {1, 10, 50, 90}) {
221
48
            for (const auto projection :
222
96
                 {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
223
96
                auto scenario = baseline;
224
96
                scenario.operation = ReaderOperation::PREDICATE_SCAN;
225
96
                scenario.encoding = encoding;
226
96
                scenario.selectivity_percent = selectivity;
227
96
                scenario.projection = projection;
228
96
                add(scenario);
229
96
            }
230
48
        }
231
12
    }
232
24
    for (const int selectivity : {1, 10, 50, 90}) {
233
24
        for (const auto projection :
234
48
             {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
235
48
            auto scenario = baseline;
236
48
            scenario.operation = ReaderOperation::PREDICATE_SCAN;
237
48
            scenario.encoding = Encoding::DICTIONARY;
238
48
            scenario.selectivity_percent = selectivity;
239
48
            scenario.projection = projection;
240
48
            add(scenario);
241
48
        }
242
24
    }
243
12
    for (const auto value_type : {ValueType::INT64, ValueType::BYTE_ARRAY}) {
244
24
        for (const int selectivity : {10, 50}) {
245
24
            for (const auto projection :
246
48
                 {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
247
48
                auto scenario = baseline;
248
48
                scenario.operation = ReaderOperation::PREDICATE_SCAN;
249
48
                scenario.encoding = Encoding::DICTIONARY;
250
48
                scenario.selectivity_percent = selectivity;
251
48
                scenario.projection = projection;
252
48
                scenario.value_type = value_type;
253
48
                add(scenario);
254
48
            }
255
24
        }
256
12
    }
257
24
    for (const int width : {4, 32, 128, 512}) {
258
48
        for (const int predicate_position : {0, width - 1}) {
259
48
            auto scenario = baseline;
260
48
            scenario.operation = ReaderOperation::PREDICATE_SCAN;
261
48
            scenario.schema_width = width;
262
48
            scenario.predicate_position = predicate_position;
263
48
            add(scenario);
264
48
        }
265
24
    }
266
30
    for (const int null_percent : {0, 1, 10, 50, 90}) {
267
60
        for (const auto pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
268
360
            for (const int selectivity : {0, 1, 10, 50, 90, 100}) {
269
360
                for (const auto projection :
270
720
                     {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
271
720
                    auto scenario = baseline;
272
720
                    scenario.operation = ReaderOperation::PREDICATE_SCAN;
273
720
                    scenario.null_percent = null_percent;
274
720
                    scenario.null_pattern = pattern;
275
720
                    scenario.selectivity_percent = selectivity;
276
720
                    scenario.projection = projection;
277
720
                    add(scenario);
278
720
                }
279
360
            }
280
60
        }
281
30
    }
282
6
    return scenarios;
283
6
}
284
285
inline SelectionPlan make_selection_plan(size_t total_rows, int selectivity_percent,
286
4
                                         Pattern pattern) {
287
4
    SelectionPlan plan {.total_rows = total_rows, .selected_rows = 0, .ranges = {}};
288
4
    if (total_rows == 0 || selectivity_percent <= 0) {
289
1
        return plan;
290
1
    }
291
3
    if (selectivity_percent >= 100) {
292
1
        plan.selected_rows = total_rows;
293
1
        plan.ranges.push_back({.first = 0, .count = total_rows});
294
1
        return plan;
295
1
    }
296
2
    plan.selected_rows = total_rows * static_cast<size_t>(selectivity_percent) / 100;
297
2
    if (plan.selected_rows == 0) {
298
0
        plan.selected_rows = 1;
299
0
    }
300
2
    if (pattern == Pattern::CLUSTERED) {
301
1
        plan.ranges.push_back({.first = 0, .count = plan.selected_rows});
302
1
        return plan;
303
1
    }
304
305
    // Evenly spaced rows deliberately maximize the number of physical ranges. This is the
306
    // adversarial sparse shape that exposes per-run decoder and cursor overhead.
307
101
    for (size_t selected = 0; selected < plan.selected_rows; ++selected) {
308
100
        const size_t row = selected * total_rows / plan.selected_rows;
309
100
        if (!plan.ranges.empty() && plan.ranges.back().first + plan.ranges.back().count == row) {
310
0
            ++plan.ranges.back().count;
311
100
        } else {
312
100
            plan.ranges.push_back({.first = row, .count = 1});
313
100
        }
314
100
    }
315
1
    return plan;
316
2
}
317
318
template <typename Visitor>
319
1
inline void visit_selected_rows(const SelectionPlan& plan, Visitor visitor) {
320
3
    for (const auto& range : plan.ranges) {
321
8
        for (size_t offset = 0; offset < range.count; ++offset) {
322
5
            visitor(range.first + offset);
323
5
        }
324
3
    }
325
1
}
326
327
167
inline std::string to_string(Encoding value) {
328
167
    switch (value) {
329
132
    case Encoding::PLAIN:
330
132
        return "plain";
331
17
    case Encoding::DICTIONARY:
332
17
        return "dictionary";
333
9
    case Encoding::BYTE_STREAM_SPLIT:
334
9
        return "byte_stream_split";
335
9
    case Encoding::DELTA_BINARY_PACKED:
336
9
        return "delta_binary_packed";
337
0
    case Encoding::DELTA_LENGTH_BYTE_ARRAY:
338
0
        return "delta_length_byte_array";
339
0
    case Encoding::DELTA_BYTE_ARRAY:
340
0
        return "delta_byte_array";
341
167
    }
342
0
    return "unknown";
343
167
}
344
345
167
inline std::string to_string(ValueType value) {
346
167
    switch (value) {
347
159
    case ValueType::INT32:
348
159
        return "int32";
349
4
    case ValueType::INT64:
350
4
        return "int64";
351
0
    case ValueType::FLOAT:
352
0
        return "float";
353
0
    case ValueType::DOUBLE:
354
0
        return "double";
355
4
    case ValueType::BYTE_ARRAY:
356
4
        return "byte_array";
357
0
    case ValueType::FIXED_LEN_BYTE_ARRAY:
358
0
        return "fixed_len_byte_array";
359
167
    }
360
0
    return "unknown";
361
167
}
362
363
167
inline std::string to_string(Pattern value) {
364
167
    return value == Pattern::CLUSTERED ? "clustered" : "alternating";
365
167
}
366
367
167
inline std::string to_string(Projection value) {
368
167
    return value == Projection::PREDICATE_ONLY ? "predicate_only" : "predicate_projected";
369
167
}
370
371
0
inline std::string to_string(SelectionOperation value) {
372
0
    switch (value) {
373
0
    case SelectionOperation::RESIZE_IDENTITY:
374
0
        return "resize_identity";
375
0
    case SelectionOperation::ROW_FILTER:
376
0
        return "row_filter";
377
0
    case SelectionOperation::CASCADE_FILTER:
378
0
        return "cascade_filter";
379
0
    }
380
0
    return "unknown";
381
0
}
382
383
167
inline std::string to_string(ReaderOperation value) {
384
167
    switch (value) {
385
1
    case ReaderOperation::OPEN_TO_FIRST_BLOCK:
386
1
        return "open_to_first_block";
387
4
    case ReaderOperation::FULL_SCAN:
388
4
        return "full_scan";
389
159
    case ReaderOperation::PREDICATE_SCAN:
390
159
        return "predicate_scan";
391
1
    case ReaderOperation::COMPLEX_RESIDUAL_SCAN:
392
1
        return "complex_residual_scan";
393
1
    case ReaderOperation::LIMIT_1:
394
1
        return "limit_1";
395
1
    case ReaderOperation::LIMIT_1000:
396
1
        return "limit_1000";
397
167
    }
398
0
    return "unknown";
399
167
}
400
401
167
inline std::string reader_scenario_name(const ReaderScenario& scenario) {
402
167
    return to_string(scenario.operation) + "/" + to_string(scenario.encoding) + "/" +
403
167
           to_string(scenario.value_type) + "/null_" + std::to_string(scenario.null_percent) + "/" +
404
167
           to_string(scenario.null_pattern) + "/sel_" +
405
167
           std::to_string(scenario.selectivity_percent) + "/" + to_string(scenario.projection) +
406
167
           "/width_" + std::to_string(scenario.schema_width) + "/predicate_" +
407
167
           std::to_string(scenario.predicate_position);
408
167
}
409
410
0
inline std::string to_string(Kernel value) {
411
0
    switch (value) {
412
0
    case Kernel::BYTE_STREAM_SPLIT:
413
0
        return "byte_stream_split";
414
0
    case Kernel::DELTA_PREFIX_SUM:
415
0
        return "delta_prefix_sum";
416
0
    case Kernel::DICTIONARY_GATHER:
417
0
        return "dictionary_gather";
418
0
    case Kernel::NULLABLE_EXPAND:
419
0
        return "nullable_expand";
420
0
    case Kernel::RAW_PREDICATE:
421
0
        return "raw_predicate";
422
0
    case Kernel::NESTED_SELECTION:
423
0
        return "nested_selection";
424
0
    }
425
0
    return "unknown";
426
0
}
427
428
0
inline std::string to_string(NestedSelectionImplementation value) {
429
0
    switch (value) {
430
0
    case NestedSelectionImplementation::LEGACY:
431
0
        return "legacy";
432
0
    case NestedSelectionImplementation::FUSED:
433
0
        return "fused";
434
0
    }
435
0
    return "unknown";
436
0
}
437
438
} // namespace doris::parquet_benchmark