TGViewer
C++ - Reddit C++ - Reddit @r_cpp · 230 subscribers
Post #25017 16
Avoiding per-cell std::string allocation in a vectorized filter

Writing a small columnar query engine and hit a string-copy trap in the filter operator. The fix turned out to be
measurable so I thought I'd share.

Naive version: output chunk built cell-by-cell.

  for (idx_t i = 0; i < input.size(); i++) {
if (!matches[i]) continue;
for (idx_t c = 0; c < num_cols; c++)
result.SetValue(c, out, input.GetValue(c, i));
out++;
}


GetValue/SetValue go through a tagged Value type, and for VARCHAR they each allocate a fresh std::string. 1M
rows with a few VARCHAR columns means millions of allocations on a single filter pass.

Vectorized version: build a uint32_t sel[] of matching row indices, then per column copy with the typed pointer.

  auto *s = src.GetData<int64_t>();
auto *d = dst.GetData<int64_t>();
for (idx_t i = 0; i < n; i++) d[i] = s[sel[i]];


Trivial for numeric types. For VARCHAR it's trickier: string_t is a 16-byte type, inline for short strings, a
pointer to a heap-allocated payload for longer ones. Copying the 16 bytes is cheap. The problem is that the
long-string pointer aims at the source vector's string heap. Let src go out of scope and dst's strings point at freed
memory.

The string heap (VectorStringBuffer) is already owned via shared_ptr<VectorBuffer>. Fix is a setter that makes dst
adopt src's heap:

  auto *s = src.GetData<string_t>();
auto *d = dst.GetData<string_t>();
for (idx_t i = 0; i < n; i++) d[i] = s[sel[i]];
dst.SetAuxiliaryPtr(src.GetAuxiliaryPtr()); // dst keeps src's heap alive


No string copies. Refcount bumps once per vector, not once per cell.

A WHERE ... GROUP BY region query on 1M rows went from 894 ms to ~150 ms. Roughly 100 ms of that was this change
alone; the rest was unrelated parallelism on another pass.

Question for the sub: is there a standard name for this pattern? "Copy handles that reference an upstream buffer,
retain the buffer as long as any handle lives." Arrow solves the same problem internally. shared_ptr<Buffer>
adoption feels manual. Curious what the idiomatic C++ answer is.

Repo if anyone wants the full context: https://github.com/SouravRoy-ETL/slothdb

https://redd.it/1sqj7ze
@r_cpp
GitHub GitHub - SouravRoy-ETL/slothdb: An experimental embedded SQL engine in C++20. Query Parquet, CSV, JSON, Arrow, Avro, SQLite, and… An experimental embedded SQL engine in C++20. Query Parquet, CSV, JSON, Arrow, Avro, SQLite, and Excel files directly with SQL, in-process. Early-stage. - SouravRoy-ETL/slothdb
More from @r_cpp
  1. Oct 6, 2026operator<>: A Dream of Higher-order Metaprogramming Parity https://www.elbeno.com/blog/?p=…
  2. Oct 6, 2026University of Waterloo user study on understanding CMake changes Hi everyone, I’m Mattie N…
  3. Oct 6, 2026Try to Implement Reflection Annotations in C++20 https://typecombinator.github.io/en/posts…
  4. Oct 5, 2026New C++ Conference Videos Released This Month - September 2026 (Updated To Include Videos…
  5. Oct 5, 2026What’s New for C++ Developers in Visual Studio 2026 (18.7 – 18.10) https://devblogs.micros…
  6. Oct 5, 2026Notes on dllexport and dllimport https://www.hanshq.net/dllexport.html https://redd.it/1wy…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →