Skip to content

Commit c2344a8

Browse files
AntoinePrvpitrou
authored andcommitted
Explicitly pass bit width (don't store multiple time)
1 parent dcc6db0 commit c2344a8

2 files changed

Lines changed: 122 additions & 108 deletions

File tree

cpp/src/arrow/util/rle_encoding_internal.h

Lines changed: 76 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ using rle_size_t = int32_t;
9292
template <typename T>
9393
class RleRunDecoder;
9494

95+
/// A Single Run Length Encoded run.
96+
///
97+
/// Consist of a single value repeated multiple times.
98+
/// A previous version of this class also stored the value bit width to be self contain,
99+
/// removing it and passing it explicitly when needed proved to speed up decoding up to
100+
/// 10 % on some benchmarks.
95101
class RleRun {
96102
public:
97103
/// The decoder class used to decode a single run in the given type.
@@ -102,24 +108,21 @@ class RleRun {
102108

103109
explicit RleRun(const uint8_t* data, rle_size_t values_count,
104110
rle_size_t value_bit_width) noexcept
105-
: values_count_(values_count), value_bit_width_(value_bit_width) {
111+
: values_count_(values_count) {
106112
ARROW_DCHECK_GE(value_bit_width, 0);
107113
ARROW_DCHECK_GE(values_count, 0);
108-
std::copy(data, data + raw_data_size(), data_.begin());
114+
std::copy(data, data + raw_data_size(value_bit_width), data_.begin());
109115
}
110116

111117
/// The number of repeated values in this run.
112118
constexpr rle_size_t values_count() const noexcept { return values_count_; }
113119

114-
/// The size in bits of each encoded value.
115-
constexpr rle_size_t values_bit_width() const noexcept { return value_bit_width_; }
116-
117120
/// A pointer to the repeated value raw bytes.
118121
constexpr const uint8_t* raw_data_ptr() const noexcept { return data_.data(); }
119122

120123
/// The number of bytes used for the raw repeated value.
121-
constexpr rle_size_t raw_data_size() const noexcept {
122-
auto out = bit_util::BytesForBits(value_bit_width_);
124+
constexpr rle_size_t raw_data_size(rle_size_t value_bit_width) const noexcept {
125+
auto out = bit_util::BytesForBits(value_bit_width);
123126
ARROW_DCHECK_LE(out, std::numeric_limits<rle_size_t>::max());
124127
return static_cast<rle_size_t>(out);
125128
}
@@ -130,13 +133,19 @@ class RleRun {
130133
std::array<uint8_t, 8> data_ = {};
131134
/// The number of time the value is repeated.
132135
rle_size_t values_count_ = 0;
133-
/// The size in bit of a packed value in the run.
134-
rle_size_t value_bit_width_ = 0;
135136
};
136137

137138
template <typename T>
138139
class BitPackedRunDecoder;
139140

141+
/// A single bit packed run.
142+
///
143+
/// Consist of a view on a buffer of bytes that encode integers on ``value_bit_width``
144+
/// bits (that is the numbers are small enough that high order bits are all zeros and can
145+
/// be omitted).
146+
/// A previous version of this class also stored the value bit width to be self contain,
147+
/// removing it and passing it explicitly when needed proved to speed up decoding up to
148+
/// 10 % on some benchmarks.
140149
class BitPackedRun {
141150
public:
142151
/// The decoder class used to decode a single run in the given type.
@@ -147,20 +156,17 @@ class BitPackedRun {
147156

148157
constexpr BitPackedRun(const uint8_t* data, rle_size_t values_count,
149158
rle_size_t value_bit_width) noexcept
150-
: data_(data), values_count_(values_count), value_bit_width_(value_bit_width) {
151-
ARROW_CHECK_GE(value_bit_width_, 0);
159+
: data_(data), values_count_(values_count) {
160+
ARROW_CHECK_GE(value_bit_width, 0);
152161
ARROW_CHECK_GE(values_count_, 0);
153162
}
154163

155164
constexpr rle_size_t values_count() const noexcept { return values_count_; }
156165

157-
/// The size in bits of each encoded value.
158-
constexpr rle_size_t values_bit_width() const noexcept { return value_bit_width_; }
159-
160166
constexpr const uint8_t* raw_data_ptr() const noexcept { return data_; }
161167

162-
constexpr rle_size_t raw_data_size() const noexcept {
163-
auto out = bit_util::BytesForBits(static_cast<int64_t>(value_bit_width_) *
168+
constexpr rle_size_t raw_data_size(rle_size_t value_bit_width) const noexcept {
169+
auto out = bit_util::BytesForBits(static_cast<int64_t>(value_bit_width) *
164170
static_cast<int64_t>(values_count_));
165171
ARROW_CHECK_LE(out, std::numeric_limits<rle_size_t>::max());
166172
return static_cast<rle_size_t>(out);
@@ -171,8 +177,6 @@ class BitPackedRun {
171177
const uint8_t* data_ = nullptr;
172178
/// Number of values in this run.
173179
rle_size_t values_count_ = 0;
174-
/// The size in bit of a packed value in the run
175-
rle_size_t value_bit_width_ = 0;
176180
};
177181

178182
/// A parser that emits either a ``BitPackedRun`` or a ``RleRun``.
@@ -249,9 +253,11 @@ class RleRunDecoder {
249253

250254
constexpr RleRunDecoder() noexcept = default;
251255

252-
explicit RleRunDecoder(const RunType& run) noexcept { Reset(run); }
256+
explicit RleRunDecoder(const RunType& run, rle_size_t value_bit_width) noexcept {
257+
Reset(run, value_bit_width);
258+
}
253259

254-
void Reset(const RunType& run) noexcept {
260+
void Reset(const RunType& run, rle_size_t value_bit_width) noexcept {
255261
remaining_count_ = run.values_count();
256262
if constexpr (std::is_same_v<value_type, bool>) {
257263
// ARROW-18031: just check the LSB of the next byte and move on.
@@ -261,7 +267,7 @@ class RleRunDecoder {
261267
} else {
262268
// Memcopy is required to avoid undefined behavior.
263269
value_ = {};
264-
std::memcpy(&value_, run.raw_data_ptr(), run.raw_data_size());
270+
std::memcpy(&value_, run.raw_data_ptr(), run.raw_data_size(value_bit_width));
265271
value_ = ::arrow::bit_util::FromLittleEndian(value_);
266272
}
267273
}
@@ -275,21 +281,22 @@ class RleRunDecoder {
275281
/// Try to advance by as many values as provided.
276282
/// Return the number of values skipped.
277283
/// May advance by less than asked for if there are not enough values left.
278-
[[nodiscard]] rle_size_t Advance(rle_size_t batch_size) {
284+
[[nodiscard]] rle_size_t Advance(rle_size_t batch_size, rle_size_t value_bit_width) {
279285
const auto steps = std::min(batch_size, remaining_count_);
280286
remaining_count_ -= steps;
281287
return steps;
282288
}
283289

284290
/// Get the next value and return false if there are no more.
285-
[[nodiscard]] constexpr bool Get(value_type* out_value) {
286-
return GetBatch(out_value, 1) == 1;
291+
[[nodiscard]] constexpr bool Get(value_type* out_value, rle_size_t value_bit_width) {
292+
return GetBatch(out_value, 1, value_bit_width) == 1;
287293
}
288294

289295
/// Get a batch of values return the number of decoded elements.
290296
/// May write fewer elements to the output than requested if there are not enough values
291297
/// left.
292-
[[nodiscard]] rle_size_t GetBatch(value_type* out, rle_size_t batch_size) {
298+
[[nodiscard]] rle_size_t GetBatch(value_type* out, rle_size_t batch_size,
299+
rle_size_t value_bit_width) {
293300
if (ARROW_PREDICT_FALSE(remaining_count_ == 0)) {
294301
return 0;
295302
}
@@ -319,47 +326,48 @@ class BitPackedRunDecoder {
319326

320327
BitPackedRunDecoder() noexcept = default;
321328

322-
explicit BitPackedRunDecoder(const RunType& run) noexcept { Reset(run); }
329+
explicit BitPackedRunDecoder(const RunType& run, rle_size_t value_bit_width) noexcept {
330+
Reset(run, value_bit_width);
331+
}
323332

324-
void Reset(const RunType& run) noexcept {
325-
value_bit_width_ = run.values_bit_width();
333+
void Reset(const RunType& run, rle_size_t value_bit_width) noexcept {
326334
remaining_count_ = run.values_count();
327-
ARROW_DCHECK_GE(value_bit_width_, 0);
328-
ARROW_DCHECK_LE(value_bit_width_, 64);
329-
bit_reader_.Reset(run.raw_data_ptr(), run.raw_data_size());
335+
ARROW_DCHECK_GE(value_bit_width, 0);
336+
ARROW_DCHECK_LE(value_bit_width, 64);
337+
bit_reader_.Reset(run.raw_data_ptr(), run.raw_data_size(value_bit_width));
330338
}
331339

332340
/// Return the number of values that can be advanced.
333341
constexpr rle_size_t remaining() const { return remaining_count_; }
334342

335-
/// Return the size in bit in which each encoded value is written.
336-
constexpr rle_size_t value_bit_width() const { return value_bit_width_; }
337-
338343
/// Try to advance by as many values as provided.
339344
/// Return the number of values skipped or 0 if it fail to advance.
340345
/// May advance by less than asked for if there are not enough values left.
341-
[[nodiscard]] rle_size_t Advance(rle_size_t batch_size) {
346+
[[nodiscard]] rle_size_t Advance(rle_size_t batch_size, rle_size_t value_bit_width) {
342347
const auto steps = std::min(batch_size, remaining_count_);
343-
if (bit_reader_.Advance(steps * value_bit_width_)) {
348+
if (bit_reader_.Advance(steps * value_bit_width)) {
344349
remaining_count_ -= steps;
345350
return steps;
346351
}
347352
return 0;
348353
}
349354

350355
/// Get the next value and return false if there are no more or an error occurred.
351-
[[nodiscard]] bool Get(value_type* out_value) { return GetBatch(out_value, 1) == 1; }
356+
[[nodiscard]] constexpr bool Get(value_type* out_value, rle_size_t value_bit_width) {
357+
return GetBatch(out_value, 1, value_bit_width) == 1;
358+
}
352359

353360
/// Get a batch of values return the number of decoded elements.
354361
/// May write fewer elements to the output than requested if there are not enough values
355362
/// left or if an error occurred.
356-
[[nodiscard]] rle_size_t GetBatch(value_type* out, rle_size_t batch_size) {
363+
[[nodiscard]] rle_size_t GetBatch(value_type* out, rle_size_t batch_size,
364+
rle_size_t value_bit_width) {
357365
if (ARROW_PREDICT_FALSE(remaining_count_ == 0)) {
358366
return 0;
359367
}
360368

361369
const auto to_read = std::min(remaining_count_, batch_size);
362-
const auto actual_read = bit_reader_.GetBatch(value_bit_width_, out, to_read);
370+
const auto actual_read = bit_reader_.GetBatch(value_bit_width, out, to_read);
363371
// There should not be any reason why the actual read would be different
364372
// but this is error resistant.
365373
remaining_count_ -= actual_read;
@@ -368,7 +376,6 @@ class BitPackedRunDecoder {
368376

369377
private:
370378
::arrow::bit_util::BitReader bit_reader_ = {};
371-
rle_size_t value_bit_width_ = 0;
372379
rle_size_t remaining_count_ = 0;
373380

374381
static_assert(std::is_integral_v<value_type>,
@@ -400,6 +407,7 @@ class RleBitPackedDecoder {
400407
ARROW_DCHECK_LE(value_bit_width, 64);
401408
parser_.Reset(data, data_size, value_bit_width);
402409
decoder_ = {};
410+
value_bit_width_ = value_bit_width;
403411
}
404412

405413
/// Whether there is still runs to iterate over.
@@ -454,6 +462,7 @@ class RleBitPackedDecoder {
454462
private:
455463
RleBitPackedParser parser_ = {};
456464
std::variant<RleRunDecoder<value_type>, BitPackedRunDecoder<value_type>> decoder_ = {};
465+
rle_size_t value_bit_width_;
457466

458467
/// Return the number of values that are remaining in the current run.
459468
rle_size_t run_remaining() const {
@@ -462,7 +471,9 @@ class RleBitPackedDecoder {
462471

463472
/// Get a batch of values from the current run and return the number elements read.
464473
[[nodiscard]] rle_size_t RunGetBatch(value_type* out, rle_size_t batch_size) {
465-
return std::visit([&](auto& dec) { return dec.GetBatch(out, batch_size); }, decoder_);
474+
return std::visit(
475+
[&](auto& dec) { return dec.GetBatch(out, batch_size, value_bit_width_); },
476+
decoder_);
466477
}
467478

468479
/// Call the parser with a single callable for all event types.
@@ -734,8 +745,8 @@ auto RleBitPackedDecoder<T>::GetBatch(value_type* out, rle_size_t batch_size)
734745
using RunDecoder = typename decltype(run)::template DecoderType<value_type>;
735746

736747
ARROW_DCHECK_LT(values_read, batch_size);
737-
RunDecoder decoder(run);
738-
const auto read = decoder.GetBatch(out, batch_size - values_read);
748+
RunDecoder decoder(run, value_bit_width_);
749+
const auto read = decoder.GetBatch(out, batch_size - values_read, value_bit_width_);
739750
ARROW_DCHECK_LE(read, batch_size - values_read);
740751
values_read += read;
741752
out += read;
@@ -824,8 +835,9 @@ struct GetSpacedResult {
824835
template <typename Converter, typename BitRunReader, typename BitRun, typename value_type>
825836
auto RunGetSpaced(Converter* converter, typename Converter::out_type* out,
826837
rle_size_t batch_size, rle_size_t null_count,
827-
BitRunReader* validity_reader, BitRun* validity_run,
828-
RleRunDecoder<value_type>* decoder) -> GetSpacedResult<rle_size_t> {
838+
rle_size_t value_bit_width, BitRunReader* validity_reader,
839+
BitRun* validity_run, RleRunDecoder<value_type>* decoder)
840+
-> GetSpacedResult<rle_size_t> {
829841
ARROW_DCHECK_GT(batch_size, 0);
830842
// The equality case is handled in the main loop in GetSpaced
831843
ARROW_DCHECK_LT(null_count, batch_size);
@@ -873,7 +885,7 @@ auto RunGetSpaced(Converter* converter, typename Converter::out_type* out,
873885
return {0, 0};
874886
}
875887
converter->WriteRepeated(out, out + batch.total_read(), value);
876-
const auto actual_values_read = decoder->Advance(batch.values_read());
888+
const auto actual_values_read = decoder->Advance(batch.values_read(), value_bit_width);
877889
// We always cropped the number of values_read by the remaining values in the run.
878890
// What's more the RLE decoder should not encounter any errors.
879891
ARROW_DCHECK_EQ(actual_values_read, batch.values_read());
@@ -884,8 +896,8 @@ auto RunGetSpaced(Converter* converter, typename Converter::out_type* out,
884896
template <typename Converter, typename BitRunReader, typename BitRun, typename value_type>
885897
auto RunGetSpaced(Converter* converter, typename Converter::out_type* out,
886898
rle_size_t batch_size, rle_size_t null_count,
887-
BitRunReader* validity_reader, BitRun* validity_run,
888-
BitPackedRunDecoder<value_type>* decoder)
899+
rle_size_t value_bit_width, BitRunReader* validity_reader,
900+
BitRun* validity_run, BitPackedRunDecoder<value_type>* decoder)
889901
-> GetSpacedResult<rle_size_t> {
890902
ARROW_DCHECK_GT(batch_size, 0);
891903
// The equality case is handled in the main loop in GetSpaced
@@ -920,7 +932,7 @@ auto RunGetSpaced(Converter* converter, typename Converter::out_type* out,
920932
// buffer_start is 0 at this point so size is end
921933
buffer_end = std::min(std::min(run_values_remaining(), batch.values_remaining()),
922934
kBufferCapacity);
923-
buffer_end = decoder->GetBatch(buffer.data(), buffer_size());
935+
buffer_end = decoder->GetBatch(buffer.data(), buffer_size(), value_bit_width);
924936
ARROW_DCHECK_LE(buffer_size(), kBufferCapacity);
925937

926938
if (ARROW_PREDICT_FALSE(!converter->InputIsValid(buffer.data(), buffer_size()))) {
@@ -972,14 +984,15 @@ auto RunGetSpaced(Converter* converter, typename Converter::out_type* out,
972984
template <typename Converter, typename BitRunReader, typename BitRun, typename value_type>
973985
auto RunGetSpaced(
974986
Converter* converter, typename Converter::out_type* out, rle_size_t batch_size,
975-
rle_size_t null_count, BitRunReader* validity_reader, BitRun* validity_run,
987+
rle_size_t null_count, rle_size_t value_bit_width, BitRunReader* validity_reader,
988+
BitRun* validity_run,
976989
std::variant<RleRunDecoder<value_type>, BitPackedRunDecoder<value_type>>* decoder)
977990
-> GetSpacedResult<rle_size_t> {
978991
return std::visit(
979992
[&](auto& dec) {
980993
ARROW_DCHECK_GT(dec.remaining(), 0);
981-
return RunGetSpaced(converter, out, batch_size, null_count, validity_reader,
982-
validity_run, &dec);
994+
return RunGetSpaced(converter, out, batch_size, null_count, value_bit_width,
995+
validity_reader, validity_run, &dec);
983996
},
984997
*decoder);
985998
}
@@ -1023,8 +1036,8 @@ auto RleBitPackedDecoder<T>::GetSpaced(Converter converter,
10231036
// Remaining from a previous call that would have left some unread data from a run.
10241037
if (ARROW_PREDICT_FALSE(run_remaining() > 0)) {
10251038
const auto read = internal::RunGetSpaced(&converter, out, batch.total_remaining(),
1026-
batch.null_remaining(), &validity_reader,
1027-
&validity_run, &decoder_);
1039+
batch.null_remaining(), value_bit_width_,
1040+
&validity_reader, &validity_run, &decoder_);
10281041

10291042
batch.AccrueReadNulls(read.null_read);
10301043
batch.AccrueReadValues(read.values_read);
@@ -1044,11 +1057,11 @@ auto RleBitPackedDecoder<T>::GetSpaced(Converter converter,
10441057
ParseWithCallable([&](auto run) {
10451058
using RunDecoder = typename decltype(run)::template DecoderType<value_type>;
10461059

1047-
RunDecoder decoder(run);
1060+
RunDecoder decoder(run, value_bit_width_);
10481061

10491062
const auto read = internal::RunGetSpaced(&converter, out, batch.total_remaining(),
1050-
batch.null_remaining(), &validity_reader,
1051-
&validity_run, &decoder);
1063+
batch.null_remaining(), value_bit_width_,
1064+
&validity_reader, &validity_run, &decoder);
10521065

10531066
batch.AccrueReadNulls(read.null_read);
10541067
batch.AccrueReadValues(read.values_read);
@@ -1205,9 +1218,9 @@ auto RleBitPackedDecoder<T>::GetBatchWithDict(const V* dictionary,
12051218
};
12061219

12071220
if (ARROW_PREDICT_FALSE(run_remaining() > 0)) {
1208-
const auto read =
1209-
internal::RunGetSpaced(&converter, out, batch_size, /* null_count= */ 0,
1210-
&validity_reader, &validity_run, &decoder_);
1221+
const auto read = internal::RunGetSpaced(&converter, out, batch_size,
1222+
/* null_count= */ 0, value_bit_width_,
1223+
&validity_reader, &validity_run, &decoder_);
12111224

12121225
ARROW_DCHECK_EQ(read.null_read, 0);
12131226
values_read += read.values_read;
@@ -1226,11 +1239,11 @@ auto RleBitPackedDecoder<T>::GetBatchWithDict(const V* dictionary,
12261239
ParseWithCallable([&](auto run) {
12271240
using RunDecoder = typename decltype(run)::template DecoderType<value_type>;
12281241

1229-
RunDecoder decoder(run);
1242+
RunDecoder decoder(run, value_bit_width_);
12301243

12311244
const auto read = internal::RunGetSpaced(&converter, out, batch_values_remaining(),
1232-
/* null_count= */ 0, &validity_reader,
1233-
&validity_run, &decoder);
1245+
/* null_count= */ 0, value_bit_width_,
1246+
&validity_reader, &validity_run, &decoder);
12341247

12351248
ARROW_DCHECK_EQ(read.null_read, 0);
12361249
values_read += read.values_read;

0 commit comments

Comments
 (0)