- Notifications
You must be signed in to change notification settings - Fork276
Roaring bitmaps in C (and C++), with SIMD (AVX2, AVX-512 and NEON) optimizations: used by Apache Doris, ClickHouse, and StarRocks
License
RoaringBitmap/CRoaring
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Portable Roaring bitmaps in C (and C++) with full support for your favorite compiler (GNU GCC, LLVM's clang, Visual Studio, Apple Xcode, Intel oneAPI). Included in theAwesome C list of open source C software.
Bitsets, also called bitmaps, are commonly used as fast data structures. Unfortunately, they can use too much memory.To compensate, we often use compressed bitmaps.
Roaring bitmaps are compressed bitmaps which tend to outperform conventional compressed bitmaps such as WAH, EWAH or Concise.They are used by several major systems such asApache Lucene and derivative systems such asSolr andElasticsearch,Metamarkets' Druid,LinkedIn Pinot,Netflix Atlas,Apache Spark,OpenSearchServer,Cloud Torrent,Whoosh,InfluxDB,Pilosa,Bleve,Microsoft Visual Studio Team Services (VSTS), and eBay'sApache Kylin. The CRoaring library is used in several systems such asApache Doris,ClickHouse,Redpanda, andStarRocks. The YouTube SQL Engine,Google Procella, uses Roaring bitmaps for indexing.
We published a peer-reviewed article on the design and evaluation of this library:
- Roaring Bitmaps: Implementation of an Optimized Software Library, Software: Practice and Experience 48 (4), 2018arXiv:1709.07821
Roaring bitmaps are found to work well in many important applications:
Use Roaring for bitmap compression whenever possible. Do not use other bitmap compression methods (Wang et al., SIGMOD 2017)
There is a serialized format specification for interoperability between implementations. Hence, it is possible to serialize a Roaring Bitmap from C++, read it in Java, modify it, serialize it back and read it in Go and Python.
The primary goal of the CRoaring is to provide a high performance low-level implementation that fully take advantageof the latest hardware. Roaring bitmaps are already available on a variety of platform through Java, Go, Rust... implementations. CRoaring is a library that seeks to achieve superior performance by staying close to the latest hardware.
(c) 2016-... The CRoaring authors.
- Linux, macOS, FreeBSD, Windows (MSYS2 and Microsoft Visual studio).
- We test the library with ARM, x64/x86 and POWER processors. We only support little endian systems (big endian systems are vanishingly rare).
- Recent C compiler supporting the C11 standard (GCC 7 or better, LLVM 8 or better (clang), Xcode 11 or better, Microsoft Visual Studio 2022 or better, Intel oneAPI Compiler 2023.2 or better), there is also an optional C++ class that requires a C++ compiler supporting the C++11 standard.
- CMake (to contribute to the project, users can rely on amalgamation/unity builds if they do not wish to use CMake).
- The CMake system assumes that git is available.
- Under x64 systems, the library provides runtime dispatch so that optimized functions are called based on the detected CPU features. It works with GCC, clang (version 9 and up) and Visual Studio (2017 and up). Other systems (e.g., ARM) do not need runtime dispatch.
Hardly anyone has access to an actual big-endian system. Nevertheless,We support big-endian systems such as IBM s390x through emulators---except forIO serialization which is only supported on little-endian systems (seeissue 423).
The CRoaring library can be amalgamated into a single source file that makes it easierfor integration into other projects. Moreover, by making it possible to compileall the critical code into one compilation unit, it can improve the performance. Forthe rationale, please see theSQLite documentation,or the correspondingWikipedia entry.Users who choose this route, do not need to rely on CRoaring's build system (based on CMake).
We offer amalgamated files as part of each release.
Linux or macOS users might follow the following instructions if they have a recent C or C++ compiler installed and a standard utility (wget
).
- Pull the library in a directory
wget https://github.com/RoaringBitmap/CRoaring/releases/download/v2.1.0/roaring.cwget https://github.com/RoaringBitmap/CRoaring/releases/download/v2.1.0/roaring.hwget https://github.com/RoaringBitmap/CRoaring/releases/download/v2.1.0/roaring.hh
- Create a new file named
demo.c
with this content:#include<stdio.h>#include<stdlib.h>#include"roaring.c"intmain() {roaring_bitmap_t*r1=roaring_bitmap_create();for (uint32_ti=100;i<1000;i++)roaring_bitmap_add(r1,i);printf("cardinality = %d\n", (int)roaring_bitmap_get_cardinality(r1));roaring_bitmap_free(r1);bitset_t*b=bitset_create();for (intk=0;k<1000;++k) {bitset_set(b,3*k); }printf("%zu \n",bitset_count(b));bitset_free(b);returnEXIT_SUCCESS;}
- Create a new file named
demo.cpp
with this content:#include<iostream>#include"roaring.hh"// the amalgamated roaring.hh includes roaring64map.hh#include"roaring.c"intmain() { roaring::Roaring r1;for (uint32_t i =100; i <1000; i++) { r1.add(i); } std::cout <<"cardinality =" << r1.cardinality() << std::endl; roaring::Roaring64Map r2;for (uint64_t i =18000000000000000100ull; i <18000000000000001000ull; i++) { r2.add(i); } std::cout <<"cardinality =" << r2.cardinality() << std::endl;return0;}
- Compile
cc -o demo demo.cc++ -std=c++11 -o demopp demo.cpp
./demo
cardinality = 9001000
./demopp
cardinality = 900cardinality = 900
If you like CMake and CPM, you can add just a few lines in yourCMakeLists.txt
file to grab aCRoaring
release.See our CPM demonstration for further details.
cmake_minimum_required(VERSION 3.10)project(roaring_demo LANGUAGES CXX C)set(CMAKE_CXX_STANDARD 17)set(CMAKE_C_STANDARD 11)add_executable(hello hello.cpp)# You can add CPM.cmake like so:# mkdir -p cmake# wget -O cmake/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmakeinclude(cmake/CPM.cmake)CPMAddPackage(NAME roaring GITHUB_REPOSITORY"RoaringBitmap/CRoaring" GIT_TAG v2.0.4OPTIONS"BUILD_TESTING OFF")target_link_libraries(hello roaring::roaring)
If you like CMake, you can add just a few lines in yourCMakeLists.txt
file to grab aCRoaring
release.See our demonstration for further details.
If you installed the CRoaring library locally, you may use it with CMake'sfind_package
function as in this example:
cmake_minimum_required(VERSION 3.15)project(test_roaring_installVERSION 0.1.0 LANGUAGES CXX C)set(CMAKE_CXX_STANDARD 11)set(CMAKE_CXX_STANDARD_REQUIREDON)set(CMAKE_C_STANDARD 11)set(CMAKE_C_STANDARD_REQUIREDON)find_package(roaring REQUIRED)file(WRITE main.cpp"#include <iostream>#include\"roaring/roaring.hh\"int main() { roaring::Roaring r1; for (uint32_t i = 100; i < 1000; i++) { r1.add(i); } std::cout <<\"cardinality =\" << r1.cardinality() << std::endl; return 0;}")add_executable(repro main.cpp)target_link_libraries(reproPUBLIC roaring::roaring)
To generate the amalgamated files yourself, you can invoke a bash script...
./amalgamation.sh
If you prefer a silent output, you can use the following command to redirectstdout
:
./amalgamation.sh> /dev/null
(Bash shells are standard under Linux and macOS. Bash shells are available under Windows as part of the GitHub Desktop under the nameGit Shell
. So if you have cloned theCRoaring
GitHub repository from within the GitHub Desktop, you can right-click onCRoaring
, selectGit Shell
and then enter the above commands.)
It is not necessary to invoke the script in the CRoaring directory. You can invokeit from any directory where you want the amalgamation files to be written.
It will generate three files for C users:roaring.h
,roaring.c
andamalgamation_demo.c
... as well as some brief instructions. Theamalgamation_demo.c
file is a short example, whereasroaring.h
androaring.c
are "amalgamated" files (including all source and header files for the project). This means that you can simply copy the filesroaring.h
androaring.c
into your project and be ready to go! No need to produce a library! See theamalgamation_demo.c
file.
The C interface is found in the files
We also have a C++ interface:
Some users have to deal with large volumes of data. It may be important for these users to be aware of theaddMany
(C++)roaring_bitmap_or_many
(C) functions as it is much faster and economical to add values in batches when possible. Furthermore, calling periodically therunOptimize
(C++) orroaring_bitmap_run_optimize
(C) functions may help.
We have microbenchmarks constructed with the Google Benchmarks.Under Linux or macOS, you may run them as follows:
cmake -B build -D ENABLE_ROARING_MICROBENCHMARKS=ONcmake --build build./build/microbenchmarks/bench
By default, the benchmark tools picks one data set (e.g.,CRoaring/benchmarks/realdata/census1881
).We have several data sets and you may pick others:
./build/microbenchmarks/bench benchmarks/realdata/wikileaks-noquotes
You may disable some functionality for the purpose of benchmarking. For example, assuming youhave an x64 processor, you could benchmark the code without AVX-512 even if both your processorand compiler supports it:
cmake -B buildnoavx512 -D ROARING_DISABLE_AVX512=ON -D ENABLE_ROARING_MICROBENCHMARKS=ONcmake --build buildnoavx512./buildnoavx512/microbenchmarks/bench
You can benchmark without AVX or AVX-512 as well:
cmake -B buildnoavx -D ROARING_DISABLE_AVX=ON -D ENABLE_ROARING_MICROBENCHMARKS=ONcmake --build buildnoavx./buildnoavx/microbenchmarks/bench
For general users, CRoaring would apply default allocator without extra codes. But global memory hook is also provided for those who want a custom memory allocator. Here is an example:
#include<roaring.h>intmain(){// define with your own memory hookroaring_memory_tmy_hook{my_malloc,my_free ...};// initialize global memory hookroaring_init_memory_hook(my_hook);// write you code here ...}
By default we use:
staticroaring_memory_tglobal_memory_hook= { .malloc=malloc, .realloc=realloc, .calloc=calloc, .free=free, .aligned_malloc=roaring_bitmap_aligned_malloc, .aligned_free=roaring_bitmap_aligned_free,};
We require that thefree
/aligned_free
functions follow the Cconvention wherefree(NULL)
/aligned_free(NULL)
have no effect.
This example assumes that CRoaring has been build and that you are linking against the corresponding library. By default, CRoaring will install its header files in aroaring
directory. If you are working from the amalgamation script, you may add the line#include "roaring.c"
if you are not linking against a prebuilt CRoaring library and replace#include <roaring/roaring.h>
by#include "roaring.h"
.
#include<roaring/roaring.h>#include<stdio.h>#include<stdlib.h>#include<assert.h>boolroaring_iterator_sumall(uint32_tvalue,void*param) {*(uint32_t*)param+=value;return true;// iterate till the end}intmain() {// create a new empty bitmaproaring_bitmap_t*r1=roaring_bitmap_create();// then we can add valuesfor (uint32_ti=100;i<1000;i++)roaring_bitmap_add(r1,i);// check whether a value is containedassert(roaring_bitmap_contains(r1,500));// compute how many bits there are:uint32_tcardinality=roaring_bitmap_get_cardinality(r1);printf("Cardinality = %d \n",cardinality);// if your bitmaps have long runs, you can compress them by calling// run_optimizeuint32_texpectedsizebasic=roaring_bitmap_portable_size_in_bytes(r1);roaring_bitmap_run_optimize(r1);uint32_texpectedsizerun=roaring_bitmap_portable_size_in_bytes(r1);printf("size before run optimize %d bytes, and after %d bytes\n",expectedsizebasic,expectedsizerun);// create a new bitmap containing the values {1,2,3,5,6}roaring_bitmap_t*r2=roaring_bitmap_from(1,2,3,5,6);roaring_bitmap_printf(r2);// print it// we can also create a bitmap from a pointer to 32-bit integersuint32_tsomevalues[]= {2,3,4};roaring_bitmap_t*r3=roaring_bitmap_of_ptr(3,somevalues);// we can also go in reverse and go from arrays to bitmapsuint64_tcard1=roaring_bitmap_get_cardinality(r1);uint32_t*arr1= (uint32_t*)malloc(card1*sizeof(uint32_t));assert(arr1!=NULL);roaring_bitmap_to_uint32_array(r1,arr1);roaring_bitmap_t*r1f=roaring_bitmap_of_ptr(card1,arr1);free(arr1);assert(roaring_bitmap_equals(r1,r1f));// what we recover is equalroaring_bitmap_free(r1f);// we can go from arrays to bitmaps from "offset" by "limit"size_toffset=100;size_tlimit=1000;uint32_t*arr3= (uint32_t*)malloc(limit*sizeof(uint32_t));assert(arr3!=NULL);roaring_bitmap_range_uint32_array(r1,offset,limit,arr3);free(arr3);// we can copy and compare bitmapsroaring_bitmap_t*z=roaring_bitmap_copy(r3);assert(roaring_bitmap_equals(r3,z));// what we recover is equalroaring_bitmap_free(z);// we can compute union two-by-tworoaring_bitmap_t*r1_2_3=roaring_bitmap_or(r1,r2);roaring_bitmap_or_inplace(r1_2_3,r3);// we can compute a big unionconstroaring_bitmap_t*allmybitmaps[]= {r1,r2,r3};roaring_bitmap_t*bigunion=roaring_bitmap_or_many(3,allmybitmaps);assert(roaring_bitmap_equals(r1_2_3,bigunion));// what we recover is equal// can also do the big union with a heaproaring_bitmap_t*bigunionheap=roaring_bitmap_or_many_heap(3,allmybitmaps);assert(roaring_bitmap_equals(r1_2_3,bigunionheap));roaring_bitmap_free(r1_2_3);roaring_bitmap_free(bigunion);roaring_bitmap_free(bigunionheap);// we can compute intersection two-by-tworoaring_bitmap_t*i1_2=roaring_bitmap_and(r1,r2);roaring_bitmap_free(i1_2);// we can write a bitmap to a pointer and recover it lateruint32_texpectedsize=roaring_bitmap_portable_size_in_bytes(r1);char*serializedbytes=malloc(expectedsize);// When serializing data to a file, we recommend that you also use// checksums so that, at deserialization, you can be confident// that you are recovering the correct data.roaring_bitmap_portable_serialize(r1,serializedbytes);// Note: it is expected that the input follows the specification// https://github.com/RoaringBitmap/RoaringFormatSpec// otherwise the result may be unusable.// The 'roaring_bitmap_portable_deserialize_safe' function will not read// beyond expectedsize bytes.// We also recommend that you use checksums to check that serialized data corresponds// to the serialized bitmap. The CRoaring library does not provide checksumming.roaring_bitmap_t*t=roaring_bitmap_portable_deserialize_safe(serializedbytes,expectedsize);if(t==NULL) {returnEXIT_FAILURE; }constchar*reason=NULL;// If your input came from an untrusted source, then you need to validate the// resulting bitmap. Failing to do so could lead to undefined behavior, crashes and so forth.if (!roaring_bitmap_internal_validate(t,&reason)) {returnEXIT_FAILURE; }// At this point, the bitmap is safe.assert(roaring_bitmap_equals(r1,t));// what we recover is equalroaring_bitmap_free(t);// we can also check whether there is a bitmap at a memory location without// reading itsize_tsizeofbitmap=roaring_bitmap_portable_deserialize_size(serializedbytes,expectedsize);assert(sizeofbitmap==expectedsize);// sizeofbitmap would be zero if no bitmap were found// We can also read the bitmap "safely" by specifying a byte size limit.// The 'roaring_bitmap_portable_deserialize_safe' function will not read// beyond expectedsize bytes.// We also recommend that you use checksums to check that serialized data corresponds// to the serialized bitmap. The CRoaring library does not provide checksumming.t=roaring_bitmap_portable_deserialize_safe(serializedbytes,expectedsize);if(t==NULL) {printf("Problem during deserialization.\n");// We could clear any memory and close any file here.returnEXIT_FAILURE; }// We can validate the bitmap we recovered to make sure it is proper.// If the data came from an untrusted source, you should call// roaring_bitmap_internal_validate.constchar*reason_failure=NULL;if (!roaring_bitmap_internal_validate(t,&reason_failure)) {printf("safely deserialized invalid bitmap: %s\n",reason_failure);// We could clear any memory and close any file here.returnEXIT_FAILURE; }assert(roaring_bitmap_equals(r1,t));// what we recover is equalroaring_bitmap_free(t);free(serializedbytes);// we can iterate over all values using custom functionsuint32_tcounter=0;roaring_iterate(r1,roaring_iterator_sumall,&counter);// we can also create iterator structscounter=0;roaring_uint32_iterator_t*i=roaring_iterator_create(r1);while (i->has_value) {counter++;// could use i->current_valueroaring_uint32_iterator_advance(i); }// you can skip over values and move the iterator with// roaring_uint32_iterator_move_equalorlarger(i,someintvalue)roaring_uint32_iterator_free(i);// roaring_bitmap_get_cardinality(r1) == counter// for greater speed, you can iterate over the data in bulki=roaring_iterator_create(r1);uint32_tbuffer[256];while (1) {uint32_tret=roaring_uint32_iterator_read(i,buffer,256);for (uint32_tj=0;j<ret;j++) {counter+=buffer[j]; }if (ret<256) {break; } }roaring_uint32_iterator_free(i);roaring_bitmap_free(r1);roaring_bitmap_free(r2);roaring_bitmap_free(r3);returnEXIT_SUCCESS;}
We also support efficient 64-bit compressed bitmaps in C:
roaring64_bitmap_t *r2 = roaring64_bitmap_create();for (uint64_t i =100; i <1000; i++) roaring64_bitmap_add(r2, i);printf("cardinality (64-bit) = %d\n", (int) roaring64_bitmap_get_cardinality(r2));roaring64_bitmap_free(r2);
The API is similar to the conventional 32-bit bitmaps. Please seethe header fileroaring64.h
(compare withroaring.h
).
We support convention bitsets (uncompressed) as part of the library.
Simple example:
bitset_t*b=bitset_create();bitset_set(b,10);bitset_get(b,10);// returns truebitset_free(b);// frees memory
More advanced example:
bitset_t*b=bitset_create();for (intk=0;k<1000;++k) {bitset_set(b,3*k);}// We have bitset_count(b) == 1000.// We have bitset_get(b, 3) is true// You can iterate through the values:size_tk=0;for (size_ti=0;bitset_next_set_bit(b,&i);i++) {// You will have i == kk+=3;}// We support a wide range of operations on two bitsets such as// bitset_inplace_symmetric_difference(b1,b2);// bitset_inplace_symmetric_difference(b1,b2);// bitset_inplace_difference(b1,b2);// should make no difference// bitset_inplace_union(b1,b2);// bitset_inplace_intersection(b1,b2);// bitsets_disjoint// bitsets_intersect
In some instances, you may want to convert a Roaring bitmap into a conventional (uncompressed) bitset.Indeed, bitsets have advantages such as higher query performances in some cases. The following codeillustrates how you may do so:
roaring_bitmap_t*r1=roaring_bitmap_create();for (uint32_ti=100;i<100000;i+=1+ (i%5)) {roaring_bitmap_add(r1,i);}for (uint32_ti=100000;i<500000;i+=100) {roaring_bitmap_add(r1,i);}roaring_bitmap_add_range(r1,500000,600000);bitset_t*bitset=bitset_create();boolsuccess=roaring_bitmap_to_bitset(r1,bitset);assert(success);// could fail due to memory allocation.assert(bitset_count(bitset)==roaring_bitmap_get_cardinality(r1));// You can then query the bitset:for (uint32_ti=100;i<100000;i+=1+ (i%5)) {assert(bitset_get(bitset,i));}for (uint32_ti=100000;i<500000;i+=100) {assert(bitset_get(bitset,i));}// you must free the memory:bitset_free(bitset);roaring_bitmap_free(r1);
You should be aware that a convention bitset (bitset_t *
) may use much morememory than a Roaring bitmap in some cases. You should run benchmarks to determinewhether the conversion to a bitset has performance benefits in your case.
This example assumes that CRoaring has been build and that you are linking against the corresponding library. By default, CRoaring will install its header files in aroaring
directory so you may need to replace#include "roaring.hh"
by#include <roaring/roaring.hh>
. If you are working from the amalgamation script, you may add the line#include "roaring.c"
if you are not linking against a CRoaring prebuilt library.
#include<iostream>#include"roaring.hh"usingnamespaceroaring;intmain() { Roaring r1;for (uint32_t i =100; i <1000; i++) { r1.add(i); }// check whether a value is containedassert(r1.contains(500));// compute how many bits there are:uint32_t cardinality = r1.cardinality();// if your bitmaps have long runs, you can compress them by calling// run_optimizeuint32_t size = r1.getSizeInBytes(); r1.runOptimize();// you can enable "copy-on-write" for fast and shallow copies r1.setCopyOnWrite(true);uint32_t compact_size = r1.getSizeInBytes(); std::cout <<"size before run optimize" << size <<" bytes, and after" << compact_size <<" bytes." << std::endl;// create a new bitmap with varargs Roaring r2 =Roaring::bitmapOf(5,1,2,3,5,6); r2.printf();printf("\n");// create a new bitmap with initializer list Roaring r2i =Roaring::bitmapOfList({1,2,3,5,6});assert(r2i == r2);// we can also create a bitmap from a pointer to 32-bit integersconstuint32_t values[] = {2,3,4}; Roaringr3(3, values);// we can also go in reverse and go from arrays to bitmapsuint64_t card1 = r1.cardinality();uint32_t *arr1 =newuint32_t[card1]; r1.toUint32Array(arr1); Roaringr1f(card1, arr1);delete[] arr1;// bitmaps shall be equalassert(r1 == r1f);// we can copy and compare bitmaps Roaringz(r3);assert(r3 == z);// we can compute union two-by-two Roaring r1_2_3 = r1 | r2; r1_2_3 |= r3;// we can compute a big unionconst Roaring *allmybitmaps[] = {&r1, &r2, &r3}; Roaring bigunion =Roaring::fastunion(3, allmybitmaps);assert(r1_2_3 == bigunion);// we can compute intersection two-by-two Roaring i1_2 = r1 & r2;// we can write a bitmap to a pointer and recover it lateruint32_t expectedsize = r1.getSizeInBytes();char *serializedbytes =newchar[expectedsize]; r1.write(serializedbytes);// readSafe will not overflow, but the resulting bitmap// is only valid and usable if the input follows the// Roaring specification: https://github.com/RoaringBitmap/RoaringFormatSpec/ Roaring t =Roaring::readSafe(serializedbytes, expectedsize);assert(r1 == t);delete[] serializedbytes;// we can iterate over all values using custom functionsuint32_t counter =0; r1.iterate( [](uint32_t value,void *param) { *(uint32_t *)param += value;returntrue; }, &counter);// we can also iterate the C++ way counter =0;for (Roaring::const_iterator i = t.begin(); i != t.end(); i++) { ++counter; }// counter == t.cardinality()// we can move iterators to skip valuesconstuint32_t manyvalues[] = {2,3,4,7,8}; Roaringrogue(5, manyvalues); Roaring::const_iterator j = rogue.begin(); j.equalorlarger(4);// *j == 4return EXIT_SUCCESS;}
CRoaring follows the standard cmake workflow. Starting from the root directory ofthe project (CRoaring), you can do:
mkdir -p buildcd buildcmake ..cmake --build .# follow by 'ctest' if you want to test.# you can also type 'make install' to install the library on your system# C header files typically get installed to /usr/local/include/roaring# whereas C++ header files get installed to /usr/local/include/roaring
(You can replace thebuild
directory with any other directory name.)By default all tests are built on all platforms, to skip building and running tests add-DENABLE_ROARING_TESTS=OFF
to the command line.
As with allcmake
projects, you can specify the compilers you wish to use by adding (for example)-DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++
to thecmake
command line.
If you are using clang or gcc and you know your target architecture, you can set the architecture by specifying-DROARING_ARCH=arch
. For example, if you have many server but the oldest server is running the Intelhaswell
architecture, you can specify -DROARING_ARCH=haswell
. In such cases, the produced binary will be optimized for processors having the characteristics of a haswell process and may not run on older architectures. You can find out the list of valid architecture values by typingman gcc
.
mkdir -p build_haswellcd build_haswellcmake -DROARING_ARCH=haswell ..cmake --build .
For a debug release, starting from the root directory of the project (CRoaring), try
mkdir -p debugcd debugcmake -DCMAKE_BUILD_TYPE=Debug -DROARING_SANITIZE=ON ..ctest
To check that your code abides by the style convention (make sure thatclang-format
is installed):
./tools/clang-format-check.sh
To reformat your code according to the style convention (make sure thatclang-format
is installed):
./tools/clang-format.sh
We are assuming that you have a common Windows PC with at least Visual Studio 2015, and an x64 processor.
To build with at least Visual Studio 2015 from the command line:
- Grab the CRoaring code from GitHub, e.g., by cloning it usingGitHub Desktop.
- InstallCMake. When you install it, make sure to ask that
cmake
be made available from the command line. - Create a subdirectory within CRoaring, such as
VisualStudio
. - Using a shell, go to this newly created directory. For example, within GitHub Desktop, you can right-click on
CRoaring
in your GitHub repository list, and selectOpen in Git Shell
, then typecd VisualStudio
in the newly created shell. - Type
cmake -DCMAKE_GENERATOR_PLATFORM=x64 ..
in the shell while in theVisualStudio
repository. (Alternatively, if you want to build a static library, you may use the command linecmake -DCMAKE_GENERATOR_PLATFORM=x64 -DROARING_BUILD_STATIC=ON ..
.) - This last command created a Visual Studio solution file in the newly created directory (e.g.,
RoaringBitmap.sln
). Open this file in Visual Studio. You should now be able to build the project and run the tests. For example, in theSolution Explorer
window (available from theView
menu), right-clickALL_BUILD
and selectBuild
. To test the code, still in theSolution Explorer
window, selectRUN_TESTS
and selectBuild
.
To build with at least Visual Studio 2017 directly in the IDE:
- Grab the CRoaring code from GitHub, e.g., by cloning it usingGitHub Desktop.
- Select the
Visual C++ tools for CMake
optional component when installing the C++ Development Workload within Visual Studio. - Within Visual Studio use
File > Open > Folder...
to open the CRoaring folder. - Right click on
CMakeLists.txt
in the parent directory withinSolution Explorer
and selectBuild
to build the project. - For testing, in the Standard toolbar, drop the
Select Startup Item...
menu and choose one of the tests. Run the test by pressing the button to the left of the dropdown.
We have optimizations specific to AVX2 and AVX-512 in the code, and they are turned dynamically based on the detected hardware at runtime.
You can install pre-built binaries forroaring
or build it from source usingConan. Use the following command to install latest version:
conan install --requires="roaring/[*]" --build=missing
For detailed instructions on how to use Conan, please refer to theConan documentation.
Theroaring
Conan recipe is kept up to date by Conan maintainers and community contributors.If the version is out of date, pleasecreate an issue or pull request on the ConanCenterIndex repository.
vcpkg users on Windows, Linux and macOS can download and installroaring
with one single command from their favorite shell.
On Linux and macOS:
$ ./vcpkg install roaring
will build and installroaring
as a static library.
On Windows (64-bit):
.\vcpkg.exe install roaring:x64-windows
will build and installroaring
as a shared library.
.\vcpkg.exe install roaring:x64-windows-static
will build and installroaring
as a static library.
These commands will also print out instructions on how to use the library from MSBuild or CMake-based projects.
If you find the version ofroaring
shipped withvcpkg
is out-of-date, feel free to report it tovcpkg
community either by submiting an issue or by creating a PR.
Our AVX2 code does not use floating-point numbers or multiplications, so it is not subject to turbo frequency throttling on many-core Intel processors.
Our AVX-512 code is only enabled on recent hardware (Intel Ice Lake or better and AMD Zen 4) where SIMD-specific frequency throttling is not observed.
Like, for example, STL containers, the CRoaring library has no built-in thread support. Thus whenever you modify a bitmap in one thread, it is unsafe to query it in others. However, you can safely copy a bitmap and use both copies in concurrently.
If you use "copy-on-write" (default to disabled), then you should pass copies to the different threads. They will create shared containers, and for shared containers, we use reference counting with an atomic counter.
To summarize:
- If you do not use copy-on-write, you can access concurrent the same bitmap safely as long as you do not modify it. If you plan on modifying it, you should pass different copies to the different threads.
- If you use copy-on-write, you should always pass copies to the different threads. The copies and then lightweight (shared containers).
Thus the following pattern where you copy bitmaps and pass them to different threads is safe with or without COW:
roaring_bitmap_set_copy_on_write(r1, true);roaring_bitmap_set_copy_on_write(r2, true);roaring_bitmap_set_copy_on_write(r3, true);roaring_bitmap_t*r1a=roaring_bitmap_copy(r1);roaring_bitmap_t*r1b=roaring_bitmap_copy(r1);roaring_bitmap_t*r2a=roaring_bitmap_copy(r2);roaring_bitmap_t*r2b=roaring_bitmap_copy(r2);roaring_bitmap_t*r3a=roaring_bitmap_copy(r3);roaring_bitmap_t*r3b=roaring_bitmap_copy(r3);roaring_bitmap_t*rarray1[3]= {r1a,r2a,r3a};roaring_bitmap_t*rarray2[3]= {r1b,r2b,r3b};std::threadthread1(run,rarray1);std::threadthread2(run,rarray2);
Suppose you want to compute the union (OR) of many bitmaps. How do you proceed? There are manydifferent strategies.
You can useroaring_bitmap_or_many(bitmapcount, bitmaps)
orroaring_bitmap_or_many_heap(bitmapcount, bitmaps)
or you mayeven roll your own aggregation:
roaring_bitmap_t*answer=roaring_bitmap_copy(bitmaps[0]);for (size_ti=1;i<bitmapcount;i++) {roaring_bitmap_or_inplace(answer,bitmaps[i]);}
All of them will work but they have different performance characteristics. Theroaring_bitmap_or_many_heap
shouldprobably only be used if, after benchmarking, you find that it is faster by a good margin: it uses more memory.
Theroaring_bitmap_or_many
is meant as a good default. It works by trying to delay work as much as possible.However, because it delays computations, it also does not optimize the format as the computation runs. It mightthus fail to see some useful pattern in the data such as long consecutive values.
The approach based on repeated calls toroaring_bitmap_or_inplace
is also fine, and might even be faster in some cases. You can expect it to be faster if, aftera few calls, you get long sequences of consecutive values in the answer. That is, if thefinal answer is all integers in the range [0,1000000), and this is apparent quickly, then thelaterroaring_bitmap_or_inplace
will be very fast.
You should benchmark these alternatives on your own data to decide what is best.
Tom Cornebize wrote a Python wrapper available athttps://github.com/Ezibenroc/PyRoaringBitMapInstalling it is as easy as typing...
pip install pyroaring
Salvatore Previti wrote a Node/JavaScript wrapper available athttps://github.com/SalvatorePreviti/roaring-nodeInstalling it is as easy as typing...
npm install roaring
Jérémie Piotte wrote aSwift wrapper.
Brandon Smith wrote a C# wrapper available athttps://github.com/RogueException/CRoaring.Net (works for Windows and Linux under x64 processors)
There is a Go (golang) wrapper available athttps://github.com/RoaringBitmap/gocroaring
Saulius Grigaliunas wrote a Rust wrapper available athttps://github.com/saulius/croaring-rs
Yuce Tekol wrote a D wrapper available athttps://github.com/yuce/droaring
Antonio Guilherme Ferreira Viggiano wrote a Redis Module available athttps://github.com/aviggiano/redis-roaring
Justin Whear wrote a Zig wrapper available athttps://github.com/jwhear/roaring-zig
https://groups.google.com/forum/#!forum/roaring-bitmaps
When contributing a change to the project, please runtools/clang-format.sh
after making any changes. A github action runs on all PRs to ensure formatting is consistent with this.
- Daniel Lemire, Owen Kaser, Nathan Kurz, Luca Deri, Chris O'Hara, François Saint-Jacques, Gregory Ssi-Yan-Kai, Roaring Bitmaps: Implementation of an Optimized Software Library, Software: Practice and Experience Volume 48, Issue 4 April 2018 Pages 867-895arXiv:1709.07821
- Samy Chambi, Daniel Lemire, Owen Kaser, Robert Godin,Better bitmap performance with Roaring bitmaps,Software: Practice and Experience Volume 46, Issue 5, pages 709–719, May 2016arXiv:1402.6407
- Daniel Lemire, Gregory Ssi-Yan-Kai, Owen Kaser, Consistently faster and smaller compressed bitmaps with Roaring, Software: Practice and Experience Volume 46, Issue 11, pages 1547-1569, November 2016arXiv:1603.06549
- Samy Chambi, Daniel Lemire, Robert Godin, Kamel Boukhalfa, Charles Allen, Fangjin Yang, Optimizing Druid with Roaring bitmaps, IDEAS 2016, 2016.http://r-libre.teluq.ca/950/
About
Roaring bitmaps in C (and C++), with SIMD (AVX2, AVX-512 and NEON) optimizations: used by Apache Doris, ClickHouse, and StarRocks