blob: 429993442a35c387f3f172c6d9e55bd7d6d77723 [file] [log] [blame]
// 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 "benchmark/benchmark.h"
#include <cstdint>
#include <cstdlib>
#include <memory>
#include "arrow/array/array_base.h"
#include "arrow/array/array_primitive.h"
#include "arrow/testing/random.h"
#include "arrow/util/bit_block_counter.h"
#include "arrow/util/bit_util.h"
#include "arrow/util/bitmap_reader.h"
namespace arrow {
namespace internal {
struct UnaryBitBlockBenchmark {
benchmark::State& state;
int64_t offset;
int64_t bitmap_length;
std::shared_ptr<Array> arr;
int64_t expected;
explicit UnaryBitBlockBenchmark(benchmark::State& state, int64_t offset = 0)
: state(state), offset(offset), bitmap_length(1 << 20) {
random::RandomArrayGenerator rng(/*seed=*/0);
// State parameter is the average number of total values for each null
// value. So 100 means that 1 out of 100 on average are null.
double null_probability = 1. / static_cast<double>(state.range(0));
arr = rng.Int8(bitmap_length, 0, 100, null_probability);
// Compute the expected result
this->expected = 0;
const auto& int8_arr = static_cast<const Int8Array&>(*arr);
for (int64_t i = this->offset; i < bitmap_length; ++i) {
if (int8_arr.IsValid(i)) {
this->expected += int8_arr.Value(i);
}
}
}
template <typename NextBlockFunc>
void BenchBitBlockCounter(NextBlockFunc&& next_block) {
const auto& int8_arr = static_cast<const Int8Array&>(*arr);
const uint8_t* bitmap = arr->null_bitmap_data();
for (auto _ : state) {
BitBlockCounter scanner(bitmap, this->offset, bitmap_length - this->offset);
int64_t result = 0;
int64_t position = this->offset;
while (true) {
BitBlockCount block = next_block(&scanner);
if (block.length == 0) {
break;
}
if (block.length == block.popcount) {
// All not-null
for (int64_t i = 0; i < block.length; ++i) {
result += int8_arr.Value(position + i);
}
} else if (block.popcount > 0) {
// Some but not all not-null
for (int64_t i = 0; i < block.length; ++i) {
if (BitUtil::GetBit(bitmap, position + i)) {
result += int8_arr.Value(position + i);
}
}
}
position += block.length;
}
// Sanity check
if (result != expected) {
std::abort();
}
}
state.SetItemsProcessed(state.iterations() * bitmap_length);
}
void BenchBitmapReader() {
const auto& int8_arr = static_cast<const Int8Array&>(*arr);
for (auto _ : state) {
internal::BitmapReader bit_reader(arr->null_bitmap_data(), this->offset,
bitmap_length - this->offset);
int64_t result = 0;
for (int64_t i = this->offset; i < bitmap_length; ++i) {
if (bit_reader.IsSet()) {
result += int8_arr.Value(i);
}
bit_reader.Next();
}
// Sanity check
if (result != expected) {
std::abort();
}
}
state.SetItemsProcessed(state.iterations() * bitmap_length);
}
};
struct BinaryBitBlockBenchmark {
benchmark::State& state;
int64_t offset;
int64_t bitmap_length;
std::shared_ptr<Array> left;
std::shared_ptr<Array> right;
int64_t expected;
const Int8Array* left_int8;
const Int8Array* right_int8;
explicit BinaryBitBlockBenchmark(benchmark::State& state, int64_t offset = 0)
: state(state), offset(offset), bitmap_length(1 << 20) {
random::RandomArrayGenerator rng(/*seed=*/0);
// State parameter is the average number of total values for each null
// value. So 100 means that 1 out of 100 on average are null.
double null_probability = 1. / static_cast<double>(state.range(0));
left = rng.Int8(bitmap_length, 0, 100, null_probability);
right = rng.Int8(bitmap_length, 0, 50, null_probability);
left_int8 = static_cast<const Int8Array*>(left.get());
right_int8 = static_cast<const Int8Array*>(right.get());
// Compute the expected result
expected = 0;
for (int64_t i = this->offset; i < bitmap_length; ++i) {
if (left_int8->IsValid(i) && right_int8->IsValid(i)) {
expected += left_int8->Value(i) + right_int8->Value(i);
}
}
}
void BenchBitBlockCounter() {
const uint8_t* left_bitmap = left->null_bitmap_data();
const uint8_t* right_bitmap = right->null_bitmap_data();
for (auto _ : state) {
BinaryBitBlockCounter scanner(left_bitmap, this->offset, right_bitmap, this->offset,
bitmap_length - this->offset);
int64_t result = 0;
int64_t position = this->offset;
while (true) {
BitBlockCount block = scanner.NextAndWord();
if (block.length == 0) {
break;
}
if (block.length == block.popcount) {
// All not-null
for (int64_t i = 0; i < block.length; ++i) {
result += left_int8->Value(position + i) + right_int8->Value(position + i);
}
} else if (block.popcount > 0) {
// Some but not all not-null
for (int64_t i = 0; i < block.length; ++i) {
if (BitUtil::GetBit(left_bitmap, position + i) &&
BitUtil::GetBit(right_bitmap, position + i)) {
result += left_int8->Value(position + i) + right_int8->Value(position + i);
}
}
}
position += block.length;
}
// Sanity check
if (result != expected) {
std::abort();
}
}
state.SetItemsProcessed(state.iterations() * bitmap_length);
}
void BenchBitmapReader() {
for (auto _ : state) {
internal::BitmapReader left_reader(left->null_bitmap_data(), this->offset,
bitmap_length - this->offset);
internal::BitmapReader right_reader(right->null_bitmap_data(), this->offset,
bitmap_length - this->offset);
int64_t result = 0;
for (int64_t i = this->offset; i < bitmap_length; ++i) {
if (left_reader.IsSet() && right_reader.IsSet()) {
result += left_int8->Value(i) + right_int8->Value(i);
}
left_reader.Next();
right_reader.Next();
}
// Sanity check
if (result != expected) {
std::abort();
}
}
state.SetItemsProcessed(state.iterations() * bitmap_length);
}
};
static void BitBlockCounterSum(benchmark::State& state) {
UnaryBitBlockBenchmark(state, /*offset=*/0)
.BenchBitBlockCounter([](BitBlockCounter* counter) { return counter->NextWord(); });
}
static void BitBlockCounterSumWithOffset(benchmark::State& state) {
UnaryBitBlockBenchmark(state, /*offset=*/4)
.BenchBitBlockCounter([](BitBlockCounter* counter) { return counter->NextWord(); });
}
static void BitBlockCounterFourWordsSum(benchmark::State& state) {
UnaryBitBlockBenchmark(state, /*offset=*/0)
.BenchBitBlockCounter(
[](BitBlockCounter* counter) { return counter->NextFourWords(); });
}
static void BitBlockCounterFourWordsSumWithOffset(benchmark::State& state) {
UnaryBitBlockBenchmark(state, /*offset=*/4)
.BenchBitBlockCounter(
[](BitBlockCounter* counter) { return counter->NextFourWords(); });
}
static void BitmapReaderSum(benchmark::State& state) {
UnaryBitBlockBenchmark(state, /*offset=*/0).BenchBitmapReader();
}
static void BitmapReaderSumWithOffset(benchmark::State& state) {
UnaryBitBlockBenchmark(state, /*offset=*/4).BenchBitmapReader();
}
static void BinaryBitBlockCounterSum(benchmark::State& state) {
BinaryBitBlockBenchmark(state, /*offset=*/0).BenchBitBlockCounter();
}
static void BinaryBitBlockCounterSumWithOffset(benchmark::State& state) {
BinaryBitBlockBenchmark(state, /*offset=*/4).BenchBitBlockCounter();
}
static void BinaryBitmapReaderSum(benchmark::State& state) {
BinaryBitBlockBenchmark(state, /*offset=*/0).BenchBitmapReader();
}
static void BinaryBitmapReaderSumWithOffset(benchmark::State& state) {
BinaryBitBlockBenchmark(state, /*offset=*/4).BenchBitmapReader();
}
// Range value: average number of total values per null
BENCHMARK(BitBlockCounterSum)->Range(2, 1 << 16);
BENCHMARK(BitBlockCounterSumWithOffset)->Range(2, 1 << 16);
BENCHMARK(BitBlockCounterFourWordsSum)->Range(2, 1 << 16);
BENCHMARK(BitBlockCounterFourWordsSumWithOffset)->Range(2, 1 << 16);
BENCHMARK(BitmapReaderSum)->Range(2, 1 << 16);
BENCHMARK(BitmapReaderSumWithOffset)->Range(2, 1 << 16);
BENCHMARK(BinaryBitBlockCounterSum)->Range(2, 1 << 16);
BENCHMARK(BinaryBitBlockCounterSumWithOffset)->Range(2, 1 << 16);
BENCHMARK(BinaryBitmapReaderSum)->Range(2, 1 << 16);
BENCHMARK(BinaryBitmapReaderSumWithOffset)->Range(2, 1 << 16);
} // namespace internal
} // namespace arrow