Skip to content

Commit

Permalink
Scope BitsetWordType to Bitset class only
Browse files Browse the repository at this point in the history
  • Loading branch information
fsaintjacques committed Jan 31, 2020
1 parent ede0703 commit d906e49
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 76 deletions.
63 changes: 36 additions & 27 deletions include/jitmap/bitset.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

namespace jitmap {

using BitsetWordType = uint64_t;

// Bitset is a container similar to `std::bitset<N>` but wrapping a data pointer.
// The ownership/lifetime of the data pointer is defined by `Ptr` type.
template <size_t N = kBitsPerContainer, typename Ptr = const BitsetWordType*>
Expand All @@ -38,24 +40,23 @@ class Bitset {

// Indicate if the pointer is read-only.
static constexpr bool storage_is_const = std::is_const<storage_type>::value;
static constexpr size_t kBitsPerWord = sizeof(word_type) * CHAR_BIT;

// Construct a bitset from a pointer.
Bitset(Ptr data) : data_(std::move(data)) { JITMAP_PRE_NE(data_, nullptr); }

// Return the capacity (in bits) of the bitset.
constexpr size_t size() const noexcept { return N; }
// Return the capacity (in words) of the bitset.
constexpr size_t size_words() const noexcept { return N / kBitsPerBitsetWord; }

template <typename OtherPtr>
bool operator==(const Bitset<N, OtherPtr>& rhs) const {
const auto& lhs_data = data();
const auto& rhs_data = rhs.data();
const auto& lhs_word = word();
const auto& rhs_word = rhs.word();

if (lhs_data == rhs_data) return true;
if (lhs_word == rhs_word) return true;

for (size_t i = 0; i < size_words(); i++) {
if (lhs_data[i] != rhs_data[i]) return false;
if (lhs_word[i] != rhs_word[i]) return false;
}

return true;
Expand All @@ -73,13 +74,13 @@ class Bitset {
}

bool operator[](size_t i) const noexcept {
return data()[i / sizeof(word_type)] & (1U << (i % sizeof(word_type)));
return word()[i / sizeof(word_type)] & (1U << (i % sizeof(word_type)));
}

// Indicate if all bits are set.
bool all() const noexcept {
for (size_t i = 0; i < size_words(); i++) {
if (data()[i] != std::numeric_limits<word_type>::max()) return false;
if (word()[i] != std::numeric_limits<word_type>::max()) return false;
}

return true;
Expand All @@ -88,7 +89,7 @@ class Bitset {
// Indicate if at least one bit is set.
bool any() const noexcept {
for (size_t i = 0; i < size_words(); i++) {
if (data()[i] != 0) return true;
if (word()[i] != 0) return true;
}

return false;
Expand All @@ -97,7 +98,7 @@ class Bitset {
// Indicate if no bit is set.
bool none() const noexcept {
for (size_t i = 0; i < size_words(); i++) {
if (data()[i] != 0) return false;
if (word()[i] != 0) return false;
}

return true;
Expand All @@ -108,7 +109,7 @@ class Bitset {
size_t sum = 0;

for (size_t i = 0; i < size_words(); i++) {
sum += __builtin_popcountll(data()[i]);
sum += __builtin_popcountll(word()[i]);
}

return sum;
Expand All @@ -123,7 +124,7 @@ class Bitset {
enable_if_writable<T1, Bitset<N, Ptr>&> operator&=(
const Bitset<N, OtherPtr>& other) noexcept {
for (size_t i = 0; i < size_words(); i++) {
data()[i] &= other.data()[i];
word()[i] &= other.word()[i];
}
return *this;
}
Expand All @@ -133,7 +134,7 @@ class Bitset {
enable_if_writable<T1, Bitset<N, Ptr>&> operator|=(
const Bitset<N, OtherPtr>& other) noexcept {
for (size_t i = 0; i < size_words(); i++) {
data()[i] |= other.data()[i];
word()[i] |= other.word()[i];
}
return *this;
}
Expand All @@ -143,7 +144,7 @@ class Bitset {
enable_if_writable<T1, Bitset<N, Ptr>&> operator^=(
const Bitset<N, OtherPtr>& other) noexcept {
for (size_t i = 0; i < size_words(); i++) {
data()[i] ^= other.data()[i];
word()[i] ^= other.word()[i];
}
return *this;
}
Expand All @@ -152,15 +153,15 @@ class Bitset {
template <typename T1 = storage_type>
enable_if_writable<T1, Bitset<N, Ptr>&> operator~() noexcept {
for (size_t i = 0; i < size_words(); i++) {
data()[i] = ~data()[i];
word()[i] = ~word()[i];
}
return *this;
}

// Set all bits.
template <typename T1 = storage_type>
enable_if_writable<T1> set() noexcept {
memset(data(), 0xFF, size() / CHAR_BIT);
memset(word(), 0xFF, size() / CHAR_BIT);
}

/* TODO
Expand All @@ -172,7 +173,7 @@ class Bitset {
// Clear all bits.
template <typename T1 = storage_type>
enable_if_writable<T1> reset() noexcept {
memset(data(), 0, size() / CHAR_BIT);
memset(word(), 0, size() / CHAR_BIT);
}

/* TODO
Expand All @@ -194,26 +195,34 @@ class Bitset {
*/

// Data pointers

const BitsetWordType* data() const {
return reinterpret_cast<const BitsetWordType*>(&data_[0]);
}
const char* data() const { return reinterpret_cast<const char*>(&data_[0]); }

template <typename T1 = storage_type>
enable_if_writable<T1, BitsetWordType*> data() {
return reinterpret_cast<BitsetWordType*>(&data_[0]);
enable_if_writable<T1, char*> data() {
return reinterpret_cast<char*>(&data_[0]);
}

private:
Ptr data_;

static_assert(N % (kBitsPerBitsetWord) == 0,
"Bitset size must be a multiple of the BitsetWordType");
static_assert(N >= kBytesPerBitsetWord, "Bitset size must be greater than word_type");
static_assert(N % (kBitsPerWord) == 0, "Bitset size must be a multiple of word_type");
static_assert(N >= kBitsPerWord, "Bitset size must be greater than word_type");

// Friend itself of other template parameters, used for accessing `data_`.
template <size_t M, typename OtherPtr>
friend class Bitset;

// Return the capacity (in words) of the bitset.
constexpr size_t size_words() const noexcept {
return N / (CHAR_BIT * sizeof(word_type));
}

const word_type* word() const { return reinterpret_cast<const word_type*>(&data_[0]); }

template <typename T1 = storage_type>
enable_if_writable<T1, word_type*> word() {
return reinterpret_cast<word_type*>(&data_[0]);
}
};

// Create a bitset from a memory address.
Expand All @@ -235,7 +244,7 @@ auto allocate_aligned(size_t alignment, size_t length) {

template <size_t N>
auto make_owned_bitset() {
constexpr size_t kNumberWords = N / kBitsPerBitsetWord;
constexpr size_t kNumberWords = N / (sizeof(BitsetWordType) * CHAR_BIT);
return make_bitset<N>(allocate_aligned<BitsetWordType>(kCacheLineSize, kNumberWords));
}

Expand Down
2 changes: 1 addition & 1 deletion include/jitmap/query/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class CompilerException : public Exception {
};

// Signature of generated functions
typedef void (*DenseEvalFn)(const BitsetWordType**, BitsetWordType*);
typedef void (*DenseEvalFn)(const char**, char*);

struct CompilerOptions {
// Controls LLVM optimization level (-O0, -O1, -O2, -O3). Anything above 3
Expand Down
3 changes: 1 addition & 2 deletions include/jitmap/query/query.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include <string>
#include <vector>

#include <jitmap/size.h>
#include <jitmap/util/pimpl.h>

namespace jitmap {
Expand Down Expand Up @@ -75,7 +74,7 @@ class Query : util::Pimpl<QueryImpl> {
// auto ordered_bitmaps = ReorderInputs({"a": a, "b": b, "c": c}, order);
// query->Eval(ordered_bitmaps, output);
// ```
void Eval(const BitsetWordType** inputs, BitsetWordType* output);
void Eval(const char** inputs, char* output);

// Return the referenced variables and the expected order.o
//
Expand Down
6 changes: 0 additions & 6 deletions include/jitmap/size.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,11 @@ namespace jitmap {

// The size of a cacheline.
constexpr size_t kCacheLineSize = 64ULL;

// The log of the number of bits per container.
constexpr size_t kLogBitsPerContainer = 16ULL;
// The number of bits per container.
constexpr size_t kBitsPerContainer = 1ULL << kLogBitsPerContainer;
// The number of bytes per container.
constexpr size_t kBytesPerContainer = kBitsPerContainer / CHAR_BIT;

using BitsetWordType = uint32_t;
constexpr size_t kBytesPerBitsetWord = sizeof(BitsetWordType);
constexpr size_t kBitsPerBitsetWord = kBytesPerBitsetWord * CHAR_BIT;
constexpr size_t kWordsPerContainers = kBytesPerContainer / kBytesPerBitsetWord;

} // namespace jitmap
2 changes: 2 additions & 0 deletions include/jitmap/util/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

#pragma once

#include <cstddef>

namespace jitmap {

#ifndef CACHELINE
Expand Down
6 changes: 3 additions & 3 deletions src/jitmap/query/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class ExpressionCodeGen {
auto induction_type = llvm::Type::getInt64Ty(*ctx_);
auto zero = llvm::ConstantInt::get(induction_type, 0);
auto step = llvm::ConstantInt::get(induction_type, 1);
auto n_words = llvm::ConstantInt::get(induction_type, word_size());
auto n_words = llvm::ConstantInt::get(induction_type, words());

auto loop_block = llvm::BasicBlock::Create(*ctx_, "loop", fn);
auto after_block = llvm::BasicBlock::Create(*ctx_, "after_loop", fn);
Expand Down Expand Up @@ -247,8 +247,8 @@ class ExpressionCodeGen {
llvm::Type* ElementType() { return llvm::Type::getIntNTy(*ctx_, scalar_width()); }
llvm::Type* ElementPtrType() { return ElementType()->getPointerTo(); }

uint8_t scalar_width() const { return kBitsPerBitsetWord; }
uint32_t word_size() const { return kBitsPerContainer / scalar_width(); }
uint8_t scalar_width() const { return sizeof(char) * CHAR_BIT; }
uint32_t words() const { return kBitsPerContainer / scalar_width(); }

std::unique_ptr<llvm::LLVMContext> ctx_;
std::unique_ptr<llvm::Module> module_;
Expand Down
2 changes: 1 addition & 1 deletion src/jitmap/query/query.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const std::string& Query::name() const { return impl().name(); }
const Expr& Query::expr() const { return impl().expr(); }
const std::vector<std::string>& Query::variables() const { return impl().variables(); }

void Query::Eval(const BitsetWordType** inputs, BitsetWordType* output) {
void Query::Eval(const char** inputs, char* output) {
JITMAP_PRE_NE(inputs, nullptr);
JITMAP_PRE_NE(output, nullptr);

Expand Down
6 changes: 1 addition & 5 deletions tests/bitset_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ TEST(BitsetTest, StackUInt64) {
auto empty = make_bitset<64>(&no_bits);
EXPECT_EQ(empty, empty);
EXPECT_EQ(empty.size(), kBitsetSize);
EXPECT_EQ(empty.size_words(), sizeof(no_bits) / kBytesPerBitsetWord);
EXPECT_EQ(empty.count(), 0);
EXPECT_FALSE(empty.all());
EXPECT_FALSE(empty.any());
Expand All @@ -80,7 +79,6 @@ TEST(BitsetTest, StackUInt64) {
auto full = make_bitset<64>(&all_bits);
EXPECT_EQ(full, full);
EXPECT_EQ(full.size(), kBitsetSize);
EXPECT_EQ(full.size_words(), sizeof(all_bits) / kBytesPerBitsetWord);
EXPECT_EQ(full.count(), kBitsetSize);
EXPECT_TRUE(full.all());
EXPECT_TRUE(full.any());
Expand All @@ -90,7 +88,6 @@ TEST(BitsetTest, StackUInt64) {
auto some = make_bitset<64>(&some_bits);
EXPECT_EQ(some, some);
EXPECT_EQ(some.size(), kBitsetSize);
EXPECT_EQ(some.size_words(), sizeof(some_bits) / kBytesPerBitsetWord);
EXPECT_EQ(some.count(), 32);
EXPECT_FALSE(some.all());
EXPECT_TRUE(some.any());
Expand All @@ -100,7 +97,6 @@ TEST(BitsetTest, StackUInt64) {
auto other = make_bitset<64>(&other_bits);
EXPECT_EQ(other, other);
EXPECT_EQ(other.size(), kBitsetSize);
EXPECT_EQ(other.size_words(), sizeof(other_bits) / kBytesPerBitsetWord);
EXPECT_EQ(other.count(), 32);
EXPECT_FALSE(other.all());
EXPECT_TRUE(other.any());
Expand Down Expand Up @@ -151,7 +147,7 @@ TEST(BitsetTest, ErrorOnNullPtrConstructor) {

template <size_t N = kBitsPerContainer>
class alignas(kCacheLineSize) BitsetStorage
: public std::array<BitsetWordType, N / kBitsPerBitsetWord> {
: public std::array<BitsetWordType, N / (sizeof(BitsetWordType) * CHAR_BIT)> {
public:
BitsetStorage(bool value = false) {
memset(this->data(), value ? 0xFF : 0x00, N / CHAR_BIT);
Expand Down
8 changes: 4 additions & 4 deletions tests/jitmap_benchmark.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ static void StaticIntersection2(benchmark::State& state) {
}

static void JitIntersection2(benchmark::State& state) {
std::array<BitsetWordType, kWordsPerContainers> a, b, output;
std::vector<const BitsetWordType*> inputs{a.data(), b.data()};
std::array<char, kBytesPerContainer> a, b, output;
std::vector<const char*> inputs{a.data(), b.data()};

query::ExecutionContext engine{query::JitEngine::Make()};
auto query = query::Query::Make("benchmark_query_2", "a & b", &engine);
Expand All @@ -61,8 +61,8 @@ static void StaticIntersection3(benchmark::State& state) {
}

static void JitIntersection3(benchmark::State& state) {
std::array<BitsetWordType, kWordsPerContainers> a, b, c, output;
std::vector<const BitsetWordType*> inputs{a.data(), b.data(), c.data()};
std::array<char, kBytesPerContainer> a, b, c, output;
std::vector<const char*> inputs{a.data(), b.data(), c.data()};

query::ExecutionContext engine{query::JitEngine::Make()};
auto query = query::Query::Make("benchmark_query_3", "a & b & c", &engine);
Expand Down
33 changes: 16 additions & 17 deletions tests/query/compiler_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ namespace query {

class JitTest : public QueryTest {
public:
void AssertQueryResult(const std::string& query_expr,
std::vector<BitsetWordType> input_words,
BitsetWordType output_word) {
void AssertQueryResult(const std::string& query_expr, std::vector<char> input_words,
char output_word) {
auto query = Query::Make(query_name(), query_expr, &ctx);

auto [_, inputs] = InitInputs(input_words);
JITMAP_UNUSED(_);

std::vector<BitsetWordType> output(kWordsPerContainers, 0UL);
std::vector<char> output(kBytesPerContainer, 0UL);
EXPECT_THAT(output, testing::Each(0UL));

query->Eval(inputs.data(), output.data());
Expand All @@ -43,15 +42,15 @@ class JitTest : public QueryTest {
private:
std::string query_name() { return "query_" + std::to_string(id++); }

std::tuple<std::vector<std::vector<BitsetWordType>>, std::vector<const BitsetWordType*>>
InitInputs(std::vector<BitsetWordType> input_words) {
std::tuple<std::vector<std::vector<char>>, std::vector<const char*>> InitInputs(
std::vector<char> input_words) {
size_t n_bitmaps = input_words.size();

std::vector<std::vector<BitsetWordType>> bitmaps;
std::vector<const BitsetWordType*> inputs;
std::vector<std::vector<char>> bitmaps;
std::vector<const char*> inputs;
for (size_t i = 0; i < n_bitmaps; i++) {
auto repeated_word = input_words[i];
bitmaps.emplace_back(kWordsPerContainers, repeated_word);
bitmaps.emplace_back(kBytesPerContainer, repeated_word);
inputs.emplace_back(bitmaps[i].data());
EXPECT_THAT(bitmaps[i], testing::Each(repeated_word));
}
Expand All @@ -70,14 +69,14 @@ TEST_F(JitTest, CpuDetection) {
}

TEST_F(JitTest, CompileAndExecuteTest) {
BitsetWordType full = 0xFFFFFFFF;
BitsetWordType empty = 0x0;

BitsetWordType a = 0b00010010110101011001001011010101;
BitsetWordType b = 0b11001000110101011101010101011011;
BitsetWordType c = 0b00000001110101111110101001111000;
BitsetWordType d = 0b11111111111110101111111100000011;
BitsetWordType e = 0b11111110100100001110000011010110;
char full = 0xFF;
char empty = 0x0;

char a = 0b00010010;
char b = 0b11001000;
char c = 0b00000001;
char d = 0b11111111;
char e = 0b11111110;

AssertQueryResult("!a", {a}, ~a);
AssertQueryResult("a & b", {a, b}, a & b);
Expand Down
Loading

0 comments on commit d906e49

Please sign in to comment.