This is an automated email from the ASF dual-hosted git repository.
apitrou pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push:
new 728185119b GH-38542: [C++][Parquet] Faster scalar BYTE_STREAM_SPLIT
(#38529)
728185119b is described below
commit 728185119b1d21860d0803752f92d17a6b92b443
Author: Antoine Pitrou <[email protected]>
AuthorDate: Thu Nov 2 10:07:06 2023 +0100
GH-38542: [C++][Parquet] Faster scalar BYTE_STREAM_SPLIT (#38529)
### Rationale for this change
BYTE_STREAM_SPLIT encoding and decoding benefit from SIMD accelerations on
x86, but scalar implementations are used otherwise.
### What changes are included in this PR?
Improve the speed of scalar implementations of BYTE_STREAM_SPLIT by using a
blocked algorithm.
Benchmark numbers on the author's machine (AMD Ryzen 9 3900X):
* before:
```
-------------------------------------------------------------------------------------------------------
Benchmark Time CPU
Iterations UserCounters...
-------------------------------------------------------------------------------------------------------
BM_ByteStreamSplitDecode_Float_Scalar/1024 1374 ns 1374 ns
510396 bytes_per_second=2.77574G/s
BM_ByteStreamSplitDecode_Float_Scalar/4096 5483 ns 5483 ns
127498 bytes_per_second=2.78303G/s
BM_ByteStreamSplitDecode_Float_Scalar/32768 44042 ns 44035 ns
15905 bytes_per_second=2.77212G/s
BM_ByteStreamSplitDecode_Float_Scalar/65536 87966 ns 87952 ns
7962 bytes_per_second=2.77583G/s
BM_ByteStreamSplitDecode_Double_Scalar/1024 2583 ns 2583 ns
271436 bytes_per_second=2.95408G/s
BM_ByteStreamSplitDecode_Double_Scalar/4096 10533 ns 10532 ns
65695 bytes_per_second=2.89761G/s
BM_ByteStreamSplitDecode_Double_Scalar/32768 84067 ns 84053 ns
8275 bytes_per_second=2.90459G/s
BM_ByteStreamSplitDecode_Double_Scalar/65536 168332 ns 168309 ns
4155 bytes_per_second=2.9011G/s
BM_ByteStreamSplitEncode_Float_Scalar/1024 1435 ns 1435 ns
484278 bytes_per_second=2.65802G/s
BM_ByteStreamSplitEncode_Float_Scalar/4096 5725 ns 5725 ns
121877 bytes_per_second=2.66545G/s
BM_ByteStreamSplitEncode_Float_Scalar/32768 46291 ns 46283 ns
15134 bytes_per_second=2.63745G/s
BM_ByteStreamSplitEncode_Float_Scalar/65536 91139 ns 91128 ns
7707 bytes_per_second=2.6791G/s
BM_ByteStreamSplitEncode_Double_Scalar/1024 3093 ns 3093 ns
226198 bytes_per_second=2.46663G/s
BM_ByteStreamSplitEncode_Double_Scalar/4096 12724 ns 12722 ns
54522 bytes_per_second=2.39873G/s
BM_ByteStreamSplitEncode_Double_Scalar/32768 100488 ns 100475 ns
6957 bytes_per_second=2.42987G/s
BM_ByteStreamSplitEncode_Double_Scalar/65536 200885 ns 200852 ns
3486 bytes_per_second=2.43105G/s
```
* after:
```
-------------------------------------------------------------------------------------------------------
Benchmark Time CPU
Iterations UserCounters...
-------------------------------------------------------------------------------------------------------
BM_ByteStreamSplitDecode_Float_Scalar/1024 932 ns 932 ns
753352 bytes_per_second=4.09273G/s
BM_ByteStreamSplitDecode_Float_Scalar/4096 3715 ns 3715 ns
188394 bytes_per_second=4.10783G/s
BM_ByteStreamSplitDecode_Float_Scalar/32768 30167 ns 30162 ns
23441 bytes_per_second=4.04716G/s
BM_ByteStreamSplitDecode_Float_Scalar/65536 59483 ns 59475 ns
11744 bytes_per_second=4.10496G/s
BM_ByteStreamSplitDecode_Double_Scalar/1024 1862 ns 1862 ns
374715 bytes_per_second=4.09823G/s
BM_ByteStreamSplitDecode_Double_Scalar/4096 7554 ns 7553 ns
91975 bytes_per_second=4.04038G/s
BM_ByteStreamSplitDecode_Double_Scalar/32768 60429 ns 60421 ns
11499 bytes_per_second=4.04067G/s
BM_ByteStreamSplitDecode_Double_Scalar/65536 120992 ns 120972 ns
5756 bytes_per_second=4.03631G/s
BM_ByteStreamSplitEncode_Float_Scalar/1024 737 ns 737 ns
947423 bytes_per_second=5.17843G/s
BM_ByteStreamSplitEncode_Float_Scalar/4096 2934 ns 2933 ns
239459 bytes_per_second=5.20175G/s
BM_ByteStreamSplitEncode_Float_Scalar/32768 23730 ns 23727 ns
29243 bytes_per_second=5.14485G/s
BM_ByteStreamSplitEncode_Float_Scalar/65536 47671 ns 47664 ns
14682 bytes_per_second=5.12209G/s
BM_ByteStreamSplitEncode_Double_Scalar/1024 1517 ns 1517 ns
458928 bytes_per_second=5.02827G/s
BM_ByteStreamSplitEncode_Double_Scalar/4096 6224 ns 6223 ns
111361 bytes_per_second=4.90407G/s
BM_ByteStreamSplitEncode_Double_Scalar/32768 49719 ns 49713 ns
14059 bytes_per_second=4.91099G/s
BM_ByteStreamSplitEncode_Double_Scalar/65536 99445 ns 99432 ns
7027 bytes_per_second=4.91072G/s
```
### Are these changes tested?
Yes, though the scalar implementations are unfortunately only exercised on
non-x86 by default (see added comment in the PR).
### Are there any user-facing changes?
No.
* Closes: #38542
Authored-by: Antoine Pitrou <[email protected]>
Signed-off-by: Antoine Pitrou <[email protected]>
---
cpp/src/arrow/util/CMakeLists.txt | 1 +
...stream_split.h => byte_stream_split_internal.h} | 148 ++++++++++++++----
cpp/src/arrow/util/byte_stream_split_test.cc | 172 +++++++++++++++++++++
cpp/src/parquet/encoding.cc | 6 +-
cpp/src/parquet/encoding_benchmark.cc | 2 +-
5 files changed, 298 insertions(+), 31 deletions(-)
diff --git a/cpp/src/arrow/util/CMakeLists.txt
b/cpp/src/arrow/util/CMakeLists.txt
index 3cecab3a63..3dc8eac1ab 100644
--- a/cpp/src/arrow/util/CMakeLists.txt
+++ b/cpp/src/arrow/util/CMakeLists.txt
@@ -43,6 +43,7 @@ add_arrow_test(utility-test
align_util_test.cc
atfork_test.cc
byte_size_test.cc
+ byte_stream_split_test.cc
cache_test.cc
checked_cast_test.cc
compression_test.cc
diff --git a/cpp/src/arrow/util/byte_stream_split.h
b/cpp/src/arrow/util/byte_stream_split_internal.h
similarity index 84%
rename from cpp/src/arrow/util/byte_stream_split.h
rename to cpp/src/arrow/util/byte_stream_split_internal.h
index d428df0659..ae85e2cfa8 100644
--- a/cpp/src/arrow/util/byte_stream_split.h
+++ b/cpp/src/arrow/util/byte_stream_split_internal.h
@@ -17,20 +17,24 @@
#pragma once
+#include "arrow/util/endian.h"
#include "arrow/util/simd.h"
#include "arrow/util/ubsan.h"
-#include <stdint.h>
#include <algorithm>
+#include <cassert>
+#include <cstdint>
#ifdef ARROW_HAVE_SSE4_2
// Enable the SIMD for ByteStreamSplit Encoder/Decoder
#define ARROW_HAVE_SIMD_SPLIT
#endif // ARROW_HAVE_SSE4_2
-namespace arrow {
-namespace util {
-namespace internal {
+namespace arrow::util::internal {
+
+//
+// SIMD implementations
+//
#if defined(ARROW_HAVE_SSE4_2)
template <typename T>
@@ -565,48 +569,140 @@ void inline ByteStreamSplitDecodeSimd(const uint8_t*
data, int64_t num_values,
}
template <typename T>
-void inline ByteStreamSplitEncodeSimd(const uint8_t* raw_values, const size_t
num_values,
+void inline ByteStreamSplitEncodeSimd(const uint8_t* raw_values, const int64_t
num_values,
uint8_t* output_buffer_raw) {
#if defined(ARROW_HAVE_AVX512)
- return ByteStreamSplitEncodeAvx512<T>(raw_values, num_values,
output_buffer_raw);
+ return ByteStreamSplitEncodeAvx512<T>(raw_values,
static_cast<size_t>(num_values),
+ output_buffer_raw);
#elif defined(ARROW_HAVE_AVX2)
- return ByteStreamSplitEncodeAvx2<T>(raw_values, num_values,
output_buffer_raw);
+ return ByteStreamSplitEncodeAvx2<T>(raw_values,
static_cast<size_t>(num_values),
+ output_buffer_raw);
#elif defined(ARROW_HAVE_SSE4_2)
- return ByteStreamSplitEncodeSse2<T>(raw_values, num_values,
output_buffer_raw);
+ return ByteStreamSplitEncodeSse2<T>(raw_values,
static_cast<size_t>(num_values),
+ output_buffer_raw);
#else
#error "ByteStreamSplitEncodeSimd not implemented"
#endif
}
#endif
+//
+// Scalar implementations
+//
+
+inline void DoSplitStreams(const uint8_t* src, int width, int64_t nvalues,
+ uint8_t** dest_streams) {
+ // Value empirically chosen to provide the best performance on the author's
machine
+ constexpr int kBlockSize = 32;
+
+ while (nvalues >= kBlockSize) {
+ for (int stream = 0; stream < width; ++stream) {
+ uint8_t* dest = dest_streams[stream];
+ for (int i = 0; i < kBlockSize; i += 8) {
+ uint64_t a = src[stream + i * width];
+ uint64_t b = src[stream + (i + 1) * width];
+ uint64_t c = src[stream + (i + 2) * width];
+ uint64_t d = src[stream + (i + 3) * width];
+ uint64_t e = src[stream + (i + 4) * width];
+ uint64_t f = src[stream + (i + 5) * width];
+ uint64_t g = src[stream + (i + 6) * width];
+ uint64_t h = src[stream + (i + 7) * width];
+#if ARROW_LITTLE_ENDIAN
+ uint64_t r = a | (b << 8) | (c << 16) | (d << 24) | (e << 32) | (f <<
40) |
+ (g << 48) | (h << 56);
+#else
+ uint64_t r = (a << 56) | (b << 48) | (c << 40) | (d << 32) | (e << 24)
|
+ (f << 16) | (g << 8) | h;
+#endif
+ arrow::util::SafeStore(&dest[i], r);
+ }
+ dest_streams[stream] += kBlockSize;
+ }
+ src += width * kBlockSize;
+ nvalues -= kBlockSize;
+ }
+
+ // Epilog
+ for (int stream = 0; stream < width; ++stream) {
+ uint8_t* dest = dest_streams[stream];
+ for (int64_t i = 0; i < nvalues; ++i) {
+ dest[i] = src[stream + i * width];
+ }
+ }
+}
+
+inline void DoMergeStreams(const uint8_t** src_streams, int width, int64_t
nvalues,
+ uint8_t* dest) {
+ // Value empirically chosen to provide the best performance on the author's
machine
+ constexpr int kBlockSize = 128;
+
+ while (nvalues >= kBlockSize) {
+ for (int stream = 0; stream < width; ++stream) {
+ // Take kBlockSize bytes from the given stream and spread them
+ // to their logical places in destination.
+ const uint8_t* src = src_streams[stream];
+ for (int i = 0; i < kBlockSize; i += 8) {
+ uint64_t v = arrow::util::SafeLoadAs<uint64_t>(&src[i]);
+#if ARROW_LITTLE_ENDIAN
+ dest[stream + i * width] = static_cast<uint8_t>(v);
+ dest[stream + (i + 1) * width] = static_cast<uint8_t>(v >> 8);
+ dest[stream + (i + 2) * width] = static_cast<uint8_t>(v >> 16);
+ dest[stream + (i + 3) * width] = static_cast<uint8_t>(v >> 24);
+ dest[stream + (i + 4) * width] = static_cast<uint8_t>(v >> 32);
+ dest[stream + (i + 5) * width] = static_cast<uint8_t>(v >> 40);
+ dest[stream + (i + 6) * width] = static_cast<uint8_t>(v >> 48);
+ dest[stream + (i + 7) * width] = static_cast<uint8_t>(v >> 56);
+#else
+ dest[stream + i * width] = static_cast<uint8_t>(v >> 56);
+ dest[stream + (i + 1) * width] = static_cast<uint8_t>(v >> 48);
+ dest[stream + (i + 2) * width] = static_cast<uint8_t>(v >> 40);
+ dest[stream + (i + 3) * width] = static_cast<uint8_t>(v >> 32);
+ dest[stream + (i + 4) * width] = static_cast<uint8_t>(v >> 24);
+ dest[stream + (i + 5) * width] = static_cast<uint8_t>(v >> 16);
+ dest[stream + (i + 6) * width] = static_cast<uint8_t>(v >> 8);
+ dest[stream + (i + 7) * width] = static_cast<uint8_t>(v);
+#endif
+ }
+ src_streams[stream] += kBlockSize;
+ }
+ dest += width * kBlockSize;
+ nvalues -= kBlockSize;
+ }
+
+ // Epilog
+ for (int stream = 0; stream < width; ++stream) {
+ const uint8_t* src = src_streams[stream];
+ for (int64_t i = 0; i < nvalues; ++i) {
+ dest[stream + i * width] = src[i];
+ }
+ }
+}
+
template <typename T>
-void ByteStreamSplitEncodeScalar(const uint8_t* raw_values, const size_t
num_values,
+void ByteStreamSplitEncodeScalar(const uint8_t* raw_values, const int64_t
num_values,
uint8_t* output_buffer_raw) {
- constexpr size_t kNumStreams = sizeof(T);
- for (size_t i = 0U; i < num_values; ++i) {
- for (size_t j = 0U; j < kNumStreams; ++j) {
- const uint8_t byte_in_value = raw_values[i * kNumStreams + j];
- output_buffer_raw[j * num_values + i] = byte_in_value;
- }
+ constexpr int kNumStreams = static_cast<int>(sizeof(T));
+ std::array<uint8_t*, kNumStreams> dest_streams;
+ for (int stream = 0; stream < kNumStreams; ++stream) {
+ dest_streams[stream] = &output_buffer_raw[stream * num_values];
}
+ DoSplitStreams(raw_values, kNumStreams, num_values, dest_streams.data());
}
template <typename T>
void ByteStreamSplitDecodeScalar(const uint8_t* data, int64_t num_values,
int64_t stride,
T* out) {
- constexpr size_t kNumStreams = sizeof(T);
- auto output_buffer_raw = reinterpret_cast<uint8_t*>(out);
-
- for (int64_t i = 0; i < num_values; ++i) {
- for (size_t b = 0; b < kNumStreams; ++b) {
- const size_t byte_index = b * stride + i;
- output_buffer_raw[i * kNumStreams + b] = data[byte_index];
- }
+ constexpr int kNumStreams = static_cast<int>(sizeof(T));
+ std::array<const uint8_t*, kNumStreams> src_streams;
+ for (int stream = 0; stream < kNumStreams; ++stream) {
+ src_streams[stream] = &data[stream * stride];
}
+ DoMergeStreams(src_streams.data(), kNumStreams, num_values,
+ reinterpret_cast<uint8_t*>(out));
}
template <typename T>
-void inline ByteStreamSplitEncode(const uint8_t* raw_values, const size_t
num_values,
+void inline ByteStreamSplitEncode(const uint8_t* raw_values, const int64_t
num_values,
uint8_t* output_buffer_raw) {
#if defined(ARROW_HAVE_SIMD_SPLIT)
return ByteStreamSplitEncodeSimd<T>(raw_values, num_values,
output_buffer_raw);
@@ -625,6 +721,4 @@ void inline ByteStreamSplitDecode(const uint8_t* data,
int64_t num_values, int64
#endif
}
-} // namespace internal
-} // namespace util
-} // namespace arrow
+} // namespace arrow::util::internal
diff --git a/cpp/src/arrow/util/byte_stream_split_test.cc
b/cpp/src/arrow/util/byte_stream_split_test.cc
new file mode 100644
index 0000000000..3ea27f57da
--- /dev/null
+++ b/cpp/src/arrow/util/byte_stream_split_test.cc
@@ -0,0 +1,172 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "arrow/testing/gtest_util.h"
+#include "arrow/testing/util.h"
+#include "arrow/util/byte_stream_split_internal.h"
+
+namespace arrow::util::internal {
+
+using ByteStreamSplitTypes = ::testing::Types<float, double>;
+
+template <typename Func>
+struct NamedFunc {
+ std::string name;
+ Func func;
+
+ friend std::ostream& operator<<(std::ostream& os, const NamedFunc& func) {
+ os << func.name;
+ return os;
+ }
+};
+
+// A simplistic reference implementation for validation
+void RefererenceByteStreamSplitEncode(const uint8_t* src, int width,
+ const int64_t num_values, uint8_t* dest)
{
+ for (int64_t i = 0; i < num_values; ++i) {
+ for (int stream = 0; stream < width; ++stream) {
+ dest[stream * num_values + i] = *src++;
+ }
+ }
+}
+
+template <typename T>
+class TestByteStreamSplitSpecialized : public ::testing::Test {
+ public:
+ using EncodeFunc =
NamedFunc<std::function<decltype(ByteStreamSplitEncode<T>)>>;
+ using DecodeFunc =
NamedFunc<std::function<decltype(ByteStreamSplitDecode<T>)>>;
+
+ static constexpr int kWidth = static_cast<int>(sizeof(T));
+
+ void SetUp() override {
+ encode_funcs_.push_back({"reference", &ReferenceEncode});
+ encode_funcs_.push_back({"scalar", &ByteStreamSplitEncodeScalar<T>});
+ decode_funcs_.push_back({"scalar", &ByteStreamSplitDecodeScalar<T>});
+#if defined(ARROW_HAVE_SIMD_SPLIT)
+ encode_funcs_.push_back({"simd", &ByteStreamSplitEncodeSimd<T>});
+ decode_funcs_.push_back({"simd", &ByteStreamSplitDecodeSimd<T>});
+#endif
+ }
+
+ void TestRoundtrip(int64_t num_values) {
+ // Test one-shot roundtrip among all encode/decode function combinations
+ ARROW_SCOPED_TRACE("num_values = ", num_values);
+ const auto input = MakeRandomInput(num_values);
+ std::vector<uint8_t> encoded(num_values * kWidth);
+ std::vector<T> decoded(num_values);
+
+ for (const auto& encode_func : encode_funcs_) {
+ ARROW_SCOPED_TRACE("encode_func = ", encode_func);
+ encoded.assign(encoded.size(), 0);
+ encode_func.func(reinterpret_cast<const uint8_t*>(input.data()),
num_values,
+ encoded.data());
+ for (const auto& decode_func : decode_funcs_) {
+ ARROW_SCOPED_TRACE("decode_func = ", decode_func);
+ decoded.assign(decoded.size(), T{});
+ decode_func.func(encoded.data(), num_values, /*stride=*/num_values,
+ decoded.data());
+ ASSERT_EQ(decoded, input);
+ }
+ }
+ }
+
+ void TestPiecewiseDecode(int64_t num_values) {
+ // Test chunked decoding against the reference encode function
+ ARROW_SCOPED_TRACE("num_values = ", num_values);
+ const auto input = MakeRandomInput(num_values);
+ std::vector<uint8_t> encoded(num_values * kWidth);
+ ReferenceEncode(reinterpret_cast<const uint8_t*>(input.data()), num_values,
+ encoded.data());
+ std::vector<T> decoded(num_values);
+
+ std::default_random_engine gen(seed_++);
+ std::uniform_int_distribution<int64_t> chunk_size_dist(1, 123);
+
+ for (const auto& decode_func : decode_funcs_) {
+ ARROW_SCOPED_TRACE("decode_func = ", decode_func);
+ decoded.assign(decoded.size(), T{});
+
+ int64_t offset = 0;
+ while (offset < num_values) {
+ auto chunk_size = std::min<int64_t>(num_values - offset,
chunk_size_dist(gen));
+ decode_func.func(encoded.data() + offset, chunk_size,
/*stride=*/num_values,
+ decoded.data() + offset);
+ offset += chunk_size;
+ }
+ ASSERT_EQ(offset, num_values);
+ ASSERT_EQ(decoded, input);
+ }
+ }
+
+ protected:
+ static void ReferenceEncode(const uint8_t* raw_values, const int64_t
num_values,
+ uint8_t* output_buffer_raw) {
+ RefererenceByteStreamSplitEncode(raw_values, kWidth, num_values,
output_buffer_raw);
+ }
+
+ static std::vector<T> MakeRandomInput(int64_t num_values) {
+ std::vector<T> input(num_values);
+ random_bytes(kWidth * num_values, seed_++,
reinterpret_cast<uint8_t*>(input.data()));
+ // Avoid NaNs to ease comparison
+ for (auto& value : input) {
+ if (std::isnan(value)) {
+ value = nan_replacement_++;
+ }
+ }
+ return input;
+ }
+
+ std::vector<EncodeFunc> encode_funcs_;
+ std::vector<DecodeFunc> decode_funcs_;
+
+ static inline uint32_t seed_ = 42;
+ static inline T nan_replacement_ = 0;
+};
+
+TYPED_TEST_SUITE(TestByteStreamSplitSpecialized, ByteStreamSplitTypes);
+
+TYPED_TEST(TestByteStreamSplitSpecialized, RoundtripSmall) {
+ for (int64_t num_values : {1, 5, 7, 12, 19, 31, 32}) {
+ this->TestRoundtrip(num_values);
+ }
+}
+
+TYPED_TEST(TestByteStreamSplitSpecialized, RoundtripMidsized) {
+ for (int64_t num_values : {126, 127, 128, 129, 133, 200}) {
+ this->TestRoundtrip(num_values);
+ }
+}
+
+TYPED_TEST(TestByteStreamSplitSpecialized, PiecewiseDecode) {
+ this->TestPiecewiseDecode(/*num_values=*/500);
+}
+
+} // namespace arrow::util::internal
diff --git a/cpp/src/parquet/encoding.cc b/cpp/src/parquet/encoding.cc
index 5221f2588c..1bb487c20d 100644
--- a/cpp/src/parquet/encoding.cc
+++ b/cpp/src/parquet/encoding.cc
@@ -37,7 +37,7 @@
#include "arrow/util/bit_util.h"
#include "arrow/util/bitmap_ops.h"
#include "arrow/util/bitmap_writer.h"
-#include "arrow/util/byte_stream_split.h"
+#include "arrow/util/byte_stream_split_internal.h"
#include "arrow/util/checked_cast.h"
#include "arrow/util/hashing.h"
#include "arrow/util/int_util_overflow.h"
@@ -850,8 +850,8 @@ std::shared_ptr<Buffer>
ByteStreamSplitEncoder<DType>::FlushValues() {
AllocateBuffer(this->memory_pool(), EstimatedDataEncodedSize());
uint8_t* output_buffer_raw = output_buffer->mutable_data();
const uint8_t* raw_values = sink_.data();
- ::arrow::util::internal::ByteStreamSplitEncode<T>(
- raw_values, static_cast<size_t>(num_values_in_buffer_),
output_buffer_raw);
+ ::arrow::util::internal::ByteStreamSplitEncode<T>(raw_values,
num_values_in_buffer_,
+ output_buffer_raw);
sink_.Reset();
num_values_in_buffer_ = 0;
return std::move(output_buffer);
diff --git a/cpp/src/parquet/encoding_benchmark.cc
b/cpp/src/parquet/encoding_benchmark.cc
index 717c716330..b5b6cc8d93 100644
--- a/cpp/src/parquet/encoding_benchmark.cc
+++ b/cpp/src/parquet/encoding_benchmark.cc
@@ -24,7 +24,7 @@
#include "arrow/testing/random.h"
#include "arrow/testing/util.h"
#include "arrow/type.h"
-#include "arrow/util/byte_stream_split.h"
+#include "arrow/util/byte_stream_split_internal.h"
#include "arrow/visit_data_inline.h"
#include "parquet/encoding.h"