Skip to content

Commit 968fa1c

Browse files
authored
feat: support prefetch for orc (#77)
1 parent d3bb3a9 commit 968fa1c

31 files changed

Lines changed: 1487 additions & 170 deletions

cmake_modules/orc.diff

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ index a914e5f26..efe1d4933 100644
3030
};
3131

3232
// Specializations for char
33+
diff --git a/c++/include/orc/Reader.hh b/c++/include/orc/Reader.hh
34+
index b015b6491..585e50ec5 100644
35+
--- a/c++/include/orc/Reader.hh
36+
+++ b/c++/include/orc/Reader.hh
37+
@@ -659,6 +659,9 @@ namespace orc {
38+
virtual void preBuffer(const std::vector<uint32_t>& stripes,
39+
const std::list<uint64_t>& includeTypes) = 0;
40+
41+
+ virtual std::vector<std::pair<uint64_t, uint64_t>> preBufferRange(
42+
+ const std::vector<uint32_t>& stripes, const std::list<uint64_t>& includeTypes) = 0;
43+
+
44+
/**
45+
* Release cached entries whose right boundary is less than or equal to the given boundary.
46+
* @param boundary the boundary value to release cache entries
3347
diff --git a/c++/src/ColumnReader.cc b/c++/src/ColumnReader.cc
3448
index af434c37c..08393259c 100644
3549
--- a/c++/src/ColumnReader.cc
@@ -328,6 +342,86 @@ index ed7fee737..a8ee8a67c 100644
328342
reserve(newSize);
329343
if (newSize > currentSize_) {
330344
memset(buf_ + currentSize_, 0, newSize - currentSize_);
345+
diff --git a/c++/src/Reader.cc b/c++/src/Reader.cc
346+
index c93c62f6c..2a821b622 100644
347+
--- a/c++/src/Reader.cc
348+
+++ b/c++/src/Reader.cc
349+
@@ -1531,8 +1531,8 @@ namespace orc {
350+
}
351+
}
352+
353+
- void ReaderImpl::preBuffer(const std::vector<uint32_t>& stripes,
354+
- const std::list<uint64_t>& includeTypes) {
355+
+ std::vector<std::pair<uint64_t, uint64_t>> ReaderImpl::preBufferRange(
356+
+ const std::vector<uint32_t>& stripes, const std::list<uint64_t>& includeTypes) {
357+
std::vector<uint32_t> newStripes;
358+
for (auto stripe : stripes) {
359+
if (stripe < static_cast<uint32_t>(footer_->stripes_size())) newStripes.push_back(stripe);
360+
@@ -1544,7 +1544,7 @@ namespace orc {
361+
}
362+
363+
if (newStripes.empty() || newIncludeTypes.empty()) {
364+
- return;
365+
+ return {};
366+
}
367+
368+
orc::RowReaderOptions rowReaderOptions;
369+
@@ -1553,7 +1553,7 @@ namespace orc {
370+
std::vector<bool> selectedColumns;
371+
columnSelector.updateSelected(selectedColumns, rowReaderOptions);
372+
373+
- std::vector<ReadRange> ranges;
374+
+ std::vector<std::pair<uint64_t, uint64_t>> ranges;
375+
ranges.reserve(newIncludeTypes.size());
376+
for (auto stripe : newStripes) {
377+
// get stripe information
378+
@@ -1598,17 +1598,23 @@ namespace orc {
379+
380+
offset += stream.length();
381+
}
382+
+ }
383+
+ return ranges;
384+
+ }
385+
386+
- {
387+
- std::lock_guard<std::mutex> lock(contents_->readCacheMutex);
388+
-
389+
- if (!contents_->readCache) {
390+
- contents_->readCache = std::make_shared<ReadRangeCache>(
391+
- getStream(), options_.getCacheOptions(), contents_->pool, contents_->readerMetrics);
392+
- }
393+
- contents_->readCache->cache(std::move(ranges));
394+
- }
395+
+ void ReaderImpl::preBuffer(const std::vector<uint32_t>& stripes,
396+
+ const std::list<uint64_t>& includeTypes) {
397+
+ auto ranges = preBufferRange(stripes, includeTypes);
398+
+ std::vector<ReadRange> read_ranges;
399+
+ for (const auto& range : ranges) {
400+
+ read_ranges.emplace_back(range.first, range.second);
401+
+ }
402+
+ std::lock_guard<std::mutex> lock(contents_->readCacheMutex);
403+
+ if (!contents_->readCache) {
404+
+ contents_->readCache = std::make_shared<ReadRangeCache>(
405+
+ getStream(), options_.getCacheOptions(), contents_->pool, contents_->readerMetrics);
406+
}
407+
+ contents_->readCache->cache(std::move(read_ranges));
408+
}
409+
410+
RowReader::~RowReader() {
411+
diff --git a/c++/src/Reader.hh b/c++/src/Reader.hh
412+
index 39ca73967..13da45a49 100644
413+
--- a/c++/src/Reader.hh
414+
+++ b/c++/src/Reader.hh
415+
@@ -387,6 +387,9 @@ namespace orc {
416+
std::map<uint32_t, BloomFilterIndex> getBloomFilters(
417+
uint32_t stripeIndex, const std::set<uint32_t>& included) const override;
418+
419+
+ std::vector<std::pair<uint64_t, uint64_t>> preBufferRange(
420+
+ const std::vector<uint32_t>& stripes, const std::list<uint64_t>& includeTypes) override;
421+
+
422+
void preBuffer(const std::vector<uint32_t>& stripes,
423+
const std::list<uint64_t>& includeTypes) override;
424+
void releaseBuffer(uint64_t boundary) override;
331425
diff --git a/cmake_modules/ThirdpartyToolchain.cmake b/cmake_modules/ThirdpartyToolchain.cmake
332426
index 9b2c829c7..434841224 100644
333427
--- a/cmake_modules/ThirdpartyToolchain.cmake

include/paimon/read_context.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "paimon/predicate/predicate.h"
2727
#include "paimon/result.h"
2828
#include "paimon/type_fwd.h"
29+
#include "paimon/utils/read_ahead_cache.h"
2930
#include "paimon/utils/special_field_ids.h"
3031
#include "paimon/visibility.h"
3132

@@ -53,7 +54,8 @@ class PAIMON_EXPORT ReadContext {
5354
const std::shared_ptr<Executor>& executor,
5455
const std::shared_ptr<FileSystem>& specific_file_system,
5556
const std::map<std::string, std::string>& fs_scheme_to_identifier_map,
56-
const std::map<std::string, std::string>& options);
57+
const std::map<std::string, std::string>& options, bool enable_prefetch_cache,
58+
const CacheConfig& cache_config);
5759
~ReadContext();
5860

5961
const std::string& GetPath() const {
@@ -115,6 +117,14 @@ class PAIMON_EXPORT ReadContext {
115117
return specific_file_system_;
116118
}
117119

120+
bool EnablePrefetchCache() const {
121+
return enable_prefetch_cache_;
122+
}
123+
124+
const CacheConfig& GetCacheConfig() const {
125+
return cache_config_;
126+
}
127+
118128
private:
119129
std::string path_;
120130
std::string branch_;
@@ -133,6 +143,8 @@ class PAIMON_EXPORT ReadContext {
133143
std::shared_ptr<FileSystem> specific_file_system_;
134144
std::map<std::string, std::string> fs_scheme_to_identifier_map_;
135145
std::map<std::string, std::string> options_;
146+
bool enable_prefetch_cache_;
147+
CacheConfig cache_config_;
136148
};
137149

138150
/// `ReadContextBuilder` used to build a `ReadContext`, has input validation.
@@ -215,6 +227,20 @@ class PAIMON_EXPORT ReadContextBuilder {
215227
/// @return Reference to this builder for method chaining.
216228
ReadContextBuilder& EnablePrefetch(bool enabled);
217229

230+
/// Enable or disable prefetch cache for read operations.
231+
///
232+
/// When enabled, a prefetch cache is used to prebuffer data ranges before they are needed,
233+
/// which can improve read performance by reducing redundant I/O operations.
234+
/// @param enabled Whether to enable prefetch cache (default: true)
235+
/// @return Reference to this builder for method chaining.
236+
ReadContextBuilder& EnablePrefetchCache(bool enabled);
237+
238+
/// Set the cache configuration for prefetch read operations.
239+
///
240+
/// @param config The cache configuration to use.
241+
/// @return Reference to this builder for method chaining.
242+
ReadContextBuilder& WithCacheConfig(const CacheConfig& config);
243+
218244
/// Set the total number of batches to prefetch across all files.
219245
///
220246
/// This controls the memory usage and parallelism of the prefetching mechanism.

include/paimon/reader/prefetch_file_batch_reader.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,26 @@ class PAIMON_EXPORT PrefetchFileBatchReader : public FileBatchReader {
7171
/// @param read_ranges A vector of pairs, where each pair defines a half-open interval
7272
/// `[start_row, end_row)`. The `start_row` is inclusive, and the `end_row` is exclusive.
7373
virtual Status SetReadRanges(const std::vector<std::pair<uint64_t, uint64_t>>& read_ranges) = 0;
74+
75+
/// Returns a list of file offset/length ranges that should be prefetched for the current read
76+
/// scenario.
77+
///
78+
/// This method should analyze the columns selected by the user and return the minimal set of
79+
/// physical file ranges (offset, length) that need to be read, avoiding unnecessary IO
80+
/// amplification. For example, if only a subset of columns is requested, the implementation
81+
/// should only return the byte ranges corresponding to those columns, rather than the entire
82+
/// row group or block.
83+
///
84+
/// This enables the cache to prefetch only the required data, reducing disk and network load
85+
/// and improving performance for columnar formats and selective queries.
86+
///
87+
/// By default, returns an empty list (no prefetching). Format-specific implementations should
88+
/// override this method to provide accurate offset/length hints for efficient IO.
89+
/// @return A vector of pairs, where each pair contains the file offset and length to be
90+
/// prefetched.
91+
virtual Result<std::vector<std::pair<uint64_t, uint64_t>>> PreBufferRange() {
92+
return std::vector<std::pair<uint64_t, uint64_t>>{};
93+
}
7494
};
7595

7696
} // namespace paimon

src/paimon/common/file_index/bitmap/apply_bitmap_index_batch_reader_test.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "paimon/testing/mock/mock_format_reader_builder.h"
3636
#include "paimon/testing/utils/read_result_collector.h"
3737
#include "paimon/testing/utils/testharness.h"
38+
#include "paimon/utils/read_ahead_cache.h"
3839

3940
namespace arrow {
4041
class Array;
@@ -92,7 +93,8 @@ class ApplyBitmapIndexBatchReaderTest : public ::testing::Test,
9293
/*data_file_path=*/"DUMMY", &reader_builder, fs_,
9394
prefetch_batch_count, batch_size, prefetch_batch_count * 2,
9495
/*enable_adaptive_prefetch_strategy=*/false, executor_,
95-
/*initialize_read_ranges=*/true));
96+
/*initialize_read_ranges=*/true,
97+
/*enable_prefetch_cache=*/true, CacheConfig(), pool_));
9698
} else {
9799
file_batch_reader =
98100
std::make_unique<MockFileBatchReader>(data, target_type_, batch_size);
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2026-present Alibaba Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cstring>
20+
#include <memory>
21+
#include <string>
22+
23+
#include "paimon/fs/file_system.h"
24+
#include "paimon/utils/read_ahead_cache.h"
25+
26+
namespace paimon {
27+
28+
class CacheInputStream : public InputStream {
29+
public:
30+
CacheInputStream(std::unique_ptr<InputStream> input_stream,
31+
const std::shared_ptr<ReadAheadCache>& cache)
32+
: cache_(cache), input_stream_(std::move(input_stream)) {}
33+
34+
Status Seek(int64_t offset, SeekOrigin origin) override {
35+
return input_stream_->Seek(offset, origin);
36+
}
37+
Result<int64_t> GetPos() const override {
38+
return input_stream_->GetPos();
39+
}
40+
Result<int32_t> Read(char* buffer, uint32_t size) override {
41+
return input_stream_->Read(buffer, size);
42+
}
43+
Result<int32_t> Read(char* buffer, uint32_t size, uint64_t offset) override {
44+
if (cache_) {
45+
ByteRange range{offset, static_cast<uint64_t>(size)};
46+
PAIMON_ASSIGN_OR_RAISE(ByteSlice slice, cache_->Read(range));
47+
if (slice.buffer) {
48+
std::memcpy(buffer, slice.buffer->data() + slice.offset, slice.length);
49+
return slice.length;
50+
}
51+
}
52+
return input_stream_->Read(buffer, size, offset);
53+
}
54+
void ReadAsync(char* buffer, uint32_t size, uint64_t offset,
55+
std::function<void(Status)>&& callback) override {
56+
if (cache_) {
57+
ByteRange range{offset, static_cast<uint64_t>(size)};
58+
Result<ByteSlice> slice = cache_->Read(range);
59+
if (!slice.ok()) {
60+
callback(slice.status());
61+
return;
62+
}
63+
if (slice.value().buffer) {
64+
std::memcpy(buffer, slice.value().buffer->data() + slice.value().offset,
65+
slice.value().length);
66+
callback(Status::OK());
67+
return;
68+
}
69+
}
70+
return input_stream_->ReadAsync(buffer, size, offset, std::move(callback));
71+
}
72+
73+
Status Close() override {
74+
return input_stream_->Close();
75+
}
76+
77+
Result<std::string> GetUri() const override {
78+
return input_stream_->GetUri();
79+
}
80+
81+
Result<uint64_t> Length() const override {
82+
return input_stream_->Length();
83+
}
84+
85+
private:
86+
std::shared_ptr<ReadAheadCache> cache_;
87+
std::unique_ptr<InputStream> input_stream_;
88+
};
89+
90+
} // namespace paimon

0 commit comments

Comments
 (0)