<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thiago Silva</title>
    <description>The latest articles on DEV Community by Thiago Silva (@tgosoul).</description>
    <link>https://dev.to/tgosoul</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4126839%2F3d76ac25-62a3-4d74-a429-3ad021fb0ca6.jpg</url>
      <title>DEV Community: Thiago Silva</title>
      <link>https://dev.to/tgosoul</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tgosoul"/>
    <language>en</language>
    <item>
      <title>SetrixDB: a set engine in Go — exact set intersection over IDs (and where it loses)</title>
      <dc:creator>Thiago Silva</dc:creator>
      <pubDate>Tue, 15 Sep 2026 21:40:11 +0000</pubDate>
      <link>https://dev.to/tgosoul/setrixdb-a-set-engine-in-go-exact-set-intersection-over-ids-and-where-it-loses-39dm</link>
      <guid>https://dev.to/tgosoul/setrixdb-a-set-engine-in-go-exact-set-intersection-over-ids-and-where-it-loses-39dm</guid>
      <description>&lt;p&gt;“Given an ID, is it in this list?” and “which IDs are in both lists at the same time?” These look like&lt;br&gt;
textbook exercises. But when those lists hold &lt;strong&gt;millions or billions&lt;/strong&gt; of elements and must answer in&lt;br&gt;
&lt;strong&gt;microseconds&lt;/strong&gt; — in a faceted filter, a permission check, a pre-filter of candidates for an LLM — the&lt;br&gt;
answer stops being trivial.&lt;/p&gt;

&lt;p&gt;This article is about one specific primitive: an &lt;strong&gt;exact set engine over &lt;code&gt;uint64&lt;/code&gt; IDs&lt;/strong&gt;, with measured,&lt;br&gt;
reproducible numbers — and a dedicated section on &lt;strong&gt;where it loses&lt;/strong&gt; to an established library. It is&lt;br&gt;
not about replacing databases; it is about an operation that usually gets left open.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem: set algebra over IDs
&lt;/h2&gt;

&lt;p&gt;A lot of modern software spends its time crossing &lt;strong&gt;lists of identifiers&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;E-commerce / search:&lt;/strong&gt; "products &lt;strong&gt;this color&lt;/strong&gt; &lt;strong&gt;AND&lt;/strong&gt; &lt;strong&gt;this size&lt;/strong&gt; &lt;strong&gt;AND&lt;/strong&gt; &lt;strong&gt;this brand&lt;/strong&gt; &lt;strong&gt;AND&lt;/strong&gt;
&lt;strong&gt;in stock&lt;/strong&gt;" — an intersection of four sets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions / RAG:&lt;/strong&gt; "which documents can this user &lt;strong&gt;see&lt;/strong&gt; &lt;strong&gt;AND&lt;/strong&gt; match the query?" — intersect
a permission list with a candidate list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anti-fraud / access:&lt;/strong&gt; "is this ID on any blocklist?" — pure membership.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text:&lt;/strong&gt; every term or phrase becomes a key; combined queries are intersections.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all of these, what matters is &lt;strong&gt;exact presence&lt;/strong&gt; and &lt;strong&gt;exact intersection&lt;/strong&gt; over IDs — not&lt;br&gt;
payloads. Generic structures (&lt;code&gt;map&lt;/code&gt;, joins, sorted scans) solve it — just not &lt;em&gt;optimally&lt;/em&gt;: they carry&lt;br&gt;
pointers, indirections and comparisons you don't need when the data &lt;strong&gt;is&lt;/strong&gt; the number.&lt;/p&gt;

&lt;p&gt;The core choice: always work with &lt;strong&gt;&lt;code&gt;uint64&lt;/code&gt; IDs&lt;/strong&gt;. A set is a pile of &lt;code&gt;uint64&lt;/code&gt;; an intersection is an&lt;br&gt;
&lt;code&gt;AND&lt;/code&gt;. &lt;strong&gt;Everything is arithmetic.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The mistake that made the project: 78% collisions
&lt;/h2&gt;

&lt;p&gt;Before a set can exist, I need dense, collision-free identifiers. My first keygen was a &lt;em&gt;positional&lt;br&gt;
hash&lt;/em&gt; — a simple arithmetic formula. It collided badly: on 200k short alphanumeric tokens, &lt;strong&gt;78%&lt;br&gt;
collided&lt;/strong&gt;, and &lt;code&gt;"Oa"&lt;/code&gt; and &lt;code&gt;"0b"&lt;/code&gt; landed on the same ID.&lt;/p&gt;

&lt;p&gt;The fix was implementing a &lt;strong&gt;Minimal Perfect Hash Function&lt;/strong&gt; (CHD v2, from scratch):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0 collisions&lt;/strong&gt; on a base of &lt;strong&gt;50 million keys&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4.03 bits/key&lt;/strong&gt; (~24 MiB for 50M keys) — &lt;strong&gt;3.4× less memory&lt;/strong&gt; than v1 (13.68 bits/key);&lt;/li&gt;
&lt;li&gt;lookup in &lt;strong&gt;~118 ns&lt;/strong&gt;, and membership stays &lt;strong&gt;exact&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this article has one takeaway, it is this: &lt;strong&gt;measure the collision rate on the real corpus&lt;/strong&gt;&lt;br&gt;
is the step almost everyone skips — and it changes the whole architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  How it works (from term to result)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keygen (MPHF):&lt;/strong&gt; term → deterministic, collision-free &lt;code&gt;uint64&lt;/code&gt; ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Representation:&lt;/strong&gt; ID &lt;em&gt;i&lt;/em&gt; becomes bit &lt;em&gt;i&lt;/em&gt; of a &lt;strong&gt;bitset&lt;/strong&gt;; there is also a &lt;strong&gt;sparse set&lt;/strong&gt; (sorted
list) and a &lt;strong&gt;hybrid&lt;/strong&gt; (hot ranges in a bitset + cold tail sparse).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel:&lt;/strong&gt; the bitset &lt;code&gt;AND&lt;/code&gt; runs in &lt;strong&gt;AVX-512&lt;/strong&gt; (&lt;code&gt;vpandq&lt;/code&gt; + &lt;code&gt;vpopcntq&lt;/code&gt;) via cgo, with &lt;strong&gt;runtime
dispatch&lt;/strong&gt; (&lt;code&gt;__builtin_cpu_supports&lt;/code&gt;) and a scalar fallback — the same binary runs anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale:&lt;/strong&gt; the universe is split into &lt;strong&gt;shards&lt;/strong&gt; (parallelizes compute); in the &lt;strong&gt;cluster&lt;/strong&gt;, each
node serves a shard, the coordinator broadcasts and sums, and a &lt;strong&gt;consistent hash ring&lt;/strong&gt; decides
ownership (adding/removing a node remaps only ~&lt;code&gt;1/(N+1)&lt;/code&gt; of the IDs).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The real numbers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Environment (all measurements):&lt;/strong&gt; reference server — &lt;strong&gt;2 vCPU AMD EPYC (Zen4, AVX-512), 3.8 GB RAM,&lt;br&gt;
Go 1.22 (+ gcc for cgo)&lt;/strong&gt;. Date: 09/2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  Membership (n = 1M)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Structure&lt;/th&gt;
&lt;th&gt;memory&lt;/th&gt;
&lt;th&gt;speed&lt;/th&gt;
&lt;th&gt;exact?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;map[uint64]&lt;/code&gt; (Go)&lt;/td&gt;
&lt;td&gt;22.3 B/key&lt;/td&gt;
&lt;td&gt;133.3M ops/s&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SetrixDB (MPHF CHD v2)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0.5 B/key&lt;/strong&gt; (structure)&lt;/td&gt;
&lt;td&gt;~118 ns/lookup&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;yes&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bloom filter (1% false positive)&lt;/td&gt;
&lt;td&gt;1.2 B/key&lt;/td&gt;
&lt;td&gt;23.6M ops/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;no&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Speed parity with &lt;code&gt;map&lt;/code&gt;, at &lt;strong&gt;2.2× less memory&lt;/strong&gt; — and &lt;strong&gt;exact&lt;/strong&gt;, unlike a probabilistic filter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Intersection (A = B = 1M)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;dense IDs (&lt;code&gt;denso32&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;random 64-bit IDs (&lt;code&gt;aleat64&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sorted merge (SetrixDB)&lt;/td&gt;
&lt;td&gt;9.2 ms&lt;/td&gt;
&lt;td&gt;11.3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roaring (compressed bitmap)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;148 µs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;523 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hash join (&lt;code&gt;map&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;91.6 ms&lt;/td&gt;
&lt;td&gt;94.9 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitset &lt;code&gt;AND&lt;/code&gt; (pure Go)&lt;/td&gt;
&lt;td&gt;29 µs&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bitset &lt;code&gt;AND&lt;/code&gt; (AVX-512)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6 µs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Where it &lt;strong&gt;loses&lt;/strong&gt; (and why that matters)
&lt;/h2&gt;

&lt;p&gt;Let me be explicit, because a comparison without context is misleading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sparse, huge universe:&lt;/strong&gt; when the universe &lt;strong&gt;does not fit&lt;/strong&gt; in RAM, the dense bitset is out (it
always takes &lt;code&gt;universe/8&lt;/code&gt;). That's where &lt;strong&gt;Roaring&lt;/strong&gt; wins — that's what it's for. Measured: universe
2²⁶, Roaring64 used &lt;strong&gt;~2 MB&lt;/strong&gt; against &lt;strong&gt;8.2 MB&lt;/strong&gt; for my bitset (slower in time, cheaper in memory).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Random 64-bit IDs:&lt;/strong&gt; in the &lt;code&gt;aleat64&lt;/code&gt; case, Roaring took 523 ms — but that's because it was
designed for a different regime. The point is not "I always win"; it's &lt;strong&gt;which regime each one
shines in&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Range queries, similarity, joins:&lt;/strong&gt; SetrixDB &lt;strong&gt;doesn't do them&lt;/strong&gt;. It's pure equality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frequent updates:&lt;/strong&gt; an MPHF is built for a set; adding/removing new keys requires a rebuild. For
mutable workloads, it is &lt;strong&gt;not&lt;/strong&gt; the tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;So where does it win?&lt;/strong&gt; In the opposite regime: &lt;strong&gt;a dense universe that fits in RAM&lt;/strong&gt;, large sets,&lt;br&gt;
exact intersection on the hot path. That's exactly what a real-data test showed ↓&lt;/p&gt;




&lt;h2&gt;
  
  
  Real data (not just synthetic)
&lt;/h2&gt;

&lt;p&gt;I ran three public datasets and checked &lt;strong&gt;every result externally&lt;/strong&gt; (&lt;code&gt;sort&lt;/code&gt; + &lt;code&gt;comm&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Retail — Online Retail II (UCI)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1,067,371&lt;/strong&gt; real sale lines (UK, 2009–2011). Query "United Kingdom &lt;strong&gt;AND&lt;/strong&gt; Q4/2011 &lt;strong&gt;AND&lt;/strong&gt; price ≥&lt;br&gt;
5" → &lt;strong&gt;22,701 rows&lt;/strong&gt; in &lt;strong&gt;823 µs&lt;/strong&gt;. Independent check: 22,701. Identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Text — Wikipedia titles (19.3 million terms)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;enwiki-latest-all-titles-in-ns0&lt;/code&gt;: &lt;strong&gt;19,264,252&lt;/strong&gt; titles. "multi-word &lt;strong&gt;AND&lt;/strong&gt; starts with &lt;code&gt;s&lt;/code&gt;" →&lt;br&gt;
&lt;strong&gt;1,408,399&lt;/strong&gt; in &lt;strong&gt;9.5 ms&lt;/strong&gt;; "multi-word &lt;strong&gt;AND&lt;/strong&gt; &lt;code&gt;United&lt;/code&gt;" → &lt;strong&gt;38,602&lt;/strong&gt; in &lt;strong&gt;8.0 ms&lt;/strong&gt;. Verified: identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scale — MovieLens 25M (25 million interactions)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;25,000,095&lt;/strong&gt; real ratings; derived facets (genre, decade, score). Sets with &lt;strong&gt;10.9M&lt;/strong&gt; and &lt;strong&gt;12.4M&lt;/strong&gt;&lt;br&gt;
members. Three queries, all externally verified:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Query&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Drama &lt;strong&gt;AND&lt;/strong&gt; 2000s &lt;strong&gt;AND&lt;/strong&gt; rating ≥ 4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,634,027&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drama &lt;strong&gt;AND&lt;/strong&gt; rating ≥ 4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6,096,563&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comedy &lt;strong&gt;AND&lt;/strong&gt; rating ≥ 4 &lt;strong&gt;AND&lt;/strong&gt; 2000s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;965,677&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And here is &lt;strong&gt;the number I like most&lt;/strong&gt; — because it is about picking the right representation. On the&lt;br&gt;
same 25M-ID universe, with sets of tens of millions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;memory/set&lt;/th&gt;
&lt;th&gt;latency (A∩B)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sorted list merge&lt;/td&gt;
&lt;td&gt;87.7 MB&lt;/td&gt;
&lt;td&gt;80.4 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dense bitset (AVX-512)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;227 µs&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same exact result, &lt;strong&gt;~350× faster and ~43× smaller&lt;/strong&gt;. When the universe is dense and fits in memory,&lt;br&gt;
the bitset isn't just the fastest — it's the most &lt;strong&gt;economical&lt;/strong&gt; too.&lt;/p&gt;




&lt;h2&gt;
  
  
  What SetrixDB IS — and what it is NOT
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It IS&lt;/strong&gt; an &lt;strong&gt;embeddable set engine&lt;/strong&gt;, in Go, that answers &lt;strong&gt;exact presence&lt;/strong&gt; and &lt;strong&gt;exact intersection&lt;/strong&gt;&lt;br&gt;
over &lt;code&gt;uint64&lt;/code&gt; IDs, with a SIMD kernel, sharding and a cluster mode. It &lt;strong&gt;coexists&lt;/strong&gt; with your current&lt;br&gt;
database: your data stays where it is; SetrixDB sits beside it as an &lt;strong&gt;index/pre-filter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is NOT&lt;/strong&gt; a relational, columnar, NoSQL or vector database. It doesn't do SQL, joins or&lt;br&gt;
similarity. And — importantly — &lt;strong&gt;it stores sets of IDs, not payloads&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alpha (&lt;code&gt;v0.1.0&lt;/code&gt;).&lt;/strong&gt; Tested on loopback and between two machines; the 3-node cluster ran in the
cloud, but not yet in multi-datacenter production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitset memory is linear in the universe&lt;/strong&gt; (&lt;code&gt;universe/8&lt;/code&gt;); the hybrid mitigates it (2³⁶ IDs:
8.59 GB dense → &lt;strong&gt;1.73 MB&lt;/strong&gt; hybrid), but it's a trade-off with a cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NPU backend and compact UDP protocol:&lt;/strong&gt; roadmap, not implemented.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Energy benchmarks (J/search):&lt;/strong&gt; planned, not yet measured.&lt;/li&gt;
&lt;li&gt;If any number here doesn't reproduce on your machine, that's a bug — and I want to know.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Hard questions (and answers)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"Why not just use CRoaring/Roaring?"&lt;/strong&gt;&lt;br&gt;
Because Roaring is excellent — and it is the right answer when the universe doesn't fit in RAM or is&lt;br&gt;
very sparse. SetrixDB targets another point: &lt;strong&gt;native Go, embeddable, dense universe that fits in&lt;br&gt;
RAM&lt;/strong&gt;, with MPHF in the keygen and sharding/cluster built in. If your case is Roaring's case, use&lt;br&gt;
Roaring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Why not a &lt;code&gt;map&lt;/code&gt;/Bloom filter?"&lt;/strong&gt;&lt;br&gt;
A &lt;code&gt;map&lt;/code&gt; stores pointers and is ~44× fatter per key (22.3 vs 0.5 B/key here). Bloom is smaller but it&lt;br&gt;
&lt;strong&gt;errs&lt;/strong&gt; (1% false positive) — in permissions, erring toward "can see" is unacceptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Does MPHF handle insert/delete?"&lt;/strong&gt;&lt;br&gt;
No. It's built for a set. Mutable loads require a rebuild (or the sparse mode). It's a conscious&lt;br&gt;
trade for &lt;code&gt;O(1)&lt;/code&gt; lookup at ~4 bits/key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What about Go's GC on the hot path?"&lt;/strong&gt;&lt;br&gt;
The bitsets are contiguous &lt;code&gt;[]uint64&lt;/code&gt;, allocated once; the hot loop doesn't allocate. For DMA (NPU)&lt;br&gt;
there's &lt;code&gt;UnsafePtr&lt;/code&gt; + pinning — with the caveat of keeping the buffer alive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Isn't this just 'bitset with AVX-512'?"&lt;/strong&gt;&lt;br&gt;
Partly, yes — and that's fine: bitset + SIMD is a solid, well-known base. What the project adds is the&lt;br&gt;
&lt;strong&gt;package&lt;/strong&gt;: a collision-free keygen, adaptive representation (dense/sparse/hybrid), sharding/cluster,&lt;br&gt;
and the "stored sets" mode (only the &lt;em&gt;name&lt;/em&gt; travels over the network).&lt;/p&gt;




&lt;h2&gt;
  
  
  The invitation
&lt;/h2&gt;

&lt;p&gt;SetrixDB is &lt;strong&gt;open source (Apache-2.0)&lt;/strong&gt;. If the next wave isn't about &lt;em&gt;storing more&lt;/em&gt;, but about&lt;br&gt;
&lt;strong&gt;deciding faster&lt;/strong&gt; — and if set operations deserve a dedicated, exact, vectorized engine beside what&lt;br&gt;
you already use — come test it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code, reproducible benchmarks and a quickstart:&lt;/strong&gt; &lt;strong&gt;&lt;a href="https://github.com/setrixdb/setrixdb" rel="noopener noreferrer"&gt;https://github.com/setrixdb/setrixdb&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the benchmarks, open an issue, and tell me where the numbers don't add up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SetrixDB — the arithmetic set engine.&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Sets. In microseconds. On any chip. Beside your database.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;License: Apache-2.0 · Copyright 2026 SetrixDB.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>go</category>
      <category>database</category>
      <category>performance</category>
    </item>
    <item>
      <title>SetrixDB: motor de conjuntos em Go — interseção exata sobre IDs (e onde ele perde)</title>
      <dc:creator>Thiago Silva</dc:creator>
      <pubDate>Tue, 15 Sep 2026 21:40:00 +0000</pubDate>
      <link>https://dev.to/tgosoul/setrixdb-motor-de-conjuntos-em-go-intersecao-exata-sobre-ids-e-onde-ele-perde-dic</link>
      <guid>https://dev.to/tgosoul/setrixdb-motor-de-conjuntos-em-go-intersecao-exata-sobre-ids-e-onde-ele-perde-dic</guid>
      <description>&lt;p&gt;“Dado um ID, ele está nesta lista?” e “quais IDs aparecem nas duas listas ao mesmo tempo?” Parecem&lt;br&gt;
exercícios de livro-texto. Mas quando essas listas têm &lt;strong&gt;milhões ou bilhões&lt;/strong&gt; de elementos e precisam&lt;br&gt;
responder em &lt;strong&gt;microssegundos&lt;/strong&gt; — num filtro facetado, numa checagem de permissão, num pré-filtro de&lt;br&gt;
candidatos para um LLM — a resposta deixa de ser trivial.&lt;/p&gt;

&lt;p&gt;Este artigo é sobre uma primitiva específica: um &lt;strong&gt;motor de conjuntos exato sobre IDs &lt;code&gt;uint64&lt;/code&gt;&lt;/strong&gt;, com&lt;br&gt;
números medidos e reproduzíveis — e uma seção dedicada a &lt;strong&gt;onde ele perde&lt;/strong&gt; para uma biblioteca&lt;br&gt;
consagrada. Não é sobre substituir bancos de dados; é sobre uma operação que costuma ficar em aberto.&lt;/p&gt;




&lt;h2&gt;
  
  
  O problema: álgebra de conjuntos sobre IDs
&lt;/h2&gt;

&lt;p&gt;Boa parte do software moderno passa o tempo cruzando &lt;strong&gt;listas de identificadores&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;E-commerce / busca:&lt;/strong&gt; “produtos &lt;strong&gt;desta cor&lt;/strong&gt; &lt;strong&gt;E&lt;/strong&gt; &lt;strong&gt;deste tamanho&lt;/strong&gt; &lt;strong&gt;E&lt;/strong&gt; &lt;strong&gt;desta marca&lt;/strong&gt; &lt;strong&gt;E&lt;/strong&gt;
&lt;strong&gt;em estoque&lt;/strong&gt;” — interseção de quatro conjuntos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissões / RAG:&lt;/strong&gt; “quais documentos este usuário &lt;strong&gt;pode ver&lt;/strong&gt; &lt;strong&gt;E&lt;/strong&gt; casam com a busca?” —
interseção de uma lista de permissão com uma lista de candidatos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Antifraude / acesso:&lt;/strong&gt; “este ID está em alguma lista de bloqueio?” — pertencimento puro.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Texto:&lt;/strong&gt; cada termo ou frase vira uma chave; consultas combinadas são interseções.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Em todos esses casos o que importa é &lt;strong&gt;presença&lt;/strong&gt; e &lt;strong&gt;interseção exatas&lt;/strong&gt; sobre IDs — não payloads.&lt;br&gt;
Estruturas genéricas (&lt;code&gt;map&lt;/code&gt;, joins, varredura ordenada) resolvem isso — só não de forma &lt;em&gt;otimizada&lt;/em&gt;:&lt;br&gt;
carregam ponteiros, indireções e comparações desnecessárias quando o dado &lt;strong&gt;é&lt;/strong&gt; o número.&lt;/p&gt;

&lt;p&gt;A escolha central: trabalhar sempre com &lt;strong&gt;IDs &lt;code&gt;uint64&lt;/code&gt;&lt;/strong&gt;. Um conjunto é um monte de &lt;code&gt;uint64&lt;/code&gt;; a&lt;br&gt;
interseção é um &lt;code&gt;AND&lt;/code&gt;. &lt;strong&gt;Tudo é aritmética.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  O erro que valeu o projeto: 78% de colisão
&lt;/h2&gt;

&lt;p&gt;Antes de existir conjunto, preciso de identificadores densos e sem colisão. O primeiro keygen foi um&lt;br&gt;
&lt;em&gt;hash posicional&lt;/em&gt; — uma fórmula aritmética simples. Ele colidia feio: numa base de 200 mil tokens&lt;br&gt;
alfanuméricos curtos, &lt;strong&gt;78% colidiam&lt;/strong&gt;, e &lt;code&gt;"Oa"&lt;/code&gt; e &lt;code&gt;"0b"&lt;/code&gt; caíam no mesmo ID.&lt;/p&gt;

&lt;p&gt;A correção foi implementar um &lt;strong&gt;Minimal Perfect Hash Function&lt;/strong&gt; (CHD v2, do zero):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0 colisões&lt;/strong&gt; numa base de &lt;strong&gt;50 milhões de chaves&lt;/strong&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4,03 bits/chave&lt;/strong&gt; (≈ 24 MiB para 50M de chaves) — &lt;strong&gt;3,4× menos memória&lt;/strong&gt; que a v1 (13,68 bits/chave);&lt;/li&gt;
&lt;li&gt;lookup em &lt;strong&gt;~118 ns&lt;/strong&gt;, e o pertencimento continua &lt;strong&gt;exato&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se este artigo tiver uma única lição, é esta: &lt;strong&gt;medir a taxa de colisão no corpus real&lt;/strong&gt; é o passo&lt;br&gt;
que quase todo mundo pula — e é o que muda a arquitetura inteira.&lt;/p&gt;




&lt;h2&gt;
  
  
  Como funciona (do termo ao resultado)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keygen (MPHF):&lt;/strong&gt; termo → ID &lt;code&gt;uint64&lt;/code&gt; determinístico e sem colisão.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Representação:&lt;/strong&gt; o ID &lt;em&gt;i&lt;/em&gt; vira o bit &lt;em&gt;i&lt;/em&gt; de um &lt;strong&gt;bitset&lt;/strong&gt;; há também &lt;strong&gt;conjunto esparso&lt;/strong&gt;
(lista ordenada) e um &lt;strong&gt;híbrido&lt;/strong&gt; (faixas quentes em bitset + cauda fria esparsa).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kernel:&lt;/strong&gt; o &lt;code&gt;AND&lt;/code&gt; dos bitsets roda em &lt;strong&gt;AVX-512&lt;/strong&gt; (&lt;code&gt;vpandq&lt;/code&gt; + &lt;code&gt;vpopcntq&lt;/code&gt;) via cgo, com
&lt;strong&gt;dispatch em runtime&lt;/strong&gt; (&lt;code&gt;__builtin_cpu_supports&lt;/code&gt;) e fallback escalar — o mesmo binário funciona em
qualquer máquina.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escala:&lt;/strong&gt; o universo é fatiado em &lt;strong&gt;shards&lt;/strong&gt; (paraleliza o compute); no &lt;strong&gt;cluster&lt;/strong&gt;, cada nó
serve um shard, o coordenador faz broadcast e soma, e um &lt;strong&gt;consistent hash ring&lt;/strong&gt; decide a posse
(entrar/sair um nó remapeia só ~&lt;code&gt;1/(N+1)&lt;/code&gt; dos IDs).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Os números reais
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ambiente (todas as medições):&lt;/strong&gt; servidor de referência — &lt;strong&gt;2 vCPU AMD EPYC (Zen4, AVX-512), 3,8 GB&lt;br&gt;
RAM, Go 1.22 (+ gcc para o cgo)&lt;/strong&gt;. Data: 09/2026.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pertencimento (n = 1M)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Estrutura&lt;/th&gt;
&lt;th&gt;memória&lt;/th&gt;
&lt;th&gt;velocidade&lt;/th&gt;
&lt;th&gt;exato?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;map[uint64]&lt;/code&gt; (Go)&lt;/td&gt;
&lt;td&gt;22,3 B/chave&lt;/td&gt;
&lt;td&gt;133,3M ops/s&lt;/td&gt;
&lt;td&gt;sim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SetrixDB (MPHF CHD v2)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0,5 B/chave&lt;/strong&gt; (estrutura)&lt;/td&gt;
&lt;td&gt;~118 ns/lookup&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;sim&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bloom filter (1% falso-positivo)&lt;/td&gt;
&lt;td&gt;1,2 B/chave&lt;/td&gt;
&lt;td&gt;23,6M ops/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;não&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Paridade de velocidade com o &lt;code&gt;map&lt;/code&gt;, com &lt;strong&gt;2,2× menos memória&lt;/strong&gt; — e resultado &lt;strong&gt;exato&lt;/strong&gt;, ao contrário&lt;br&gt;
de um filtro probabilístico.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interseção (A = B = 1M)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Estratégia&lt;/th&gt;
&lt;th&gt;IDs densos (&lt;code&gt;denso32&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;IDs aleatórios 64-bit (&lt;code&gt;aleat64&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Merge ordenado (SetrixDB)&lt;/td&gt;
&lt;td&gt;9,2 ms&lt;/td&gt;
&lt;td&gt;11,3 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Roaring (bitmap comprimido)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;148 µs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;523 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hash join (&lt;code&gt;map&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;91,6 ms&lt;/td&gt;
&lt;td&gt;94,9 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitset &lt;code&gt;AND&lt;/code&gt; (Go puro)&lt;/td&gt;
&lt;td&gt;29 µs&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bitset &lt;code&gt;AND&lt;/code&gt; (AVX-512)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6 µs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Onde ele &lt;strong&gt;perde&lt;/strong&gt; (e por que isso importa)
&lt;/h2&gt;

&lt;p&gt;Vou ser explícito, porque comparação sem contexto engana:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Universo esparso e enorme:&lt;/strong&gt; quando o universo &lt;strong&gt;não cabe&lt;/strong&gt; na RAM, o bitset denso deixa de ser
opção (ele ocupa &lt;code&gt;universo/8&lt;/code&gt;, sempre). Aí o &lt;strong&gt;Roaring&lt;/strong&gt; ganha — é para isso que ele existe.
Medido: universo 2²⁶, o Roaring64 usou &lt;strong&gt;~2 MB&lt;/strong&gt; contra &lt;strong&gt;8,2 MB&lt;/strong&gt; do meu bitset (mais lento em
tempo, mais econômico em memória).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDs aleatórios de 64 bits:&lt;/strong&gt; no caso &lt;code&gt;aleat64&lt;/code&gt;, o Roaring levou 523 ms — mas porque ele foi
projetado para outro regime. O ponto não é “eu ganho sempre”; é &lt;strong&gt;em qual regime cada um brilha&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Range queries, similaridade, joins:&lt;/strong&gt; o SetrixDB &lt;strong&gt;não faz&lt;/strong&gt;. É igualdade pura.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atualizações frequentes:&lt;/strong&gt; um MPHF é construído para um conjunto; inserir/remover chaves novas
exige reconstrução. Para cargas mutáveis, ele &lt;strong&gt;não&lt;/strong&gt; é a ferramenta.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Então onde ele ganha?&lt;/strong&gt; No regime oposto: &lt;strong&gt;universo denso que cabe em RAM&lt;/strong&gt;, conjuntos grandes,&lt;br&gt;
interseção exata no caminho quente. Foi exatamente o que um teste com dados reais mostrou ↓&lt;/p&gt;




&lt;h2&gt;
  
  
  Dados reais (nada de só sintético)
&lt;/h2&gt;

&lt;p&gt;Rodei três datasets públicos e conferi &lt;strong&gt;cada resultado por fora&lt;/strong&gt; do SetrixDB (&lt;code&gt;sort&lt;/code&gt; + &lt;code&gt;comm&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Varejo — Online Retail II (UCI)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1.067.371 linhas&lt;/strong&gt; reais de venda (UK, 2009–2011). Consulta “Reino Unido &lt;strong&gt;E&lt;/strong&gt; 4º tri/2011 &lt;strong&gt;E&lt;/strong&gt;&lt;br&gt;
preço ≥ 5” → &lt;strong&gt;22.701 linhas&lt;/strong&gt; em &lt;strong&gt;823 µs&lt;/strong&gt;. Verificação independente: 22.701. Idêntico.&lt;/p&gt;

&lt;h3&gt;
  
  
  Texto — títulos da Wikipédia (19,3 milhões de termos)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;enwiki-latest-all-titles-in-ns0&lt;/code&gt;: &lt;strong&gt;19.264.252&lt;/strong&gt; títulos. “multi-palavra &lt;strong&gt;E&lt;/strong&gt; começa com &lt;code&gt;s&lt;/code&gt;” →&lt;br&gt;
&lt;strong&gt;1.408.399&lt;/strong&gt; em &lt;strong&gt;9,5 ms&lt;/strong&gt;; “multi-palavra &lt;strong&gt;E&lt;/strong&gt; &lt;code&gt;United&lt;/code&gt;” → &lt;strong&gt;38.602&lt;/strong&gt; em &lt;strong&gt;8,0 ms&lt;/strong&gt;. Verificado: idêntico.&lt;/p&gt;

&lt;h3&gt;
  
  
  Escala — MovieLens 25M (25 milhões de interações)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;25.000.095 avaliações&lt;/strong&gt; reais; facetas derivadas (gênero, década, nota). Conjuntos com &lt;strong&gt;10,9M&lt;/strong&gt; e&lt;br&gt;
&lt;strong&gt;12,4M&lt;/strong&gt; de membros. Três consultas, todas verificadas por fora:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Consulta&lt;/th&gt;
&lt;th&gt;Resultado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Drama &lt;strong&gt;E&lt;/strong&gt; anos 2000 &lt;strong&gt;E&lt;/strong&gt; nota ≥ 4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1.634.027&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drama &lt;strong&gt;E&lt;/strong&gt; nota ≥ 4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.096.563&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comédia &lt;strong&gt;E&lt;/strong&gt; nota ≥ 4 &lt;strong&gt;E&lt;/strong&gt; anos 2000&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;965.677&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;O resultado mais revelador deste teste é sobre &lt;strong&gt;escolher a representação certa&lt;/strong&gt;. No mesmo&lt;br&gt;
universo de 25M de IDs, com conjuntos de dezenas de milhões:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Caminho&lt;/th&gt;
&lt;th&gt;memória/conjunto&lt;/th&gt;
&lt;th&gt;latência (A∩B)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Merge de listas ordenadas&lt;/td&gt;
&lt;td&gt;87,7 MB&lt;/td&gt;
&lt;td&gt;80,4 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bitset denso (AVX-512)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;227 µs&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mesmo resultado exato, &lt;strong&gt;~350× mais rápido e ~43× menor&lt;/strong&gt;. Quando o universo é denso e cabe na&lt;br&gt;
memória, o bitset não é só o mais rápido: é o mais &lt;strong&gt;econômico&lt;/strong&gt; também.&lt;/p&gt;




&lt;h2&gt;
  
  
  O que o SetrixDB É — e o que NÃO É
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;É&lt;/strong&gt; um &lt;strong&gt;motor de conjuntos embarcável&lt;/strong&gt;, em Go, que responde &lt;strong&gt;presença&lt;/strong&gt; e &lt;strong&gt;interseção exatas&lt;/strong&gt;&lt;br&gt;
sobre IDs &lt;code&gt;uint64&lt;/code&gt;, com kernel SIMD, sharding e modo de cluster. Ele &lt;strong&gt;coexiste&lt;/strong&gt; com o seu banco&lt;br&gt;
atual: o dado continua onde está; o SetrixDB entra ao lado como &lt;strong&gt;índice/pré-filtro&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Não é&lt;/strong&gt; banco relacional, colunar, NoSQL ou vetorial. Não faz SQL, joins nem similaridade. E —&lt;br&gt;
importante — &lt;strong&gt;guarda conjuntos de IDs, não payloads&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitações honestas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alpha (&lt;code&gt;v0.1.0&lt;/code&gt;).&lt;/strong&gt; Testado em loopback e entre duas máquinas; o cluster de 3 nós rodou em nuvem,
mas ainda não em produção multi-datacenter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memória do bitset é linear no universo&lt;/strong&gt; (&lt;code&gt;universo/8&lt;/code&gt;); o híbrido mitiga (2³⁶ IDs: 8,59 GB
densos → &lt;strong&gt;1,73 MB&lt;/strong&gt; híbridos), mas é uma escolha com custo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend de NPU e protocolo UDP compacto:&lt;/strong&gt; roadmap, não implementados.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmarks de energia (J/busca):&lt;/strong&gt; planejados, ainda não medidos.&lt;/li&gt;
&lt;li&gt;Se algum número não se reproduzir na sua máquina, isso é um bug — e eu quero saber.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Perguntas difíceis (e respostas)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;“Por que não usar CRoaring/Roaring direto?”&lt;/strong&gt;&lt;br&gt;
Porque o Roaring é excelente — e é a resposta certa quando o universo não cabe em RAM ou é muito&lt;br&gt;
esparso. O SetrixDB mira outro ponto: &lt;strong&gt;Go nativo, embarcável, universo denso que cabe em RAM&lt;/strong&gt;, com&lt;br&gt;
MPHF no keygen e sharding/cluster integrados. Se o seu caso é o do Roaring, use o Roaring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Por que não um &lt;code&gt;map&lt;/code&gt;/&lt;code&gt;Bloom filter&lt;/code&gt;?”&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;map&lt;/code&gt; guarda ponteiros e é ~44× mais gordo por chave (22,3 vs 0,5 B/chave aqui). Bloom é menorzinho,&lt;br&gt;
mas &lt;strong&gt;erra&lt;/strong&gt; (1% de falso-positivo) — em permissão, errar para o lado “pode ver” é inaceitável.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“MPHF aguenta inserção/remoção?”&lt;/strong&gt;&lt;br&gt;
Não. Ele é construído para um conjunto. Cargas mutáveis exigem reconstrução (ou o modo esparso). É&lt;br&gt;
uma troca consciente por lookup &lt;code&gt;O(1)&lt;/code&gt; e ~4 bits/chave.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“E o GC do Go no caminho quente?”&lt;/strong&gt;&lt;br&gt;
Os bitsets são &lt;code&gt;[]uint64&lt;/code&gt; contíguos, alocados uma vez; o laço quente não aloca. Para DMA (NPU) há&lt;br&gt;
&lt;code&gt;UnsafePtr&lt;/code&gt; + pinning — com o aviso de manter o buffer vivo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Isso não é só ‘bitset com AVX-512’?”&lt;/strong&gt;&lt;br&gt;
Em parte, sim — e é bom que seja: bitset + SIMD é uma base sólida e conhecida. O que o projeto&lt;br&gt;
adiciona é o &lt;strong&gt;pacote&lt;/strong&gt;: keygen sem colisão, representação adaptativa (denso/esparso/híbrido),&lt;br&gt;
sharding/cluster e o modo “conjuntos armazenados” (só o &lt;em&gt;nome&lt;/em&gt; trafega na rede).&lt;/p&gt;




&lt;h2&gt;
  
  
  O convite
&lt;/h2&gt;

&lt;p&gt;O SetrixDB é &lt;strong&gt;open source (Apache-2.0)&lt;/strong&gt;. Se a próxima onda não é sobre &lt;em&gt;guardar mais&lt;/em&gt;, mas sobre&lt;br&gt;
&lt;strong&gt;decidir mais rápido&lt;/strong&gt; — e se operações de conjunto merecem um motor dedicado, exato e vetorizado,&lt;br&gt;
ao lado do que você já usa — venha testar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Código, benchmarks reproduzíveis e quickstart:&lt;/strong&gt; &lt;strong&gt;&lt;a href="https://github.com/setrixdb/setrixdb" rel="noopener noreferrer"&gt;https://github.com/setrixdb/setrixdb&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rode os benchmarks, abra uma issue e me diga onde os números não fecham.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SetrixDB — the arithmetic set engine.&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Conjuntos. Em microssegundos. Em qualquer chip. Ao lado do seu banco.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Licença: Apache-2.0 · Copyright 2026 SetrixDB.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>go</category>
      <category>database</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
