Coverage Report

Created: 2026-08-02 02:36

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