Skip to content

Commit e20eec7

Browse files
Merge pull request #6 from lemire/master
Faster/tighter BlockedBloom64
2 parents 79aae0a + 0b9d948 commit e20eec7

File tree

4 files changed

+504
-50
lines changed

4 files changed

+504
-50
lines changed

benchmarks/Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22
OPT = -O3 -DNDEBUG
33
#OPT = -g -ggdb
44

5-
CXXFLAGS += -fno-strict-aliasing -Wall -std=c++11 -I. -I../src/ $(OPT) -march=native
5+
CXXFLAGS += -fno-strict-aliasing -Wall -std=c++11 -I. -I../src/ $(OPT)
6+
7+
UNAME_P := $(shell uname -p)
8+
ifeq ($(UNAME_P),aarch64)
9+
CXXFLAGS +=
10+
else
11+
CXXFLAGS += -march=native
12+
endif
613

714
LDFLAGS = -Wall
815

benchmarks/bulk-insert-and-query.cc

Lines changed: 130 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@
3232
#include "gcs.h"
3333
#ifdef __AVX2__
3434
#include "gqf_cpp.h"
35+
#include "simd-block.h"
3536
#endif
3637
#include "random.h"
37-
#ifdef __AVX2__
38-
#include "simd-block.h"
3938
#include "simd-block-fixed-fpp.h"
40-
#endif
4139
#include "timing.h"
4240
#ifdef __linux__
4341
#include "linux-perf-events.h"
@@ -169,6 +167,30 @@ struct FilterAPI<CuckooFilterStable<ItemType, bits_per_item, TableType, HashFami
169167
}
170168
};
171169

170+
171+
#ifdef __aarch64__
172+
template <typename HashFamily>
173+
struct FilterAPI<SimdBlockFilterFixed<HashFamily>> {
174+
using Table = SimdBlockFilterFixed<HashFamily>;
175+
static Table ConstructFromAddCount(size_t add_count) {
176+
Table ans(ceil(add_count * 8.0 / CHAR_BIT));
177+
return ans;
178+
}
179+
static void Add(uint64_t key, Table* table) {
180+
table->Add(key);
181+
}
182+
static void AddAll(const vector<uint64_t> keys, const size_t start, const size_t end, Table* table) {
183+
table->AddAll(keys, start, end);
184+
}
185+
186+
CONTAIN_ATTRIBUTES
187+
static bool Contain(uint64_t key, const Table * table) {
188+
return table->Find(key);
189+
}
190+
};
191+
192+
#endif
193+
172194
#ifdef __AVX2__
173195
template <typename HashFamily>
174196
struct FilterAPI<SimdBlockFilter<HashFamily>> {
@@ -211,6 +233,26 @@ struct FilterAPI<SimdBlockFilterFixed64<HashFamily>> {
211233
};
212234

213235

236+
template <typename HashFamily>
237+
struct FilterAPI<SimdBlockFilterFixed16<HashFamily>> {
238+
using Table = SimdBlockFilterFixed16<HashFamily>;
239+
static Table ConstructFromAddCount(size_t add_count) {
240+
Table ans(ceil(add_count * 8.0 / CHAR_BIT));
241+
return ans;
242+
}
243+
static void Add(uint64_t key, Table* table) {
244+
table->Add(key);
245+
}
246+
static void AddAll(const vector<uint64_t> keys, const size_t start, const size_t end, Table* table) {
247+
throw std::runtime_error("Unsupported");
248+
}
249+
250+
CONTAIN_ATTRIBUTES
251+
static bool Contain(uint64_t key, const Table * table) {
252+
return table->Find(key);
253+
}
254+
};
255+
214256
template <typename HashFamily>
215257
struct FilterAPI<SimdBlockFilterFixed<HashFamily>> {
216258
using Table = SimdBlockFilterFixed<HashFamily>;
@@ -249,6 +291,28 @@ struct FilterAPI<XorFilter<ItemType, FingerprintType>> {
249291
}
250292
};
251293

294+
295+
template<size_t blocksize, int k, typename HashFamily>
296+
struct FilterAPI<SimpleBlockFilter<blocksize,k,HashFamily>> {
297+
using Table = SimpleBlockFilter<blocksize,k,HashFamily>;
298+
static Table ConstructFromAddCount(size_t add_count) {
299+
Table ans(ceil(add_count * 8.0 / CHAR_BIT));
300+
return ans;
301+
}
302+
static void Add(uint64_t key, Table* table) {
303+
table->Add(key);
304+
}
305+
static void AddAll(const vector<uint64_t> keys, const size_t start, const size_t end, Table* table) {
306+
throw std::runtime_error("Unsupported");
307+
}
308+
309+
CONTAIN_ATTRIBUTES
310+
static bool Contain(uint64_t key, const Table * table) {
311+
return table->Find(key);
312+
}
313+
};
314+
315+
252316
template <typename ItemType, typename FingerprintType, typename HashFamily>
253317
struct FilterAPI<XorFilter<ItemType, FingerprintType, HashFamily>> {
254318
using Table = XorFilter<ItemType, FingerprintType, HashFamily>;
@@ -636,45 +700,21 @@ void parse_comma_separated(char * c, std::set<int> & answer ) {
636700
}
637701
}
638702

639-
/*
640-
#define MUL 1625L
641-
#define MUL2 (MUL*MUL)
642-
// (1<<64) / MUL
643-
#define INVMUL 11351842506898185L
644-
// (1<<64) / MUL2
645-
#define INVMUL2 6985749235014L
646-
int main() {
647-
printf("start\n");
648-
for (int a = 0; a < MUL; a++) {
649-
for (int b = 0; b < MUL; b++) {
650-
for (int c = 0; c < MUL; c++) {
651-
uint32_t x = a * MUL2 + b * MUL + c;
652-
int aa = (int) (((__uint128_t) x * (INVMUL2 + 1)) >> 64);
653-
if (aa != a) {
654-
printf("wrong a");
655-
return -1;
656-
}
657-
int bb = (int) (((__uint128_t) x * (INVMUL + 1)) >> 64);
658-
int rb = bb % MUL;
659-
if (rb != b) {
660-
printf("wrong b");
661-
return -1;
662-
}
663-
int expected = (a + b + c) % MUL;
664-
int got = (aa + bb + x) % MUL;
665-
if (expected != got) {
666-
printf("wrong modulo");
667-
return -1;
668-
}
669-
}
670-
}
671-
}
672-
printf("end\n");
673-
return 0;
674-
}
675-
*/
676703

677704
int main(int argc, char * argv[]) {
705+
#ifdef __aarch64__
706+
std::map<int,std::string> names = {{0,"Xor8"},{1,"Xor12"},
707+
{2,"Xor16"}, {3,"Cuckoo8"}, {4,"Cuckoo12"},
708+
{5,"Cuckoo16"}, {6,"CuckooSemiSort13" }, {7,"Bloom8"},
709+
{8,"Bloom12" }, {9,"Bloom16"}, {10,"BlockedBloom"},
710+
{11,"sort"}, {12,"Xor+8"}, {13,"Xor+16"},
711+
{14,"GCS"}, {22, "Xor10 (NBitArray)"}, {23, "Xor14 (NBitArray)"},
712+
{25, "Xor10"},{26, "Xor10.666"}, {37,"Bloom8 (addall)"},
713+
{38,"Bloom12 (addall)"},
714+
{40,"BlockedBloom (addall)"},
715+
{70,"SimpleBlockedBloom"}
716+
};
717+
#elif defined( __AVX2__)
678718
std::map<int,std::string> names = {{0,"Xor8"},{1,"Xor12"},
679719
{2,"Xor16"}, {3,"Cuckoo8"}, {4,"Cuckoo12"},
680720
{5,"Cuckoo16"}, {6,"CuckooSemiSort13" }, {7,"Bloom8"},
@@ -683,8 +723,22 @@ int main(int argc, char * argv[]) {
683723
{14,"GCS"}, {15,"CQF"}, {22, "Xor10 (NBitArray)"}, {23, "Xor14 (NBitArray)"},
684724
{25, "Xor10"},{26, "Xor10.666"}, {37,"Bloom8 (addall)"},
685725
{38,"Bloom12 (addall)"},{39,"Bloom16 (addall)"},
686-
{40,"BlockedBloom (addall)"}, {64,"BlockedBloom64"}
726+
{40,"BlockedBloom (addall)"}, {63,"BlockedBloom16"}, {64,"BlockedBloom64"},
727+
{70,"SimpleBlockedBloom"}
728+
};
729+
#else
730+
std::map<int,std::string> names = {{0,"Xor8"},{1,"Xor12"},
731+
{2,"Xor16"}, {3,"Cuckoo8"}, {4,"Cuckoo12"},
732+
{5,"Cuckoo16"}, {6,"CuckooSemiSort13" }, {7,"Bloom8"},
733+
{8,"Bloom12" }, {9,"Bloom16"},
734+
{11,"sort"}, {12,"Xor+8"}, {13,"Xor+16"},
735+
{14,"GCS"}, {22, "Xor10 (NBitArray)"}, {23, "Xor14 (NBitArray)"},
736+
{25, "Xor10"},{26, "Xor10.666"}, {37,"Bloom8 (addall)"},
737+
{38,"Bloom12 (addall)"},{39,"Bloom16 (addall)"},
738+
{70,"SimpleBlockedBloom"}
687739
};
740+
#endif
741+
688742

689743
if (argc < 2) {
690744
cout << "Usage: " << argv[0] << " <numberOfEntries> [<algorithmId> [<seed>]]" << endl;
@@ -716,6 +770,10 @@ int main(int argc, char * argv[]) {
716770
// we have a list of algos
717771
algorithmId = 9999999; // disabling
718772
parse_comma_separated(argv[2], algos);
773+
if(algos.size() == 0) {
774+
cerr<< " no algo selected " << endl;
775+
return -3;
776+
}
719777
} else {
720778
// we select just one
721779
stringstream input_string_2(argv[2]);
@@ -894,6 +952,14 @@ int main(int argc, char * argv[]) {
894952
cout << setw(NAME_WIDTH) << names[9] << cf << endl;
895953
}
896954

955+
#ifdef __aarch64__
956+
if (algorithmId == 10 || algorithmId < 0 || (algos.find(10) != algos.end())) {
957+
auto cf = FilterBenchmark<SimdBlockFilterFixed<>>(
958+
add_count, to_add, distinct_add, to_lookup, distinct_lookup, intersectionsize, hasduplicates, mixed_sets, seed);
959+
cout << setw(NAME_WIDTH) << names[10] << cf << endl;
960+
}
961+
#endif
962+
897963
#ifdef __AVX2__
898964
if (algorithmId == 10 || algorithmId < 0 || (algos.find(10) != algos.end())) {
899965
auto cf = FilterBenchmark<SimdBlockFilterFixed<>>(
@@ -905,6 +971,15 @@ int main(int argc, char * argv[]) {
905971
add_count, to_add, distinct_add, to_lookup, distinct_lookup, intersectionsize, hasduplicates, mixed_sets, seed);
906972
cout << setw(NAME_WIDTH) << names[64] << cf << endl;
907973
}
974+
975+
#endif
976+
#ifdef __SSSE3__
977+
978+
if (algorithmId == 63 || algorithmId < 0 || (algos.find(63) != algos.end())) {
979+
auto cf = FilterBenchmark<SimdBlockFilterFixed16<SimpleMixSplit>>(
980+
add_count, to_add, distinct_add, to_lookup, distinct_lookup, intersectionsize, hasduplicates, mixed_sets, seed);
981+
cout << setw(NAME_WIDTH) << names[63] << cf << endl;
982+
}
908983
#endif
909984

910985
if (algorithmId == 11 || algorithmId < 0 || (algos.find(11) != algos.end())) {
@@ -1004,6 +1079,13 @@ int main(int argc, char * argv[]) {
10041079
add_count, to_add, distinct_add, to_lookup, distinct_lookup, intersectionsize, hasduplicates, mixed_sets, seed, true);
10051080
cout << setw(NAME_WIDTH) << names[23] << cf << endl;
10061081
}
1082+
if (algorithmId == 70 || (algos.find(70) != algos.end())) {
1083+
auto cf = FilterBenchmark<
1084+
SimpleBlockFilter<8, 8, SimpleMixSplit>>(
1085+
add_count, to_add, distinct_add, to_lookup, distinct_lookup, intersectionsize, hasduplicates, mixed_sets, seed, false);
1086+
cout << setw(NAME_WIDTH) << names[70] << cf << endl;
1087+
}
1088+
10071089

10081090
// this algo overflows and crashes
10091091
/*if (algorithmId == 24 || (algos.find(24) != algos.end())) {
@@ -1062,8 +1144,13 @@ int main(int argc, char * argv[]) {
10621144
cout << setw(NAME_WIDTH) << names[40] << cf << endl;
10631145
}
10641146
#endif
1065-
1066-
1147+
#ifdef __aarch64__
1148+
if (algorithmId == 40 || algorithmId < 0 || (algos.find(40) != algos.end())) {
1149+
auto cf = FilterBenchmark<SimdBlockFilterFixed<SimpleMixSplit>>(
1150+
add_count, to_add, distinct_add, to_lookup, distinct_lookup, intersectionsize, hasduplicates, mixed_sets, seed, true);
1151+
cout << setw(NAME_WIDTH) << names[40] << cf << endl;
1152+
}
1153+
#endif
10671154

10681155
// broken algorithms (don't always find all key)
10691156
/*

src/bloom.h

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,5 +172,98 @@ std::string BloomFilter<ItemType, bits_per_item, HashFamily, k>::Info() const {
172172
}
173173
return ss.str();
174174
}
175+
176+
177+
178+
179+
/***************
180+
* Simple block filter (naive implementation)
181+
***************/
182+
183+
template<size_t blocksize, int k, typename HashFamily = ::hashing::TwoIndependentMultiplyShift>
184+
class SimpleBlockFilter {
185+
private:
186+
// The filter is divided up into Buckets:
187+
using Bucket = uint64_t[blocksize];
188+
189+
const int bucketCount;
190+
191+
Bucket* directory_;
192+
193+
HashFamily hasher_;
194+
195+
public:
196+
// Consumes at most (1 << log_heap_space) bytes on the heap:
197+
explicit SimpleBlockFilter(const int bits);
198+
~SimpleBlockFilter() noexcept;
199+
void Add(const uint64_t key) noexcept;
200+
201+
bool Find(const uint64_t key) const noexcept;
202+
uint64_t SizeInBytes() const { return sizeof(Bucket) * bucketCount; }
203+
204+
205+
};
206+
207+
template<size_t blocksize,int k, typename HashFamily>
208+
SimpleBlockFilter<blocksize,k,HashFamily>::SimpleBlockFilter(const int capacity)
209+
: bucketCount(capacity * k / ( blocksize * 64)),
210+
directory_(nullptr),
211+
hasher_() {
212+
const size_t alloc_size = bucketCount * sizeof(Bucket);
213+
const int malloc_failed =
214+
posix_memalign(reinterpret_cast<void**>(&directory_), 64, alloc_size);
215+
if (malloc_failed) throw ::std::bad_alloc();
216+
memset(directory_, 0, alloc_size);
217+
}
218+
219+
template<size_t blocksize, int k, typename HashFamily>
220+
SimpleBlockFilter<blocksize,k,HashFamily>::~SimpleBlockFilter() noexcept {
221+
free(directory_);
222+
directory_ = nullptr;
223+
}
224+
225+
static inline uint64_t rotl64(uint64_t n, unsigned int c) {
226+
// assumes width is a power of 2
227+
const unsigned int mask = (CHAR_BIT * sizeof(n) - 1);
228+
// assert ( (c<=mask) &&"rotate by type width or more");
229+
c &= mask;
230+
return (n << c) | ( n >> ((-c) & mask));
231+
}
232+
233+
template<size_t blocksize,int k, typename HashFamily>
234+
inline void
235+
SimpleBlockFilter<blocksize,k, HashFamily>::Add(const uint64_t key) noexcept {
236+
const auto hash = hasher_(key);
237+
const uint32_t bucket_idx = reduce(rotl64(hash, 32), bucketCount);
238+
Bucket * bucket = directory_ + bucket_idx;
239+
uint32_t a = (uint32_t) (hash >> 32);
240+
uint32_t b = (uint32_t) hash;
241+
for (int i = 0; i < k; i++) {
242+
((uint64_t *)bucket)[reduce(a, blocksize)] |= getBit(a);
243+
a += b;
244+
}
245+
246+
}
247+
248+
249+
template<size_t blocksize, int k, typename HashFamily>
250+
inline bool
251+
SimpleBlockFilter<blocksize,k,HashFamily>::Find(const uint64_t key) const noexcept {
252+
const auto hash = hasher_(key);
253+
const uint32_t bucket_idx = reduce(rotl64(hash, 32), bucketCount);
254+
const Bucket * bucket = directory_ + bucket_idx;
255+
uint32_t a = (uint32_t) (hash >> 32);
256+
uint32_t b = (uint32_t) hash;
257+
for (int i = 0; i < k; i++) {
258+
if ((((const uint64_t *)bucket)[reduce(a, blocksize)] & getBit(a)) == 0) {
259+
return false;
260+
}
261+
a += b;
262+
}
263+
return true;
264+
}
265+
266+
267+
175268
} // namespace bloomfilter
176269
#endif // BLOOM_FILTER_BLOOM_FILTER_H_

0 commit comments

Comments
 (0)