3838
3939#include " benchmark/benchmark.h"
4040
41+ #include " arrow/util/bit_util.h"
4142#include " arrow/util/compression.h"
4243#include " arrow/util/logging.h"
4344#include " arrow/util/pfor/pfor_wrapper_internal.h"
@@ -63,6 +64,13 @@ using GenT = std::vector<T> (*)(int64_t);
6364using Gen32 = GenT<int32_t >;
6465using Gen64 = GenT<int64_t >;
6566
67+ // Payload used when a PFOR vector is placed in delta mode. Both layouts are
68+ // independently restarted at every 1024-value PFOR vector boundary.
69+ enum class PforDeltaLayout {
70+ kPatchedDelta ,
71+ kDeltaBinaryPacked ,
72+ };
73+
6674// Map the C++ value type to its Parquet physical type + descriptor type.
6775template <typename T>
6876struct PqTraits ;
@@ -663,7 +671,8 @@ static std::shared_ptr<ColumnDescriptor> MakeDescriptor() {
663671// PFOR Encode/Decode
664672
665673template <typename T>
666- static void PforEncodeImpl (benchmark::State& state, GenT<T> gen) {
674+ static void PforEncodeImpl (benchmark::State& state, GenT<T> gen,
675+ const ::arrow::util::pfor::PforEncodeOptions& options = {}) {
667676 const int64_t num_values = state.range (0 );
668677 auto values = gen (num_values);
669678 const int64_t uncompressed_size = num_values * sizeof (T);
@@ -676,12 +685,14 @@ static void PforEncodeImpl(benchmark::State& state, GenT<T> gen) {
676685 // Compute comp_size once for the counter
677686 int64_t comp_size = max_size;
678687 ARROW_CHECK_OK (::arrow::util::pfor::PforWrapper<T>::Encode (
679- values.data (), static_cast <int32_t >(num_values), compressed.data (), &comp_size));
688+ values.data (), static_cast <int32_t >(num_values), compressed.data (), &comp_size,
689+ options));
680690
681691 for (auto _ : state) {
682692 int64_t sz = max_size;
683693 ARROW_CHECK_OK (::arrow::util::pfor::PforWrapper<T>::Encode (
684- values.data (), static_cast <int32_t >(num_values), compressed.data (), &sz));
694+ values.data (), static_cast <int32_t >(num_values), compressed.data (), &sz,
695+ options));
685696 benchmark::DoNotOptimize (sz);
686697 benchmark::ClobberMemory ();
687698 }
@@ -697,9 +708,16 @@ static void BM_PforEncode(benchmark::State& state, Gen32 gen) {
697708static void BM_Pfor64Encode (benchmark::State& state, Gen64 gen) {
698709 PforEncodeImpl<int64_t >(state, gen);
699710}
711+ static void BM_PforRawEncode (benchmark::State& state, Gen32 gen) {
712+ PforEncodeImpl<int32_t >(state, gen, {.delta_enabled = false , .force_delta = false });
713+ }
714+ static void BM_PforRaw64Encode (benchmark::State& state, Gen64 gen) {
715+ PforEncodeImpl<int64_t >(state, gen, {.delta_enabled = false , .force_delta = false });
716+ }
700717
701718template <typename T>
702- static void PforDecodeImpl (benchmark::State& state, GenT<T> gen) {
719+ static void PforDecodeImpl (benchmark::State& state, GenT<T> gen,
720+ const ::arrow::util::pfor::PforEncodeOptions& options = {}) {
703721 const int64_t num_values = state.range (0 );
704722 auto values = gen (num_values);
705723 const int64_t uncompressed_size = num_values * sizeof (T);
@@ -710,7 +728,8 @@ static void PforDecodeImpl(benchmark::State& state, GenT<T> gen) {
710728 std::vector<uint8_t > compressed (max_size);
711729 int64_t comp_size = max_size;
712730 ARROW_CHECK_OK (::arrow::util::pfor::PforWrapper<T>::Encode (
713- values.data (), static_cast <int32_t >(num_values), compressed.data (), &comp_size));
731+ values.data (), static_cast <int32_t >(num_values), compressed.data (), &comp_size,
732+ options));
714733
715734 std::vector<T> decoded (num_values);
716735 for (auto _ : state) {
@@ -732,6 +751,134 @@ static void BM_Pfor64Decode(benchmark::State& state, Gen64 gen) {
732751 PforDecodeImpl<int64_t >(state, gen);
733752}
734753
754+ // ----------------------------------------------------------------------
755+ // PFOR-DBPDelta Encode/Decode
756+
757+ // Model the alternative wire layout directly: the outer PFOR page keeps its
758+ // seven-byte header and one four-byte offset per vector, while each vector body
759+ // is an independently decodable DELTA_BINARY_PACKED stream. Consequently DBP's
760+ // first value and block header restart at every PFOR vector boundary.
761+ template <typename T>
762+ static int64_t PforDbpOuterBytes (int64_t num_values) {
763+ constexpr int64_t kVectorSize = ::arrow::util::pfor::PforConstants::kPforVectorSize ;
764+ const int64_t num_vectors = ::arrow::bit_util::CeilDiv (num_values, kVectorSize );
765+ return ::arrow::util::pfor::PforConstants::kHeaderSize +
766+ num_vectors * sizeof (::arrow::util::pfor::PforConstants::OffsetType);
767+ }
768+
769+ template <typename T>
770+ static void PforDbpDeltaEncodeImpl (benchmark::State& state, GenT<T> gen) {
771+ using PType = typename PqTraits<T>::PType;
772+ constexpr int64_t kVectorSize = ::arrow::util::pfor::PforConstants::kPforVectorSize ;
773+ const int64_t num_values = state.range (0 );
774+ auto values = gen (num_values);
775+ const int64_t uncompressed_size = num_values * sizeof (T);
776+ auto encoder = MakeTypedEncoder<PType>(Encoding::DELTA_BINARY_PACKED );
777+
778+ int64_t comp_size = PforDbpOuterBytes<T>(num_values);
779+ for (int64_t start = 0 ; start < num_values; start += kVectorSize ) {
780+ const int count = static_cast <int >(std::min (kVectorSize , num_values - start));
781+ encoder->Put (values.data () + start, count);
782+ comp_size += encoder->FlushValues ()->size ();
783+ }
784+
785+ for (auto _ : state) {
786+ int64_t encoded_bytes = PforDbpOuterBytes<T>(num_values);
787+ for (int64_t start = 0 ; start < num_values; start += kVectorSize ) {
788+ const int count = static_cast <int >(std::min (kVectorSize , num_values - start));
789+ encoder->Put (values.data () + start, count);
790+ encoded_bytes += encoder->FlushValues ()->size ();
791+ }
792+ benchmark::DoNotOptimize (encoded_bytes);
793+ }
794+
795+ state.SetBytesProcessed (state.iterations () * uncompressed_size);
796+ state.SetItemsProcessed (state.iterations () * num_values);
797+ state.counters [" compression_ratio" ] =
798+ static_cast <double >(uncompressed_size) / static_cast <double >(comp_size);
799+ }
800+
801+ template <typename T>
802+ static void PforDbpDeltaDecodeImpl (benchmark::State& state, GenT<T> gen) {
803+ using PType = typename PqTraits<T>::PType;
804+ constexpr int64_t kVectorSize = ::arrow::util::pfor::PforConstants::kPforVectorSize ;
805+ const int64_t num_values = state.range (0 );
806+ auto values = gen (num_values);
807+ const int64_t uncompressed_size = num_values * sizeof (T);
808+
809+ auto encoder = MakeTypedEncoder<PType>(Encoding::DELTA_BINARY_PACKED );
810+ std::vector<std::shared_ptr<Buffer>> vectors;
811+ int64_t comp_size = PforDbpOuterBytes<T>(num_values);
812+ for (int64_t start = 0 ; start < num_values; start += kVectorSize ) {
813+ const int count = static_cast <int >(std::min (kVectorSize , num_values - start));
814+ encoder->Put (values.data () + start, count);
815+ vectors.push_back (encoder->FlushValues ());
816+ comp_size += vectors.back ()->size ();
817+ }
818+
819+ auto decoder = MakeTypedDecoder<PType>(Encoding::DELTA_BINARY_PACKED );
820+ std::vector<T> decoded (num_values);
821+ for (auto _ : state) {
822+ int64_t start = 0 ;
823+ for (const auto & vector : vectors) {
824+ const int count = static_cast <int >(std::min (kVectorSize , num_values - start));
825+ decoder->SetData (count, vector->data (), static_cast <int >(vector->size ()));
826+ ARROW_CHECK_EQ (decoder->Decode (decoded.data () + start, count), count);
827+ start += count;
828+ }
829+ benchmark::ClobberMemory ();
830+ }
831+
832+ state.SetBytesProcessed (state.iterations () * uncompressed_size);
833+ state.SetItemsProcessed (state.iterations () * num_values);
834+ state.counters [" compression_ratio" ] =
835+ static_cast <double >(uncompressed_size) / static_cast <double >(comp_size);
836+ }
837+
838+ template <PforDeltaLayout Layout, typename T>
839+ static void PforDeltaLayoutEncodeImpl (benchmark::State& state, GenT<T> gen) {
840+ if constexpr (Layout == PforDeltaLayout::kPatchedDelta ) {
841+ PforEncodeImpl<T>(state, gen, {.delta_enabled = true , .force_delta = true });
842+ } else {
843+ PforDbpDeltaEncodeImpl<T>(state, gen);
844+ }
845+ }
846+
847+ template <PforDeltaLayout Layout, typename T>
848+ static void PforDeltaLayoutDecodeImpl (benchmark::State& state, GenT<T> gen) {
849+ if constexpr (Layout == PforDeltaLayout::kPatchedDelta ) {
850+ PforDecodeImpl<T>(state, gen, {.delta_enabled = true , .force_delta = true });
851+ } else {
852+ PforDbpDeltaDecodeImpl<T>(state, gen);
853+ }
854+ }
855+
856+ static void BM_PforPatchedDeltaEncode (benchmark::State& state, Gen32 gen) {
857+ PforDeltaLayoutEncodeImpl<PforDeltaLayout::kPatchedDelta , int32_t >(state, gen);
858+ }
859+ static void BM_PforPatchedDelta64Encode (benchmark::State& state, Gen64 gen) {
860+ PforDeltaLayoutEncodeImpl<PforDeltaLayout::kPatchedDelta , int64_t >(state, gen);
861+ }
862+ static void BM_PforPatchedDeltaDecode (benchmark::State& state, Gen32 gen) {
863+ PforDeltaLayoutDecodeImpl<PforDeltaLayout::kPatchedDelta , int32_t >(state, gen);
864+ }
865+ static void BM_PforPatchedDelta64Decode (benchmark::State& state, Gen64 gen) {
866+ PforDeltaLayoutDecodeImpl<PforDeltaLayout::kPatchedDelta , int64_t >(state, gen);
867+ }
868+
869+ static void BM_PforDbpDeltaEncode (benchmark::State& state, Gen32 gen) {
870+ PforDeltaLayoutEncodeImpl<PforDeltaLayout::kDeltaBinaryPacked , int32_t >(state, gen);
871+ }
872+ static void BM_PforDbpDelta64Encode (benchmark::State& state, Gen64 gen) {
873+ PforDeltaLayoutEncodeImpl<PforDeltaLayout::kDeltaBinaryPacked , int64_t >(state, gen);
874+ }
875+ static void BM_PforDbpDeltaDecode (benchmark::State& state, Gen32 gen) {
876+ PforDeltaLayoutDecodeImpl<PforDeltaLayout::kDeltaBinaryPacked , int32_t >(state, gen);
877+ }
878+ static void BM_PforDbpDelta64Decode (benchmark::State& state, Gen64 gen) {
879+ PforDeltaLayoutDecodeImpl<PforDeltaLayout::kDeltaBinaryPacked , int64_t >(state, gen);
880+ }
881+
735882// ----------------------------------------------------------------------
736883// DeltaBitPack Encode/Decode
737884
@@ -1120,38 +1267,48 @@ static void BM_BssLz464Decode(benchmark::State& state, Gen64 gen) {
11201267static void CustomArgs (benchmark::internal::Benchmark* b) { b->Arg (102400 ); }
11211268
11221269// Macro to register all algorithms for a given dataset
1123- #define REGISTER_DATASET (Name, GenFunc ) \
1124- BENCHMARK_CAPTURE (BM_PforEncode, Name, &GenFunc)->Apply (CustomArgs); \
1125- BENCHMARK_CAPTURE (BM_PforDecode, Name, &GenFunc)->Apply (CustomArgs); \
1126- BENCHMARK_CAPTURE (BM_DeltaBitPackEncode, Name, &GenFunc)->Apply (CustomArgs); \
1127- BENCHMARK_CAPTURE (BM_DeltaBitPackDecode, Name, &GenFunc)->Apply (CustomArgs); \
1128- BENCHMARK_CAPTURE (BM_PlainZstdEncode, Name, &GenFunc)->Apply (CustomArgs); \
1129- BENCHMARK_CAPTURE (BM_PlainZstdDecode, Name, &GenFunc)->Apply (CustomArgs); \
1130- BENCHMARK_CAPTURE (BM_PlainLz4Encode, Name, &GenFunc)->Apply (CustomArgs); \
1131- BENCHMARK_CAPTURE (BM_PlainLz4Decode, Name, &GenFunc)->Apply (CustomArgs); \
1132- BENCHMARK_CAPTURE (BM_RleBitPackEncode, Name, &GenFunc)->Apply (CustomArgs); \
1133- BENCHMARK_CAPTURE (BM_RleBitPackDecode, Name, &GenFunc)->Apply (CustomArgs); \
1134- BENCHMARK_CAPTURE (BM_BssZstdEncode, Name, &GenFunc)->Apply (CustomArgs); \
1135- BENCHMARK_CAPTURE (BM_BssZstdDecode, Name, &GenFunc)->Apply (CustomArgs); \
1136- BENCHMARK_CAPTURE (BM_BssLz4Encode, Name, &GenFunc)->Apply (CustomArgs); \
1270+ #define REGISTER_DATASET (Name, GenFunc ) \
1271+ BENCHMARK_CAPTURE (BM_PforEncode, Name, &GenFunc)->Apply (CustomArgs); \
1272+ BENCHMARK_CAPTURE (BM_PforDecode, Name, &GenFunc)->Apply (CustomArgs); \
1273+ BENCHMARK_CAPTURE (BM_PforRawEncode, Name, &GenFunc)->Apply (CustomArgs); \
1274+ BENCHMARK_CAPTURE (BM_PforPatchedDeltaEncode, Name, &GenFunc)->Apply (CustomArgs); \
1275+ BENCHMARK_CAPTURE (BM_PforPatchedDeltaDecode, Name, &GenFunc)->Apply (CustomArgs); \
1276+ BENCHMARK_CAPTURE (BM_PforDbpDeltaEncode, Name, &GenFunc)->Apply (CustomArgs); \
1277+ BENCHMARK_CAPTURE (BM_PforDbpDeltaDecode, Name, &GenFunc)->Apply (CustomArgs); \
1278+ BENCHMARK_CAPTURE (BM_DeltaBitPackEncode, Name, &GenFunc)->Apply (CustomArgs); \
1279+ BENCHMARK_CAPTURE (BM_DeltaBitPackDecode, Name, &GenFunc)->Apply (CustomArgs); \
1280+ BENCHMARK_CAPTURE (BM_PlainZstdEncode, Name, &GenFunc)->Apply (CustomArgs); \
1281+ BENCHMARK_CAPTURE (BM_PlainZstdDecode, Name, &GenFunc)->Apply (CustomArgs); \
1282+ BENCHMARK_CAPTURE (BM_PlainLz4Encode, Name, &GenFunc)->Apply (CustomArgs); \
1283+ BENCHMARK_CAPTURE (BM_PlainLz4Decode, Name, &GenFunc)->Apply (CustomArgs); \
1284+ BENCHMARK_CAPTURE (BM_RleBitPackEncode, Name, &GenFunc)->Apply (CustomArgs); \
1285+ BENCHMARK_CAPTURE (BM_RleBitPackDecode, Name, &GenFunc)->Apply (CustomArgs); \
1286+ BENCHMARK_CAPTURE (BM_BssZstdEncode, Name, &GenFunc)->Apply (CustomArgs); \
1287+ BENCHMARK_CAPTURE (BM_BssZstdDecode, Name, &GenFunc)->Apply (CustomArgs); \
1288+ BENCHMARK_CAPTURE (BM_BssLz4Encode, Name, &GenFunc)->Apply (CustomArgs); \
11371289 BENCHMARK_CAPTURE (BM_BssLz4Decode, Name, &GenFunc)->Apply (CustomArgs);
11381290
11391291// Same as REGISTER_DATASET but for int64 (BIGINT) columns; benchmark names get
11401292// the "64" codec suffix (e.g. BM_Pfor64Encode) to distinguish them.
1141- #define REGISTER_DATASET64 (Name, GenFunc ) \
1142- BENCHMARK_CAPTURE (BM_Pfor64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1143- BENCHMARK_CAPTURE (BM_Pfor64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1144- BENCHMARK_CAPTURE (BM_DeltaBitPack64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1145- BENCHMARK_CAPTURE (BM_DeltaBitPack64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1146- BENCHMARK_CAPTURE (BM_PlainZstd64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1147- BENCHMARK_CAPTURE (BM_PlainZstd64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1148- BENCHMARK_CAPTURE (BM_PlainLz464Encode, Name, &GenFunc)->Apply (CustomArgs); \
1149- BENCHMARK_CAPTURE (BM_PlainLz464Decode, Name, &GenFunc)->Apply (CustomArgs); \
1150- BENCHMARK_CAPTURE (BM_RleBitPack64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1151- BENCHMARK_CAPTURE (BM_RleBitPack64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1152- BENCHMARK_CAPTURE (BM_BssZstd64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1153- BENCHMARK_CAPTURE (BM_BssZstd64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1154- BENCHMARK_CAPTURE (BM_BssLz464Encode, Name, &GenFunc)->Apply (CustomArgs); \
1293+ #define REGISTER_DATASET64 (Name, GenFunc ) \
1294+ BENCHMARK_CAPTURE (BM_Pfor64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1295+ BENCHMARK_CAPTURE (BM_Pfor64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1296+ BENCHMARK_CAPTURE (BM_PforRaw64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1297+ BENCHMARK_CAPTURE (BM_PforPatchedDelta64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1298+ BENCHMARK_CAPTURE (BM_PforPatchedDelta64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1299+ BENCHMARK_CAPTURE (BM_PforDbpDelta64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1300+ BENCHMARK_CAPTURE (BM_PforDbpDelta64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1301+ BENCHMARK_CAPTURE (BM_DeltaBitPack64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1302+ BENCHMARK_CAPTURE (BM_DeltaBitPack64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1303+ BENCHMARK_CAPTURE (BM_PlainZstd64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1304+ BENCHMARK_CAPTURE (BM_PlainZstd64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1305+ BENCHMARK_CAPTURE (BM_PlainLz464Encode, Name, &GenFunc)->Apply (CustomArgs); \
1306+ BENCHMARK_CAPTURE (BM_PlainLz464Decode, Name, &GenFunc)->Apply (CustomArgs); \
1307+ BENCHMARK_CAPTURE (BM_RleBitPack64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1308+ BENCHMARK_CAPTURE (BM_RleBitPack64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1309+ BENCHMARK_CAPTURE (BM_BssZstd64Encode, Name, &GenFunc)->Apply (CustomArgs); \
1310+ BENCHMARK_CAPTURE (BM_BssZstd64Decode, Name, &GenFunc)->Apply (CustomArgs); \
1311+ BENCHMARK_CAPTURE (BM_BssLz464Encode, Name, &GenFunc)->Apply (CustomArgs); \
11551312 BENCHMARK_CAPTURE (BM_BssLz464Decode, Name, &GenFunc)->Apply (CustomArgs);
11561313
11571314// ClickBench datasets
0 commit comments