Skip to content

fast-pack/simdcomp

Repository files navigation

The SIMDComp library

Build Status

A simple C library for compressing lists of integers using binary packing and SIMD instructions. The assumption is either that you have a list of 32-bit integers where most of them are small, or a list of 32-bit integers where differences between successive integers are small. No software is able to reliably compress an array of 32-bit random numbers.

This library can decode at least 4 billions of compressed integers per second on most desktop or laptop processors. That is, it can decompress data at a rate of 15 GB/s. This is significantly faster than generic codecs like gzip, LZO, Snappy or LZ4.

On a Skylake Intel processor, it can decode integers at a rate 0.3 cycles per integer, which can easily translate into more than 8 decoded billions integers per second.

It runs on both x86/x64 (SSE/AVX) and 64-bit ARM (NEON, e.g. Apple Silicon). See Platforms below.

This library is part of the Awesome C list of C resources.

Contributors: Daniel Lemire, Nathan Kurz, Christoph Rupp, Anatol Belski, Nick White and others

What is it for?

This is a low-level library for fast integer compression. By design it does not define a compressed format. It is up to the (sophisticated) user to create a compressed format.

It is used by:

Requirements

  • On x86/x64: your processor should support SSE4.1 (supported by most Intel and AMD processors released since 2008). The core bit-packing functions only require SSE2 (Pentium4 or better).
  • On ARM: an AArch64/ARM processor with NEON (e.g. Apple Silicon). The SSE intrinsics are mapped to NEON by our own self-contained shim (include/neon128.h); no third-party translation library is pulled in.
  • A C99 (or better) compiler, plus a C++17 compiler if you build the benchmarks.
  • CMake 3.14 or better.

For a plain C version that does not use SIMD instructions, see https://github.com/lemire/LittleIntPacker

Platforms

The library supports two SIMD backends behind the same API:

  • x86 / x64 — Intel/AMD SSE (with optional AVX2 and AVX-512 code paths, enabled automatically when you build with -march=native on a capable host).
  • 64-bit ARM (AArch64) with NEON, such as Apple Silicon. The SSE intrinsics used by the 128-bit kernels are mapped onto ARM NEON by a small, self-contained shim that ships with the library (include/neon128.h). This is our own code written directly against <arm_neon.h>; no third-party translation layer (such as sse2neon) is pulled in. The wider AVX2/AVX-512 paths are x86-only and are simply inactive on ARM.

The public API is identical on both: it is selected automatically at compile time, so the same source (including the __m128i-based entry points) builds on either architecture.

Usage

Compression works over blocks of 128 integers.

For a complete working example, see example/example.c (after building, run it with "./build/example").

  1. Lists of integers in random order.
const uint32_t b = maxbits(datain);// computes bit width
simdpackwithoutmask(datain, buffer, b);//compressed to buffer, compressing 128 32-bit integers down to b*32 bytes
simdunpack(buffer, backbuffer, b);//uncompressed to backbuffer

While 128 32-bit integers are read, only b 128-bit words are written. Thus, the compression ratio is 32/b.

  1. Sorted lists of integers.

We used differential coding: we store the difference between successive integers. For this purpose, we need an initial value (called offset).

uint32_t offset = 0;
uint32_t b1 = simdmaxbitsd1(offset,datain); // bit width
simdpackwithoutmaskd1(offset, datain, buffer, b1);//compressing 128 32-bit integers down to b1*32 bytes
simdunpackd1(offset, buffer, backbuffer, b1);//uncompressed

General example for arrays of arbitrary length:

int compress_decompress_demo() {
  size_t k, N = 9999;
  __m128i * endofbuf;
  uint32_t * datain = malloc(N * sizeof(uint32_t));
  uint8_t * buffer;
  uint32_t * backbuffer = malloc(N * sizeof(uint32_t));
  uint32_t b;

  for (k = 0; k < N; ++k){        /* start with k=0, not k=1! */
    datain[k] = k;
  }

  b = maxbits_length(datain, N);
  buffer = malloc(simdpack_compressedbytes(N,b)); // allocate just enough memory
  endofbuf = simdpack_length(datain, N, (__m128i *)buffer, b);
  /* compressed data is stored between buffer and endofbuf using (endofbuf-buffer)*sizeof(__m128i) bytes */
  /* would be safe to do : buffer = realloc(buffer,(endofbuf-(__m128i *)buffer)*sizeof(__m128i)); */
  simdunpack_length((const __m128i *)buffer, N, backbuffer, b);

  for (k = 0; k < N; ++k){
    if(datain[k] != backbuffer[k]) {
      printf("bug\n");
      return -1;
    }
  }
  return 0;
}
  1. Frame-of-Reference

We also have frame-of-reference (FOR) functions (see simdfor.h header). They work like the bit packing routines, but do not use differential coding so they allow faster search in some cases, at the expense of compression.

Setup

The library builds with CMake:

cmake -B build
cmake --build build
ctest --test-dir build        # run the unit tests

To install the library, headers, and a CMake package configuration:

cmake --install build --prefix /your/install/prefix

Once installed, another CMake project can use it with:

find_package(simdcomp REQUIRED)
target_link_libraries(myapp PRIVATE simdcomp::simdcomp)

Or vendor it directly via FetchContent (no install needed):

include(FetchContent)
FetchContent_Declare(simdcomp
  GIT_REPOSITORY https://github.com/lemire/simdcomp.git
  GIT_TAG master)
FetchContent_MakeAvailable(simdcomp)
target_link_libraries(myapp PRIVATE simdcomp::simdcomp)

Useful options: -DSIMDCOMP_BUILD_BENCHMARKS=OFF (the benchmarks pull in lemire/counters for cycle-accurate timing and require C++17), -DSIMDCOMP_BUILD_TESTS=OFF, and -DSIMDCOMP_NATIVE=OFF (disable -march=native).

The benchmark reports CPU cycles per integer using hardware performance counters when they are available (otherwise it falls back to wall-clock time and throughput):

./build/bitpackingbenchmark        # on Apple Silicon/Linux, run with sudo for cycle counts

Go

If you are a go user, there is a "go" folder where you will find a simple demo.

Other libraries

Other programming languages

References

About

A simple C library for compressing lists of integers using binary packing

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages