Skip to content
Merged
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
56 changes: 56 additions & 0 deletions .github/workflows/benchmarks-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: CMake Build and Run Benchmark Tests

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Create Build Directory
run: mkdir build

- name: Configure CMake
working-directory: ./build
run: cmake -DDISABLE_AVX512=ON -DENABLE_ADDRESS_SANITIZER=ON ..

- name: Build Project
working-directory: ./build
run: make -j

- name: Run Benchmark Tests
working-directory: ./build
run: ./benchmark_tests

build-and-test-with-SDE:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Create Build Directory
run: mkdir build

- name: Download and Unpack SDE
working-directory: ./build
run: |
wget https://downloadmirror.intel.com/859732/sde-external-9.58.0-2025-06-16-lin.tar.xz
tar xf sde-external-9.58.0-2025-06-16-lin.tar.xz

- name: Configure CMake
working-directory: ./build
run: cmake -DENABLE_ADDRESS_SANITIZER=ON -DMARCH=icelake-client -DHAVE_STD_REGEX=ON ..

- name: Build Project
working-directory: ./build
run: make -j

- name: Run Benchmark Tests
working-directory: ./build
run: sde-external-9.58.0-2025-06-16-lin/sde64 -icl -emu-xinuse 0 -- ./benchmark_tests
6 changes: 3 additions & 3 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CMake Build and Test
name: CMake Build and Run Unittests

on:
push:
Expand All @@ -18,7 +18,7 @@ jobs:

- name: Configure CMake
working-directory: ./build
run: cmake ..
run: cmake -DDISABLE_AVX512=ON -DENABLE_ADDRESS_SANITIZER=ON ..

- name: Build Project
working-directory: ./build
Expand All @@ -45,7 +45,7 @@ jobs:

- name: Configure CMake
working-directory: ./build
run: cmake .. -DMARCH=icelake-client
run: cmake -DENABLE_ADDRESS_SANITIZER=ON -DMARCH=icelake-client -DHAVE_STD_REGEX=ON ..

- name: Build Project
working-directory: ./build
Expand Down
27 changes: 27 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
cmake_minimum_required(VERSION 3.12)
project(pixie)


set(MARCH "native" CACHE STRING "march compilier flag")
set(CMAKE_CXX_FLAGS "-march=${MARCH}")
message(STATUS "MARCH is '${MARCH}'")

option(DISABLE_AVX512 "Disable AVX512 instructions" OFF)
if(DISABLE_AVX512)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-avx512f")
message(STATUS "DISABLE_AVX512 is ON")
endif()

option(ENABLE_ADDRESS_SANITIZER "Enable AddressSanitizer" OFF)
if(ENABLE_ADDRESS_SANITIZER)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer")
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fsanitize=address")
message(STATUS "AddressSanitizer is ON")
endif()

