RoaringBitmap / RoaringBitmap/CRoaring
WASM SIMD128 backend
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 1.9k
- Forks
- 334
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 17
Description
Is your feature request related to a problem? Please describe.
When CRoaring is compiled for wasm32 (e.g. wasm32-wasi for browser / Node consumption), every container loop falls back to scalar — the x86 (CROARING_IS_X64) and ARM (__ARM_NEON) SIMD paths in include/roaring/portability.h do not fire, and there is no WASM SIMD128 equivalent. Browser-deployed CRoaring therefore runs the same bitset AND / OR / XOR / ANDNOT and popcount inner loops as a native scalar build, even though WASM SIMD128 has been broadly shipped since 2022 (Chrome 91+, Firefox 122+, Safari 16.4+).
We've confirmed this is not something Clang's auto-vectorizer recovers from. Compiling CRoaring v4.6.1 with -msimd128 and inspecting the output with wasm-objdump -d:
# at -Os (36,297 bytes):
$ wasm-objdump -d facetsearch-core.wasm \
| grep -oE 'v128\.[a-z0-9._]+|[fi][0-9]+x[0-9]+\.[a-z0-9._]+' | wc -l
0
# at -O3 (846,074 bytes):
0
Zero v128 opcodes at either level. The container loops are too branch-heavy for scalar-evolution vectorization; a hand-written SIMD backend is required to close the gap.
Describe the solution you'd like
A third arch branch in portability.h alongside the existing two:
#if defined(__wasm_simd128__)
#define CROARING_IS_WASM 1
#include <wasm_simd128.h>
#endif
…and WASM-SIMD versions of the container-internal hot loops that already have SSE2 counterparts. SSE2 → WASM SIMD128 maps directly for most of the ops CRoaring uses:
| SSE2 | WASM SIMD128 |
|---|---|
_mm_and_si128 |
wasm_v128_and |
_mm_or_si128 |
wasm_v128_or |
_mm_xor_si128 |
wasm_v128_xor |
_mm_andnot_si128 |
wasm_v128_andnot |
_mm_loadu_si128 |
wasm_v128_load |
_mm_storeu_si128 |
wasm_v128_store |
__builtin_popcountll × 2 |
wasm_i64x2_popcnt (v128 popcount is 8-bit; bitset popcount needs a short reduce) |
Rationale / expected objective benefit
The highest-leverage targets are bitset AND / OR / XOR / ANDNOT in src/containers/bitset.c and the bitset cardinality popcount. Based on the SSE2 vs scalar delta on the same loops on native builds, we'd expect roughly 2–4× on bitset-heavy workloads once WASM SIMD128 is used — exactly the speedup x86/ARM users already
get.
Expected concrete wins for browser CRoaring consumers:
- Bitset intersection / union / xor / andnot — 4× 64-bit lanes per 128-bit op; currently 1-lane scalar.
- Bitset cardinality (
roaring_cardinality,intersect_cardinality,union_cardinality) —wasm_i64x2_popcntvectorizes the hot popcount loop. - Array container intersection / union (sorted u16) —
wasm_i16x8_*min/max/eq for the galloping-search and shuffle- merge steps. Matches the SSE2 implementation insrc/containers/array.c.
Describe alternatives you've considered
-
Enabling
-msimd128and relying on Clang's auto-vectorizer. Measured above: zero v128 opcodes produced, at any optimization level. Auto-vectorization is not a viable alternative for the container loops. -
SIMDe shim layer (
simde/wasm/simd128.hfor SSE2-style intrinsics, orsimde/x86/sse2.hmapped to WASM SIMD when__wasm_simd128__is set). Would in principle let the existing SSE2 code paths compile on WASM with minimal changes, but adds a header-only dependency and its code-gen on the bitset popcount path is known to produce slightly worse shuffles than hand-writtenwasm_i64x2_popcnt. Mentioning it for completeness — it's the way to unblock this without a full per-op port.
Additional context
- Tested CRoaring version: v4.6.1 (tarball sha256
dd5c73c464be2df05b3472cce00d2d753b7eed0856752d88b23058aaff542216). - Toolchain: Zig 0.13.0 vendoring Clang 17, target
wasm32-wasi, reactor exec model. - Runtime support: browsers with WASM SIMD128 today cover >95% of global traffic. No runtime dispatch is needed if a consumer ships two builds (SIMD + scalar), same model as x86 SSE2 vs AVX2; or the SIMD build can simply be the baseline for evergreen-browser-only projects.
Are you willing to contribute code or documentation toward this new feature?
Yes
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the architecture branches in include/roaring/portability.h, then inspect the SSE2 hot loops in src/containers/bitset.c and src/containers/array.c. Review how the existing counterparts handle bitwise operations, loads, stores, and cardinality, and check wasm-objdump output from a wasm32 build. Done means the targeted loops emit WASM SIMD128 instructions and preserve the existing behavior and performance goals.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, wasm
- Domain
- performance
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100