Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions cpp/src/arrow/util/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ if (ARROW_BUILD_BENCHMARKS)
Shlwapi.lib
)
else()
target_link_libraries(arrow_benchmark_main
target_link_libraries(arrow_benchmark_main
benchmark
pthread
)
)
endif()

# TODO(wesm): Some benchmarks include gtest.h
Expand All @@ -80,4 +80,6 @@ ADD_ARROW_TEST(key-value-metadata-test)
ADD_ARROW_TEST(rle-encoding-test)
ADD_ARROW_TEST(stl-util-test)

ADD_ARROW_BENCHMARK(bit-util-benchmark)

add_subdirectory(variant)
58 changes: 58 additions & 0 deletions cpp/src/arrow/util/bit-util-benchmark.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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 <vector>

#include "arrow/buffer.h"
#include "arrow/builder.h"
#include "arrow/memory_pool.h"
#include "arrow/test-util.h"
#include "arrow/util/bit-util.h"

namespace arrow {
namespace BitUtil {

static void BM_CopyBitmap(benchmark::State& state) { // NOLINT non-const reference
const int kBufferSize = state.range(0);

std::shared_ptr<Buffer> buffer;
ASSERT_OK(AllocateBuffer(default_memory_pool(), kBufferSize, &buffer));
memset(buffer->mutable_data(), 0, kBufferSize);
test::random_bytes(kBufferSize, 0, buffer->mutable_data());

const int num_bits = kBufferSize * 8;
const uint8_t* src = buffer->data();

std::shared_ptr<Buffer> copy;
while (state.KeepRunning()) {
ABORT_NOT_OK(CopyBitmap(default_memory_pool(), src, state.range(1), num_bits, &copy));
}
state.SetBytesProcessed(state.iterations() * kBufferSize * sizeof(int8_t));
}

BENCHMARK(BM_CopyBitmap)
->Args({100000, 0})
->Args({1000000, 0})
->Args({100000, 4})
->Args({1000000, 4})
->MinTime(1.0)
->Unit(benchmark::kMicrosecond);

} // namespace BitUtil
} // namespace arrow
17 changes: 9 additions & 8 deletions cpp/src/arrow/util/bit-util-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -165,19 +165,20 @@ TEST(BitUtilTests, TestCopyBitmap) {
memset(buffer->mutable_data(), 0, kBufferSize);
test::random_bytes(kBufferSize, 0, buffer->mutable_data());

const int num_bits = kBufferSize * 8;

const uint8_t* src = buffer->data();

std::vector<int64_t> lengths = {kBufferSize * 8 - 4, kBufferSize * 8};
std::vector<int64_t> offsets = {0, 12, 16, 32, 37, 63, 64, 128};
for (int64_t offset : offsets) {
const int64_t copy_length = num_bits - offset;
for (int64_t num_bits : lengths) {
for (int64_t offset : offsets) {
const int64_t copy_length = num_bits - offset;

std::shared_ptr<Buffer> copy;
ASSERT_OK(CopyBitmap(default_memory_pool(), src, offset, copy_length, &copy));
std::shared_ptr<Buffer> copy;
ASSERT_OK(CopyBitmap(default_memory_pool(), src, offset, copy_length, &copy));

for (int64_t i = 0; i < copy_length; ++i) {
ASSERT_EQ(BitUtil::GetBit(src, i + offset), BitUtil::GetBit(copy->data(), i));
for (int64_t i = 0; i < copy_length; ++i) {
ASSERT_EQ(BitUtil::GetBit(src, i + offset), BitUtil::GetBit(copy->data(), i));
}
}
}
}
Expand Down
32 changes: 30 additions & 2 deletions cpp/src/arrow/util/bit-util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,37 @@ Status CopyBitmap(MemoryPool* pool, const uint8_t* data, int64_t offset, int64_t
std::shared_ptr<Buffer> buffer;
RETURN_NOT_OK(GetEmptyBitmap(pool, length, &buffer));
uint8_t* dest = buffer->mutable_data();
for (int64_t i = 0; i < length; ++i) {
BitUtil::SetBitTo(dest, i, BitUtil::GetBit(data, i + offset));

int64_t byte_offset = offset / 8;
int64_t bit_offset = offset % 8;
int64_t num_bytes = BitUtil::BytesForBits(length);
int64_t bits_to_zero = num_bytes * 8 - length;

if (bit_offset > 0) {
uint32_t carry_mask = BitUtil::kBitmask[bit_offset] - 1U;
uint32_t carry_shift = 8U - static_cast<uint32_t>(bit_offset);

uint32_t carry = 0U;
if (BitUtil::BytesForBits(length + bit_offset) > num_bytes) {
carry = (data[byte_offset + num_bytes] & carry_mask) << carry_shift;
}

int64_t i = num_bytes - 1;
while (i + 1 > 0) {
uint8_t cur_byte = data[byte_offset + i];
dest[i] = static_cast<uint8_t>((cur_byte >> bit_offset) | carry);
carry = (cur_byte & carry_mask) << carry_shift;
--i;
}
} else {
std::memcpy(dest, data + byte_offset, static_cast<size_t>(num_bytes));
}

for (int64_t i = length; i < length + bits_to_zero; ++i) {
// Both branches may copy extra bits - unsetting to match specification.
BitUtil::SetBitTo(dest, i, false);
}

*out = buffer;
return Status::OK();
}
Expand Down
9 changes: 3 additions & 6 deletions cpp/src/arrow/util/bit-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,10 @@ static inline void SetArrayBit(uint8_t* bits, int i, bool is_set) {
}

static inline void SetBitTo(uint8_t* bits, int64_t i, bool bit_is_set) {
// TODO: speed up. See https://graphics.stanford.edu/~seander/bithacks.html
// https://graphics.stanford.edu/~seander/bithacks.html
// "Conditionally set or clear bits without branching"
if (bit_is_set) {
SetBit(bits, i);
} else {
ClearBit(bits, i);
}
bits[i / 8] ^= static_cast<uint8_t>(-static_cast<uint8_t>(bit_is_set) ^ bits[i / 8]) &
kBitmask[i % 8];
}

// Returns the minimum number of bits needed to represent the value of 'x'
Expand Down