Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nimdeveloper/better-duck/llms.txt

Use this file to discover all available pages before exploring further.

The better-duck workspace ships a benchmark harness that measures better-duck-core head-to-head against the community duckdb crate. Because both crates vendor their own copy of DuckDB (and each sets links = "duckdb", preventing them from being linked into a single binary), each side runs as its own process — but both are in-process native calls with no per-operation subprocess overhead, so the timings are directly comparable. The harness covers three benchmark groups: primitive types, composite types, and five representative operations.
The benchmark harness lives in crates/better-duck-core/benches/. The full tables, raw JSON numbers, and SVG charts are generated in docs/benchmarks/ when you run the comparison command.

Running Benchmarks

cargo bench -p better-duck-core --bench comparison
Output is written to docs/benchmarks/:
  • REPORT.md — full tables and embedded SVG charts
  • results.json — raw latency and throughput numbers
  • comparison-primitive-types-latency.svg / comparison-primitive-types-throughput.svg
  • comparison-composite-types-latency.svg / comparison-composite-types-throughput.svg
  • comparison-operations-latency.svg / comparison-operations-throughput.svg

Operations Group Results

The operations group covers five representative end-to-end workloads. Numbers below are sample results from a single run; see REPORT.md for the full primitive-type and composite-type tables.
Workloadbetter-duck-coreduckdb crate
CRUD basics (4 ops)4.73 ms / 846 ops/s4.59 ms / 871 ops/s
Bulk ingest — 10k rows (appender)31.94 ms / 313.1 k rows/s38.55 ms / 259.4 k rows/s
Analytical GROUP BY — 100k rows4.19 ms / 23.9 M rows/s4.24 ms / 23.6 M rows/s
Prepared reuse — 100 queries75.72 ms / 1.3 k queries/s68.16 ms / 1.5 k queries/s
All-types scan — 1k rows, 11 cols32.91 ms / 30.4 k rows/s27.46 ms / 36.4 k rows/s
Each workload in the operations group tests a different query pattern:
  • CRUD basics — a single-row INSERT, a point SELECT, an UPDATE, and a DELETE in sequence.
  • Bulk ingest — ingesting 10,000 rows through each library’s native appender API.
  • Analytical GROUP BY — a GROUP BY + AVG + ORDER BY over a pre-populated 100,000-row table (query execution time only; table setup is excluded).
  • Prepared reuse — 100 point-SELECT queries reusing one prepared statement (a CachedStatement on the better-duck side).
  • All-types scan — inserting and reading back 1,000 rows across 11 mixed column types in a single table.

Benchmark Groups

The harness runs three benchmark groups, each producing a latency SVG and a throughput SVG:

Primitive types

Insert + full-scan of 20,000 rows for each scalar DuckDB type: BOOLEAN, TINYINTHUGEINT, UTINYINTUHUGEINT, FLOAT, DOUBLE, VARCHAR, DECIMAL(18,4), DATE, TIME, TIMESTAMP, INTERVAL, TIMESTAMPTZ, BLOB, UUID.

Composite types

Insert + full-scan of 2,000 rows for LIST, ARRAY, STRUCT, and MAP columns. The duckdb crate has no safe write API for these types; those rows use a neutral placeholder so that the chart shape is not distorted.

Operations

Five representative end-to-end workloads as described in the table above. This is the group most relevant for assessing real-world query-path performance.

Interpreting Results

Numbers vary between runs due to normal system noise — OS scheduling, CPU thermal state, and background activity all affect wall-clock latency. The relative comparison within a single run is what is meaningful; do not compare absolute millisecond figures across runs on different machines or different days. A few observations from the sample results above:
  • Bulk ingest: better-duck-core is ~17% faster on the appender path (31.9 ms vs 38.6 ms). This reflects the direct duckdb_append_* typed paths throughout.
  • Analytical queries: both libraries are essentially equivalent (~4.2 ms), which is expected — DuckDB’s query engine does the work and both libraries call the same underlying execution path.
  • Prepared reuse: better-duck-core is ~10% slower on prepared-statement round-trips. This is an area of active focus (see the changelog for recent fixes).
  • All-types scan: better-duck-core is moderately slower on mixed-type scans, reflecting per-type dispatch overhead in the read path.
Several query-path performance improvements landed in beta.3 and beta.4 and are reflected in the numbers above: Decimal binds no longer allocate a String per row; u128/UHUGEINT now has a direct typed append and bind path (previously fell back to a slower duckdb_value-based generic path); every query execution no longer heap-allocates a Box<duckdb_result> it immediately discards; and DuckResult::count() no longer materializes a full DuckRow for rows it is about to throw away. Check the changelog for before/after details.

Build docs developers (and LLMs) love