Coverage Report

Created: 2026-07-28 19:12

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