Coverage Report

Created: 2026-10-22 01:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/format_v2/parquet/parquet_profile.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
//   http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing,
10
// software distributed under the License is distributed on an
11
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
12
// KIND, either express or implied.  See the License for the
13
// specific language governing permissions and limitations
14
// under the License.
15
16
#pragma once
17
18
#include <memory>
19
#include <mutex>
20
#include <unordered_map>
21
22
#include "runtime/runtime_profile.h"
23
24
namespace doris::format::parquet {
25
26
struct ParquetPruningStats;
27
28
// ============================================================================
29
// ============================================================================
30
struct ParquetPageSkipProfile {
31
    RuntimeProfile::Counter* skipped_pages = nullptr; // number of data pages skipped by page index
32
    RuntimeProfile::Counter* skipped_bytes = nullptr; // compressed bytes skipped
33
};
34
35
// ============================================================================
36
// ============================================================================
37
// Deferred Variant columns can outlive the scanner. This bundle retains all their counters,
38
// while ordinary file/column reader copies only increment one shared reference count.
39
struct ParquetVariantProfile {
40
    std::shared_ptr<RuntimeProfile::Counter> variant_reconstruction_time;
41
    std::shared_ptr<RuntimeProfile::Counter> variant_reconstructed_rows;
42
    // Complete unshredded roots imported without rebuilding their Variant value tree. Bytes count
43
    // the per-row metadata and value references presented to the direct importer.
44
    std::shared_ptr<RuntimeProfile::Counter> variant_unshredded_direct_import_time;
45
    std::shared_ptr<RuntimeProfile::Counter> variant_unshredded_direct_import_rows;
46
    std::shared_ptr<RuntimeProfile::Counter> variant_unshredded_direct_import_bytes;
47
    // Pure metadata+value roots can seek a requested path before constructing a root column.
48
    std::shared_ptr<RuntimeProfile::Counter> variant_unshredded_direct_seek_time;
49
    std::shared_ptr<RuntimeProfile::Counter> variant_unshredded_direct_seek_rows;
50
    std::shared_ptr<RuntimeProfile::Counter> variant_unshredded_direct_seek_bytes;
51
    std::shared_ptr<RuntimeProfile::Counter> variant_unshredded_prefix_reuse_rows;
52
    std::shared_ptr<RuntimeProfile::Counter> variant_direct_subtree_rows;
53
    std::shared_ptr<RuntimeProfile::Counter> variant_direct_leaf_rows;
54
    std::shared_ptr<RuntimeProfile::Counter> variant_direct_leaf_path_misses;
55
    std::shared_ptr<RuntimeProfile::Counter> variant_direct_leaf_residual_fallbacks;
56
    std::shared_ptr<RuntimeProfile::Counter> variant_direct_leaf_unsupported_fallbacks;
57
};
58
59
struct ParquetColumnReaderProfile {
60
    RuntimeProfile::Counter* reader_read_rows = nullptr;   // rows read by read()
61
    RuntimeProfile::Counter* reader_skip_rows = nullptr;   // rows skipped by skip()
62
    RuntimeProfile::Counter* reader_select_rows = nullptr; // rows selected by select()
63
    // COUNT(nullable_col) shape-only path; ordinary scans keep both counters zero.
64
    RuntimeProfile::Counter* level_only_read_time = nullptr;
65
    RuntimeProfile::Counter* level_only_skip_time = nullptr;
66
    RuntimeProfile::Counter* materialization_time = nullptr; // value materialization time (ns)
67
    // One ownership reference per profile copy, rather than one per Variant counter.
68
    std::shared_ptr<const ParquetVariantProfile> variant_counters;
69
70
339
    const ParquetVariantProfile& variant_profile() const {
71
339
        static const ParquetVariantProfile disabled;
72
339
        return variant_counters ? *variant_counters : disabled;
73
339
    }
74
75
    RuntimeProfile::Counter* hybrid_selection_batches = nullptr;
76
    RuntimeProfile::Counter* hybrid_selection_ranges = nullptr;
77
    RuntimeProfile::Counter* hybrid_selection_null_fallback_batches = nullptr;
78
    RuntimeProfile::Counter* dictionary_predicate_fused_projected_rows = nullptr;
79
    // Native page/encoding reader internals. These counters keep page IO, decompression, levels,
80
    // value decode and conversion attributable to separate stages.
81
    RuntimeProfile::Counter* decompress_time = nullptr;
82
    RuntimeProfile::Counter* decompress_count = nullptr;
83
    RuntimeProfile::Counter* decode_header_time = nullptr;
84
    RuntimeProfile::Counter* decode_value_time = nullptr;
85
    RuntimeProfile::Counter* decode_dictionary_time = nullptr;
86
    RuntimeProfile::Counter* decode_level_time = nullptr;
87
    RuntimeProfile::Counter* decode_null_map_time = nullptr;
88
    RuntimeProfile::Counter* page_index_read_calls = nullptr;
89
    RuntimeProfile::Counter* skip_page_header_count = nullptr;
90
    RuntimeProfile::Counter* parse_page_header_count = nullptr;
91
    RuntimeProfile::Counter* read_page_header_time = nullptr;
92
    RuntimeProfile::Counter* page_read_count = nullptr;
93
    RuntimeProfile::Counter* page_cache_write_count = nullptr;
94
    RuntimeProfile::Counter* page_cache_compressed_write_count = nullptr;
95
    RuntimeProfile::Counter* page_cache_decompressed_write_count = nullptr;
96
    RuntimeProfile::Counter* page_cache_hit_count = nullptr;
97
    RuntimeProfile::Counter* page_cache_miss_count = nullptr;
98
    RuntimeProfile::Counter* page_cache_compressed_hit_count = nullptr;
99
    RuntimeProfile::Counter* page_cache_decompressed_hit_count = nullptr;
100
    RuntimeProfile::Counter* native_read_calls = nullptr;     // native column-reader calls
101
    RuntimeProfile::Counter* native_page_fragments = nullptr; // page-bounded read fragments
102
    RuntimeProfile::Counter* page_crossing_batches = nullptr; // batches spanning multiple pages
103
    RuntimeProfile::Counter* nested_batches = nullptr;        // complex-column read batches
104
};
105
106
// ============================================================================
107
// ============================================================================
108
struct ParquetScanProfile {
109
    RuntimeProfile::Counter* raw_rows_read = nullptr; // logical rows consumed before filtering
110
    RuntimeProfile::Counter* selected_rows = nullptr; // rows selected after conjunct filtering
111
    RuntimeProfile::Counter* rows_filtered_by_conjunct = nullptr; // rows filtered by conjuncts
112
    RuntimeProfile::Counter* lazy_read_filtered_rows =
113
            nullptr;                                  // rows avoided by late materialization
114
    RuntimeProfile::Counter* total_batches = nullptr; // total batch count
115
    RuntimeProfile::Counter* dense_batches = nullptr; // batches retaining every physical row
116
    RuntimeProfile::Counter* selected_batches =
117
            nullptr; // non-empty batches compacted by predicates
118
    RuntimeProfile::Counter* empty_selection_batches =
119
            nullptr;                                           // empty batches after full filtering
120
    RuntimeProfile::Counter* range_gap_skipped_rows = nullptr; // rows skipped by range gaps
121
    RuntimeProfile::Counter* column_read_time = nullptr;       // column read time (ns)
122
    RuntimeProfile::Counter* predicate_filter_time = nullptr;  // predicate filter time (ns)
123
    RuntimeProfile::Counter* predicate_compaction_time = nullptr;
124
    RuntimeProfile::Counter* predicate_compaction_bytes = nullptr;
125
    RuntimeProfile::Counter* predicate_compaction_count = nullptr;
126
    RuntimeProfile::Counter* predicate_alignment_columns = nullptr;
127
    RuntimeProfile::Counter* fixed_width_predicate_direct_batches = nullptr;
128
    RuntimeProfile::Counter* fixed_width_predicate_direct_rows = nullptr;
129
    RuntimeProfile::Counter* raw_value_predicate_direct_batches = nullptr;
130
    RuntimeProfile::Counter* raw_value_predicate_direct_rows = nullptr;
131
    RuntimeProfile::Counter* typed_runtime_filter_direct_batches = nullptr;
132
    RuntimeProfile::Counter* typed_runtime_filter_direct_rows = nullptr;
133
    RuntimeProfile::Counter* dictionary_predicate_direct_batches = nullptr;
134
    RuntimeProfile::Counter* dictionary_predicate_direct_rows = nullptr;
135
    RuntimeProfile::Counter* dictionary_predicate_projected_rows = nullptr;
136
    RuntimeProfile::Counter* dict_filter_rewrite_time = nullptr; // dictionary rewrite time (ns)
137
    RuntimeProfile::Counter* dict_filter_expr_rewrite_time =
138
            nullptr; // expression/residual rewrite time (ns)
139
    RuntimeProfile::Counter* dict_filter_read_dict_time = nullptr; // dictionary page read time (ns)
140
    RuntimeProfile::Counter* dict_filter_build_time =
141
            nullptr; // dictionary entry bitmap build time (ns)
142
    RuntimeProfile::Counter* dict_filter_candidate_columns = nullptr; // candidate columns
143
    RuntimeProfile::Counter* dict_filter_columns = nullptr;           // optimized columns
144
    RuntimeProfile::Counter* dict_filter_typed_compare_columns =
145
            nullptr; // fixed-width typed comparison columns
146
    RuntimeProfile::Counter* dict_filter_string_compare_columns =
147
            nullptr; // string typed comparison columns
148
    RuntimeProfile::Counter* dict_filter_vectorized_runtime_filter_columns =
149
            nullptr; // vectorized runtime-filter columns
150
    RuntimeProfile::Counter* dict_filter_unsupported_columns = nullptr; // unsupported columns
151
    RuntimeProfile::Counter* dict_filter_read_failures = nullptr;       // dictionary read failures
152
    RuntimeProfile::Counter* rows_filtered_by_dict_filter = nullptr;    // rows filtered by dict
153
    ParquetColumnReaderProfile column_reader_profile; // nested column read statistics
154
};
155
156
// ============================================================================
157
// ============================================================================
158
// ============================================================================
159
struct ParquetProfile {
160
    void init(RuntimeProfile* profile);
161
    void update_pruning_stats(const ParquetPruningStats& pruning_stats) const;
162
    void update_deferred_pruning_stats(const ParquetPruningStats& pruning_stats,
163
                                       bool selected) const;
164
165
    ParquetPageSkipProfile page_skip_profile() const;
166
    ParquetColumnReaderProfile column_reader_profile() const;
167
    ParquetScanProfile scan_profile() const;
168
169
    RuntimeProfile::Counter* total_time = nullptr;
170
    RuntimeProfile::Counter* refresh_scan_request_time = nullptr;
171
172
    RuntimeProfile::Counter* filtered_row_groups = nullptr;
173
    RuntimeProfile::Counter* filtered_row_groups_by_min_max = nullptr;
174
    RuntimeProfile::Counter* filtered_row_groups_by_dictionary = nullptr;
175
    RuntimeProfile::Counter* filtered_row_groups_by_bloom_filter = nullptr;
176
    RuntimeProfile::Counter* filtered_row_groups_by_page_index = nullptr;
177
    RuntimeProfile::Counter* to_read_row_groups = nullptr;
178
    RuntimeProfile::Counter* total_row_groups = nullptr;
179
    RuntimeProfile::Counter* selected_row_ranges = nullptr;
180
    RuntimeProfile::Counter* filtered_group_rows = nullptr;
181
    RuntimeProfile::Counter* filtered_page_rows = nullptr;
182
    // Variant access paths eligible for a row-group physical typed-leaf projection.
183
    RuntimeProfile::Counter* variant_leaf_projections = nullptr;
184
    // Row-group outcomes for candidate Variant projections, counted per projected column opened.
185
    RuntimeProfile::Counter* variant_leaf_projection_row_group_columns = nullptr;
186
    RuntimeProfile::Counter* variant_full_projection_row_group_columns = nullptr;
187
188
    // ======== Page Skip ========
189
    RuntimeProfile::Counter* pages_skipped_by_data_page_filter = nullptr;
190
    RuntimeProfile::Counter* data_page_filter_skip_bytes = nullptr;
191
192
    RuntimeProfile::Counter* selected_rows = nullptr;
193
    RuntimeProfile::Counter* rows_filtered_by_conjunct = nullptr;
194
    RuntimeProfile::Counter* total_batches = nullptr;
195
    RuntimeProfile::Counter* dense_batches = nullptr;
196
    RuntimeProfile::Counter* selected_batches = nullptr;
197
    RuntimeProfile::Counter* empty_selection_batches = nullptr;
198
    RuntimeProfile::Counter* range_gap_skipped_rows = nullptr;
199
200
    // ======== Column Reader ========
201
    RuntimeProfile::Counter* reader_read_rows = nullptr;
202
    RuntimeProfile::Counter* reader_skip_rows = nullptr;
203
    RuntimeProfile::Counter* reader_select_rows = nullptr;
204
    // COUNT(nullable_col) shape-only path; ordinary scans keep these zero.
205
    RuntimeProfile::Counter* level_only_read_time = nullptr;
206
    RuntimeProfile::Counter* level_only_skip_time = nullptr;
207
    RuntimeProfile::Counter* materialization_time = nullptr;
208
    // One ownership reference per profile copy, rather than one per Variant counter.
209
    std::shared_ptr<const ParquetVariantProfile> variant_counters;
210
211
10
    const ParquetVariantProfile& variant_profile() const {
212
10
        static const ParquetVariantProfile disabled;
213
10
        return variant_counters ? *variant_counters : disabled;
214
10
    }
215
216
    RuntimeProfile::Counter* hybrid_selection_batches = nullptr;
217
    RuntimeProfile::Counter* hybrid_selection_ranges = nullptr;
218
    RuntimeProfile::Counter* hybrid_selection_null_fallback_batches = nullptr;
219
    RuntimeProfile::Counter* dictionary_predicate_fused_projected_rows = nullptr;
220
    RuntimeProfile::Counter* native_read_calls = nullptr;
221
    RuntimeProfile::Counter* native_page_fragments = nullptr;
222
    RuntimeProfile::Counter* page_crossing_batches = nullptr;
223
    RuntimeProfile::Counter* nested_batches = nullptr;
224
225
    RuntimeProfile::Counter* lazy_read_filtered_rows = nullptr;
226
    RuntimeProfile::Counter* filtered_bytes = nullptr;
227
    RuntimeProfile::Counter* raw_rows_read = nullptr;
228
    RuntimeProfile::Counter* column_read_time = nullptr;
229
230
    RuntimeProfile::Counter* parse_meta_time = nullptr;
231
    RuntimeProfile::Counter* parse_footer_time = nullptr;
232
    RuntimeProfile::Counter* file_reader_create_time = nullptr;
233
    RuntimeProfile::Counter* open_file_num = nullptr;
234
    RuntimeProfile::Counter* file_footer_read_calls = nullptr;
235
    RuntimeProfile::Counter* file_footer_hit_cache = nullptr;
236
237
    RuntimeProfile::Counter* row_group_filter_time = nullptr;
238
    RuntimeProfile::Counter* page_index_read_calls = nullptr;
239
    RuntimeProfile::Counter* page_index_filter_time = nullptr;
240
    RuntimeProfile::Counter* read_page_index_time = nullptr;
241
    RuntimeProfile::Counter* parse_page_index_time = nullptr;
242
    RuntimeProfile::Counter* expr_zonemap_unusable = nullptr;
243
    RuntimeProfile::Counter* in_zonemap_point_check = nullptr;
244
    RuntimeProfile::Counter* in_zonemap_range_only = nullptr;
245
246
    RuntimeProfile::Counter* decompress_time = nullptr;
247
    RuntimeProfile::Counter* decompress_cnt = nullptr;
248
    RuntimeProfile::Counter* page_read_counter = nullptr;
249
    RuntimeProfile::Counter* page_cache_write_counter = nullptr;
250
    RuntimeProfile::Counter* page_cache_compressed_write_counter = nullptr;
251
    RuntimeProfile::Counter* page_cache_decompressed_write_counter = nullptr;
252
    RuntimeProfile::Counter* page_cache_hit_counter = nullptr;
253
    RuntimeProfile::Counter* page_cache_missing_counter = nullptr;
254
    RuntimeProfile::Counter* page_cache_compressed_hit_counter = nullptr;
255
    RuntimeProfile::Counter* page_cache_decompressed_hit_counter = nullptr;
256
257
    RuntimeProfile::Counter* decode_header_time = nullptr;
258
    RuntimeProfile::Counter* read_page_header_time = nullptr;
259
    RuntimeProfile::Counter* decode_value_time = nullptr;
260
    RuntimeProfile::Counter* decode_dict_time = nullptr;
261
    RuntimeProfile::Counter* decode_level_time = nullptr;
262
    RuntimeProfile::Counter* decode_null_map_time = nullptr;
263
    RuntimeProfile::Counter* skip_page_header_num = nullptr;
264
    RuntimeProfile::Counter* parse_page_header_num = nullptr;
265
266
    RuntimeProfile::Counter* predicate_filter_time = nullptr;
267
    RuntimeProfile::Counter* predicate_compaction_time = nullptr;
268
    RuntimeProfile::Counter* predicate_compaction_bytes = nullptr;
269
    RuntimeProfile::Counter* predicate_compaction_count = nullptr;
270
    RuntimeProfile::Counter* predicate_alignment_columns = nullptr;
271
    RuntimeProfile::Counter* fixed_width_predicate_direct_batches = nullptr;
272
    RuntimeProfile::Counter* fixed_width_predicate_direct_rows = nullptr;
273
    RuntimeProfile::Counter* raw_value_predicate_direct_batches = nullptr;
274
    RuntimeProfile::Counter* raw_value_predicate_direct_rows = nullptr;
275
    RuntimeProfile::Counter* typed_runtime_filter_direct_batches = nullptr;
276
    RuntimeProfile::Counter* typed_runtime_filter_direct_rows = nullptr;
277
    RuntimeProfile::Counter* dictionary_predicate_direct_batches = nullptr;
278
    RuntimeProfile::Counter* dictionary_predicate_direct_rows = nullptr;
279
    RuntimeProfile::Counter* dictionary_predicate_projected_rows = nullptr;
280
    RuntimeProfile::Counter* dict_filter_rewrite_time = nullptr;
281
    RuntimeProfile::Counter* dict_filter_expr_rewrite_time = nullptr;
282
    RuntimeProfile::Counter* dict_filter_read_dict_time = nullptr;
283
    RuntimeProfile::Counter* dict_filter_build_time = nullptr;
284
    RuntimeProfile::Counter* dict_filter_candidate_columns = nullptr;
285
    RuntimeProfile::Counter* dict_filter_columns = nullptr;
286
    RuntimeProfile::Counter* dict_filter_typed_compare_columns = nullptr;
287
    RuntimeProfile::Counter* dict_filter_string_compare_columns = nullptr;
288
    RuntimeProfile::Counter* dict_filter_vectorized_runtime_filter_columns = nullptr;
289
    RuntimeProfile::Counter* dict_filter_unsupported_columns = nullptr;
290
    RuntimeProfile::Counter* dict_filter_read_failures = nullptr;
291
    RuntimeProfile::Counter* rows_filtered_by_dict_filter = nullptr;
292
    RuntimeProfile::Counter* bloom_filter_probe_attempts = nullptr;
293
    RuntimeProfile::Counter* bloom_filter_probe_successes = nullptr;
294
    RuntimeProfile::Counter* bloom_filter_conservative_fallbacks = nullptr;
295
    RuntimeProfile::Counter* bloom_filter_corrupt_rejections = nullptr;
296
    RuntimeProfile::Counter* bloom_filter_read_time = nullptr;
297
};
298
299
// One immutable counter set per scanner RuntimeProfile. Readers copy its handles instead of
300
// registering every counter again for each file. Lazy initialization keeps other formats' profiles
301
// free of unused Parquet counters.
302
class ParquetProfileCache {
303
public:
304
22
    explicit ParquetProfileCache(RuntimeProfile* profile) : _profile(profile) {}
305
    const ParquetProfile& get();
306
    // Nested table readers share a child RuntimeProfile across scanner instances. Their counters
307
    // must stay on that child, while the root scan owns the cache until every scanner is closed.
308
    ParquetProfileCache* get_child_cache(RuntimeProfile* child_profile);
309
310
private:
311
    RuntimeProfile* _profile;
312
    std::once_flag _initialized;
313
    ParquetProfile _counters;
314
    std::mutex _children_mutex;
315
    std::unordered_map<RuntimeProfile*, std::unique_ptr<ParquetProfileCache>> _children;
316
};
317
318
} // namespace doris::format::parquet