Coverage Report

Created: 2026-07-23 12:09

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 { OPEN_TO_FIRST_BLOCK, FULL_SCAN, PREDICATE_SCAN, LIMIT_1, LIMIT_1000 };
41
42
struct DecoderScenario {
43
    Encoding encoding;
44
    ValueType value_type;
45
};
46
47
struct ReaderScenario {
48
    ReaderOperation operation;
49
    Encoding encoding;
50
    int null_percent;
51
    Pattern null_pattern;
52
    int selectivity_percent;
53
    Projection projection;
54
    int schema_width;
55
    int predicate_position;
56
};
57
58
struct SelectionRange {
59
    size_t first;
60
    size_t count;
61
};
62
63
struct SelectionPlan {
64
    size_t total_rows = 0;
65
    size_t selected_rows = 0;
66
    std::vector<SelectionRange> ranges;
67
};
68
69
1
inline std::vector<DecoderScenario> decoder_scenarios() {
70
1
    return {
71
1
            {Encoding::PLAIN, ValueType::INT32},
72
1
            {Encoding::PLAIN, ValueType::INT64},
73
1
            {Encoding::PLAIN, ValueType::FLOAT},
74
1
            {Encoding::PLAIN, ValueType::DOUBLE},
75
1
            {Encoding::PLAIN, ValueType::BYTE_ARRAY},
76
1
            {Encoding::PLAIN, ValueType::FIXED_LEN_BYTE_ARRAY},
77
1
            {Encoding::DICTIONARY, ValueType::INT32},
78
1
            {Encoding::DICTIONARY, ValueType::INT64},
79
1
            {Encoding::DICTIONARY, ValueType::FLOAT},
80
1
            {Encoding::DICTIONARY, ValueType::DOUBLE},
81
1
            {Encoding::DICTIONARY, ValueType::BYTE_ARRAY},
82
1
            {Encoding::DICTIONARY, ValueType::FIXED_LEN_BYTE_ARRAY},
83
1
            {Encoding::BYTE_STREAM_SPLIT, ValueType::FLOAT},
84
1
            {Encoding::BYTE_STREAM_SPLIT, ValueType::DOUBLE},
85
1
            {Encoding::BYTE_STREAM_SPLIT, ValueType::FIXED_LEN_BYTE_ARRAY},
86
1
            {Encoding::DELTA_BINARY_PACKED, ValueType::INT32},
87
1
            {Encoding::DELTA_BINARY_PACKED, ValueType::INT64},
88
1
            {Encoding::DELTA_LENGTH_BYTE_ARRAY, ValueType::BYTE_ARRAY},
89
1
            {Encoding::DELTA_BYTE_ARRAY, ValueType::BYTE_ARRAY},
90
1
    };
91
1
}
92
93
3
inline std::vector<ReaderScenario> reader_scenarios() {
94
3
    std::vector<ReaderScenario> scenarios;
95
3
    std::set<std::tuple<ReaderOperation, Encoding, int, Pattern, int, Projection, int, int>> seen;
96
471
    const auto add = [&](ReaderScenario scenario) {
97
471
        const auto key = std::make_tuple(scenario.operation, scenario.encoding,
98
471
                                         scenario.null_percent, scenario.null_pattern,
99
471
                                         scenario.selectivity_percent, scenario.projection,
100
471
                                         scenario.schema_width, scenario.predicate_position);
101
471
        if (seen.insert(key).second) {
102
453
            scenarios.push_back(scenario);
103
453
        }
104
471
    };
105
106
3
    const ReaderScenario baseline {.operation = ReaderOperation::FULL_SCAN,
107
3
                                   .encoding = Encoding::PLAIN,
108
3
                                   .null_percent = 10,
109
3
                                   .null_pattern = Pattern::ALTERNATING,
110
3
                                   .selectivity_percent = 10,
111
3
                                   .projection = Projection::PREDICATE_PROJECTED,
112
3
                                   .schema_width = 32,
113
3
                                   .predicate_position = 0};
114
3
    for (const auto operation :
115
3
         {ReaderOperation::OPEN_TO_FIRST_BLOCK, ReaderOperation::FULL_SCAN,
116
15
          ReaderOperation::PREDICATE_SCAN, ReaderOperation::LIMIT_1, ReaderOperation::LIMIT_1000}) {
117
15
        auto scenario = baseline;
118
15
        scenario.operation = operation;
119
15
        add(scenario);
120
15
    }
121
3
    for (const auto encoding : {Encoding::PLAIN, Encoding::DICTIONARY, Encoding::BYTE_STREAM_SPLIT,
122
12
                                Encoding::DELTA_BINARY_PACKED}) {
123
12
        auto scenario = baseline;
124
12
        scenario.encoding = encoding;
125
12
        add(scenario);
126
12
        scenario.operation = ReaderOperation::PREDICATE_SCAN;
127
12
        add(scenario);
128
12
    }
129
6
    for (const auto encoding : {Encoding::BYTE_STREAM_SPLIT, Encoding::DELTA_BINARY_PACKED}) {
130
24
        for (const int selectivity : {1, 10, 50, 90}) {
131
24
            for (const auto projection :
132
48
                 {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
133
48
                auto scenario = baseline;
134
48
                scenario.operation = ReaderOperation::PREDICATE_SCAN;
135
48
                scenario.encoding = encoding;
136
48
                scenario.selectivity_percent = selectivity;
137
48
                scenario.projection = projection;
138
48
                add(scenario);
139
48
            }
140
24
        }
141
6
    }
142
12
    for (const int width : {4, 32, 128, 512}) {
143
24
        for (const int predicate_position : {0, width - 1}) {
144
24
            auto scenario = baseline;
145
24
            scenario.operation = ReaderOperation::PREDICATE_SCAN;
146
24
            scenario.schema_width = width;
147
24
            scenario.predicate_position = predicate_position;
148
24
            add(scenario);
149
24
        }
150
12
    }
151
15
    for (const int null_percent : {0, 1, 10, 50, 90}) {
152
30
        for (const auto pattern : {Pattern::CLUSTERED, Pattern::ALTERNATING}) {
153
180
            for (const int selectivity : {0, 1, 10, 50, 90, 100}) {
154
180
                for (const auto projection :
155
360
                     {Projection::PREDICATE_ONLY, Projection::PREDICATE_PROJECTED}) {
156
360
                    auto scenario = baseline;
157
360
                    scenario.operation = ReaderOperation::PREDICATE_SCAN;
158
360
                    scenario.null_percent = null_percent;
159
360
                    scenario.null_pattern = pattern;
160
360
                    scenario.selectivity_percent = selectivity;
161
360
                    scenario.projection = projection;
162
360
                    add(scenario);
163
360
                }
164
180
            }
165
30
        }
166
15
    }
167
3
    return scenarios;
168
3
}
169
170
inline SelectionPlan make_selection_plan(size_t total_rows, int selectivity_percent,
171
4
                                         Pattern pattern) {
172
4
    SelectionPlan plan {.total_rows = total_rows, .selected_rows = 0, .ranges = {}};
173
4
    if (total_rows == 0 || selectivity_percent <= 0) {
174
1
        return plan;
175
1
    }
176
3
    if (selectivity_percent >= 100) {
177
1
        plan.selected_rows = total_rows;
178
1
        plan.ranges.push_back({.first = 0, .count = total_rows});
179
1
        return plan;
180
1
    }
181
2
    plan.selected_rows = total_rows * static_cast<size_t>(selectivity_percent) / 100;
182
2
    if (plan.selected_rows == 0) {
183
0
        plan.selected_rows = 1;
184
0
    }
185
2
    if (pattern == Pattern::CLUSTERED) {
186
1
        plan.ranges.push_back({.first = 0, .count = plan.selected_rows});
187
1
        return plan;
188
1
    }
189
190
    // Evenly spaced rows deliberately maximize the number of physical ranges. This is the
191
    // adversarial sparse shape that exposes per-run decoder and cursor overhead.
192
101
    for (size_t selected = 0; selected < plan.selected_rows; ++selected) {
193
100
        const size_t row = selected * total_rows / plan.selected_rows;
194
100
        if (!plan.ranges.empty() && plan.ranges.back().first + plan.ranges.back().count == row) {
195
0
            ++plan.ranges.back().count;
196
100
        } else {
197
100
            plan.ranges.push_back({.first = row, .count = 1});
198
100
        }
199
100
    }
200
1
    return plan;
201
2
}
202
203
0
inline std::string to_string(Encoding value) {
204
0
    switch (value) {
205
0
    case Encoding::PLAIN:
206
0
        return "plain";
207
0
    case Encoding::DICTIONARY:
208
0
        return "dictionary";
209
0
    case Encoding::BYTE_STREAM_SPLIT:
210
0
        return "byte_stream_split";
211
0
    case Encoding::DELTA_BINARY_PACKED:
212
0
        return "delta_binary_packed";
213
0
    case Encoding::DELTA_LENGTH_BYTE_ARRAY:
214
0
        return "delta_length_byte_array";
215
0
    case Encoding::DELTA_BYTE_ARRAY:
216
0
        return "delta_byte_array";
217
0
    }
218
0
    return "unknown";
219
0
}
220
221
0
inline std::string to_string(ValueType value) {
222
0
    switch (value) {
223
0
    case ValueType::INT32:
224
0
        return "int32";
225
0
    case ValueType::INT64:
226
0
        return "int64";
227
0
    case ValueType::FLOAT:
228
0
        return "float";
229
0
    case ValueType::DOUBLE:
230
0
        return "double";
231
0
    case ValueType::BYTE_ARRAY:
232
0
        return "byte_array";
233
0
    case ValueType::FIXED_LEN_BYTE_ARRAY:
234
0
        return "fixed_len_byte_array";
235
0
    }
236
0
    return "unknown";
237
0
}
238
239
0
inline std::string to_string(Pattern value) {
240
0
    return value == Pattern::CLUSTERED ? "clustered" : "alternating";
241
0
}
242
243
0
inline std::string to_string(Projection value) {
244
0
    return value == Projection::PREDICATE_ONLY ? "predicate_only" : "predicate_projected";
245
0
}
246
247
0
inline std::string to_string(ReaderOperation value) {
248
0
    switch (value) {
249
0
    case ReaderOperation::OPEN_TO_FIRST_BLOCK:
250
0
        return "open_to_first_block";
251
0
    case ReaderOperation::FULL_SCAN:
252
0
        return "full_scan";
253
0
    case ReaderOperation::PREDICATE_SCAN:
254
0
        return "predicate_scan";
255
0
    case ReaderOperation::LIMIT_1:
256
0
        return "limit_1";
257
0
    case ReaderOperation::LIMIT_1000:
258
0
        return "limit_1000";
259
0
    }
260
0
    return "unknown";
261
0
}
262
263
} // namespace doris::parquet_benchmark