Recipes¶
Task-oriented, complete, runnable. Every listing below is extracted verbatim from a compiled,
CI-tested program in examples/
(REQ-DOC-006: documentation never free-types C++), so if a recipe and the repository disagree, the
repository has already won. Build them all with cmake --preset bench && cmake --build --preset
bench or compile any one against the two-file drop-in.
Filter a column¶
Compare into a selection bitmap, then compact the surviving values. The two calls at the heart of every WHERE clause:
int main() {
const std::vector<std::int32_t> in = {5, 1, 9, 3, 7, 2, 8, 4, 6, 0};
const auto n = static_cast<std::int64_t>(in.size());
std::vector<std::uint8_t> selected((static_cast<std::size_t>(n) + 7) / 8);
// K1: which elements are > 4? (null validity = all valid)
const std::int64_t hits =
quiver::compare_bitmap(quiver::CompareOp::kGt, quiver::BatchView<std::int32_t>{in.data(), n},
4, quiver::BitmapView{nullptr}, selected.data());
// K2: compact those elements to the front of `out`.
std::vector<std::int32_t> out(static_cast<std::size_t>(n));
const std::int64_t kept = quiver::filter(quiver::BatchView<std::int32_t>{in.data(), n},
quiver::BitmapView{selected.data()}, out.data());
std::printf("%lld elements > 4 (bitmap popcount %lld):", static_cast<long long>(kept),
static_cast<long long>(hits));
for (std::int64_t i = 0; i < kept; ++i) {
std::printf(" %d", out[static_cast<std::size_t>(i)]);
}
std::printf("\n");
return 0;
}
Compare, gather, reduce: a small pipeline¶
The same predicate as a selection vector (row indices instead of bits), a gather through it, and a null-aware sum. This is the compare → take → reduce composition analytical code repeats endlessly:
int main() {
const std::vector<std::int32_t> in = {5, 1, 9, 3, 7, 2, 8, 4, 6, 0};
// K1: indices of elements >= 5. The output span's capacity is checked (assertion builds);
// the returned subspan is exactly the matches.
std::vector<std::uint32_t> selection_storage(in.size());
const auto selected =
quiver::compare_selvec(quiver::CompareOp::kGe, in, 5, std::span{selection_storage});
// K5: gather those elements, packed.
std::vector<std::int32_t> picked_storage(selected.size());
const auto picked = quiver::take(in, selected, std::span{picked_storage});
// K6: sum them (wrapping into the wider accumulator).
const auto sum = quiver::reduce_sum_wrap(picked);
std::printf("selected %zu elements >= 5; sum = %lld\n", picked.size(),
static_cast<long long>(sum));
return 0;
}
Inspect and force the ISA tier¶
Which backend is running, how to pin one at run time, and the guarantee that makes pinning safe to test with: results are identical across ISA tiers, bit for bit:
namespace {
const char* isa_name(quiver::Isa isa) {
switch (isa) {
case quiver::Isa::kScalar:
return "scalar";
case quiver::Isa::kNeon:
return "neon";
case quiver::Isa::kAvx2:
return "avx2";
case quiver::Isa::kAvx512:
return "avx512";
}
return "?";
}
std::int64_t count_gt_zero(const std::vector<std::int32_t>& in) {
std::vector<std::uint8_t> bits((in.size() + 7) / 8);
return quiver::compare_bitmap(
quiver::CompareOp::kGt,
quiver::BatchView<std::int32_t>{in.data(), static_cast<std::int64_t>(in.size())}, 0,
quiver::BitmapView{nullptr}, bits.data());
}
} // namespace
int main() {
const std::vector<std::int32_t> in = {-2, 3, 0, 5, -1, 8};
std::printf("default active ISA: %s\n", isa_name(quiver::active_isa()));
const std::int64_t base = count_gt_zero(in);
// Force the scalar tier (always accepted) and confirm the result is unchanged.
quiver::set_isa_override(quiver::Isa::kScalar);
std::printf("forced active ISA: %s\n", isa_name(quiver::active_isa()));
const std::int64_t scalar_count = count_gt_zero(in);
quiver::clear_isa_override();
std::printf("positives: %lld (scalar tier agrees: %s)\n", static_cast<long long>(base),
base == scalar_count ? "yes" : "NO");
return base == scalar_count ? 0 : 1;
}
Work with nulls (validity bitmaps)¶
Validity bitmaps are LSB-first, one bit per row, 1 = valid. They combine with mask algebra and flow through compare and reduce so null lanes never contribute:
namespace {
// Pack a bool-per-element vector into an LSB-first validity bitmap.
std::vector<std::uint8_t> pack(const std::vector<int>& flags) {
std::vector<std::uint8_t> bits((flags.size() + 7) / 8);
for (std::size_t i = 0; i < flags.size(); ++i) {
if (flags[i]) {
bits[i >> 3] = static_cast<std::uint8_t>(bits[i >> 3] | (1u << (i & 7)));
}
}
return bits;
}
} // namespace
int main() {
const std::vector<std::int32_t> in = {10, 20, 30, 40, 50, 60};
const auto n = static_cast<std::int64_t>(in.size());
const auto valid = pack({1, 1, 0, 1, 0, 1}); // elements 2 and 4 are null
const auto second = pack({1, 0, 1, 1, 1, 1}); // a second validity mask to combine
// K4: AND the two validity bitmaps -> a lane is live only where both agree.
std::vector<std::uint8_t> live((static_cast<std::size_t>(n) + 7) / 8);
quiver::mask_combine(quiver::MaskOp::kAnd, quiver::BitmapView{valid.data()},
quiver::BitmapView{second.data()}, n, live.data());
// K1: elements > 25 among the live lanes (validity ANDs into the predicate).
std::vector<std::uint8_t> hits((static_cast<std::size_t>(n) + 7) / 8);
const std::int64_t matched =
quiver::compare_bitmap(quiver::CompareOp::kGt, quiver::BatchView<std::int32_t>{in.data(), n},
25, quiver::BitmapView{live.data()}, hits.data());
// K6: min/max over the live lanes only.
const std::int32_t lo = quiver::reduce_min(quiver::BatchView<std::int32_t>{in.data(), n},
quiver::BitmapView{live.data()});
const std::int32_t hi = quiver::reduce_max(quiver::BatchView<std::int32_t>{in.data(), n},
quiver::BitmapView{live.data()});
std::printf("live-lane range [%d, %d]; %lld live elements > 25\n", lo, hi,
static_cast<long long>(matched));
return 0;
}
Where next¶
- The full per-operation contracts: API reference.
- Consuming Quiver in your build (vcpkg/Conan skeletons,
FetchContent, drop-in): vendoring. - What the operations cost, with committed evidence: Performance.