include(FetchContent)
FetchContent_Declare(
googletest
Expand Down Expand Up @@ -45,6 +61,17 @@ target_link_libraries(unittests
gtest
gtest_main)

add_executable(benchmark_tests
src/benchmark_tests.cpp)
target_include_directories(benchmark_tests
PUBLIC include
PUBLIC ${GOOGLETEST_SOURCE_DIR}/include
)
set_property(TARGET benchmark_tests PROPERTY CXX_STANDARD 20)
target_link_libraries(benchmark_tests
gtest
gtest_main)

add_executable(bench_rmm
src/bench_rmm.cpp)
target_include_directories(bench_rmm
Expand Down
8 changes: 4 additions & 4 deletions include/bits.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,17 @@ uint64_t rank_512(const uint64_t* x, uint64_t count) {

#else

uint64_t last_uint = count >> 6;
uint64_t last_uint = count < 512 ? count >> 6 : 8;

uint64_t pop_val = 0;

for (int i = 0; i < last_uint; i++) {
pop_val += std::popcount(x[i]);
}

uint64_t final = x[last_uint] & first_bits_mask(count & 63);

pop_val += std::popcount(final);
pop_val += count < 512
? std::popcount(x[last_uint] & first_bits_mask(count & 63))
: 0;
return pop_val;

#endif
Expand Down
2 changes: 1 addition & 1 deletion include/bitvector.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ class BitVectorInterleaved {
offset_size_ -= num_bits;
return result;
}
uint64_t next = bits_[iterator_64_++];
uint64_t next = iterator_64_ < bits_.size() ? bits_[iterator_64_++] : 0;
Comment thread
Malkovsky marked this conversation as resolved.
result ^= (next & first_bits_mask(num_bits - offset_size_))
<< offset_size_;
offset_bits_ = (num_bits - offset_size_ == 64)
Expand Down
71 changes: 71 additions & 0 deletions src/benchmark_tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include <gtest/gtest.h>

#include <numeric>
#include <random>

#include "bits.h"
#include "bitvector.h"

using pixie::BitVector;
using pixie::BitVectorInterleaved;

TEST(BitVectorBenchmarkTest, SelectNonInterleaved10PercentFill) {
for (size_t n = 4; n <= (1ull << 34); n <<= 2) {
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
size_t num_ones = n * 0.1;
for (int i = 0; i < num_ones; i++) {
uint64_t pos = rng() % n;
bits[pos / 64] |= (1ULL << pos % 64);
}

pixie::BitVector bv(bits, n);

auto max_rank = bv.rank(bv.size()) + 1;
for (int i = 0; i < 100000; i++) {
uint64_t rank = rng() % max_rank;
bv.select(rank);
}
}
}

TEST(BitVectorBenchmarkTest, SelectNonInterleaved90PercentFill) {
for (size_t n = 4; n <= (1ull << 34); n <<= 2) {
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
size_t num_ones = n * 0.9;
for (int i = 0; i < num_ones; i++) {
uint64_t pos = rng() % n;
bits[pos / 64] |= (1ULL << pos % 64);
}

pixie::BitVector bv(bits, n);

auto max_rank = bv.rank(bv.size()) + 1;
for (int i = 0; i < 100000; i++) {
uint64_t rank = rng() % max_rank;
bv.select(rank);
}
}
}

TEST(BitVectorBenchmarkTest, SelectNonInterleaved) {
for (size_t n = 4; n <= (1ull << 34); n <<= 2) {
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
for (auto& x : bits) {
x = rng();
}
pixie::BitVector bv(bits, n);

auto max_rank = bv.rank(bv.size()) + 1;

for (int i = 0; i < 100000; i++) {
uint64_t rank = rng() % max_rank;
bv.select(rank);
}
}
}
121 changes: 114 additions & 7 deletions src/benchmarks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ static void BM_RankNonInterleaved(benchmark::State& state) {
size_t n = state.range(0);
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(1 + n / 64);
std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
for (auto& x : bits) {
x = rng();
}
Expand All @@ -24,7 +24,7 @@ static void BM_RankInterleaved(benchmark::State& state) {
size_t n = state.range(0);
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(1 + n / 64);
std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
for (auto& x : bits) {
x = rng();
}
Expand All @@ -40,13 +40,93 @@ static void BM_SelectNonInterleaved(benchmark::State& state) {
size_t n = state.range(0);
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(1 + n / 64);
std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
for (auto& x : bits) {
x = rng();
}
pixie::BitVector bv(bits, n);

auto max_rank = bv.rank(bv.size());
auto max_rank = bv.rank(bv.size()) + 1;

for (auto _ : state) {
uint64_t rank = rng() % max_rank;
benchmark::DoNotOptimize(bv.select(rank));
}
}

static void BM_RankNonInterleaved10PercentFill(benchmark::State& state) {
size_t n = state.range(0);
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
size_t num_ones = n * 0.1;
for (int i = 0; i < num_ones; i++) {
uint64_t pos = rng() % n;
bits[pos / 64] |= (1ULL << pos % 64);
}

pixie::BitVector bv(bits, n);

for (auto _ : state) {
uint64_t pos = rng() % n;
benchmark::DoNotOptimize(bv.rank(pos));
}
}

static void BM_SelectNonInterleaved10PercentFill(benchmark::State& state) {
size_t n = state.range(0);
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
size_t num_ones = n * 0.1;
for (int i = 0; i < num_ones; i++) {
uint64_t pos = rng() % n;
bits[pos / 64] |= (1ULL << pos % 64);
}

pixie::BitVector bv(bits, n);

auto max_rank = bv.rank(bv.size()) + 1;

for (auto _ : state) {
uint64_t rank = rng() % max_rank;
benchmark::DoNotOptimize(bv.select(rank));
}
}

static void BM_RankNonInterleaved90PercentFill(benchmark::State& state) {
size_t n = state.range(0);
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
size_t num_ones = n * 0.9;
for (int i = 0; i < num_ones; i++) {
uint64_t pos = rng() % n;
bits[pos / 64] |= (1ULL << pos % 64);
}

pixie::BitVector bv(bits, n);

for (auto _ : state) {
uint64_t pos = rng() % n;
benchmark::DoNotOptimize(bv.rank(pos));
}
}

static void BM_SelectNonInterleaved90PercentFill(benchmark::State& state) {
size_t n = state.range(0);
std::mt19937_64 rng(42);

std::vector<uint64_t> bits(((8 + n / 64) / 8) * 8);
size_t num_ones = n * 0.9;
for (int i = 0; i < num_ones; i++) {
uint64_t pos = rng() % n;
bits[pos / 64] |= (1ULL << pos % 64);
}

pixie::BitVector bv(bits, n);

auto max_rank = bv.rank(bv.size()) + 1;

for (auto _ : state) {
uint64_t rank = rng() % max_rank;
Expand All @@ -57,14 +137,41 @@ static void BM_SelectNonInterleaved(benchmark::State& state) {
BENCHMARK(BM_RankNonInterleaved)
->ArgNames({"n"})
->RangeMultiplier(4)
->Range(8, 1ull << 34);
->Range(8, 1ull << 34)
->Iterations(100000);

BENCHMARK(BM_RankInterleaved)
->ArgNames({"n"})
->RangeMultiplier(4)
->Range(8, 1ull << 34);
->Range(8, 1ull << 34)
->Iterations(100000);

BENCHMARK(BM_SelectNonInterleaved)
->ArgNames({"n"})
->RangeMultiplier(4)
->Range(8, 1ull << 34);
->Range(8, 1ull << 34)
->Iterations(100000);

BENCHMARK(BM_RankNonInterleaved10PercentFill)
->ArgNames({"n"})
->RangeMultiplier(4)
->Range(8, 1ull << 34)
->Iterations(100000);

BENCHMARK(BM_SelectNonInterleaved10PercentFill)
->ArgNames({"n"})
->RangeMultiplier(4)
->Range(8, 1ull << 34)
->Iterations(100000);

BENCHMARK(BM_RankNonInterleaved90PercentFill)
->ArgNames({"n"})
->RangeMultiplier(4)
->Range(8, 1ull << 34)
->Iterations(100000);

BENCHMARK(BM_SelectNonInterleaved90PercentFill)
->ArgNames({"n"})
->RangeMultiplier(4)
->Range(8, 1ull << 34)
->Iterations(100000);
Loading