Benchmark

The same query over the same CSV file, run by five tools and timed from process start to exit with himorime. Before timing, every tool runs the query once and the outputs are compared byte for byte, so every bar below is the same answer.

Time, start to exit (median). Shorter is better.
aggregate 100k
sqly 277 ms
trdsql 220 ms
csvq 193 ms
textql 294 ms
duckdb 65 ms
filter 100k
sqly 247 ms
trdsql 196 ms
csvq 175 ms
textql 271 ms
duckdb 64 ms
export 100k
sqly 461 ms
trdsql 347 ms
csvq 206 ms
duckdb 94 ms
aggregate 1m
sqly 970 ms
trdsql 654 ms
csvq 727 ms
textql 1.20 s
duckdb 43 ms
Peak memory (median of the peak RSS). Shorter is better.
aggregate 100k
sqly 72 MiB
trdsql 22 MiB
csvq 189 MiB
textql 33 MiB
duckdb 48 MiB
filter 100k
sqly 69 MiB
trdsql 20 MiB
csvq 158 MiB
textql 31 MiB
duckdb 45 MiB
export 100k
sqly 77 MiB
trdsql 20 MiB
csvq 138 MiB
duckdb 89 MiB
aggregate 1m
sqly 112 MiB
trdsql 20 MiB
csvq 678 MiB
textql 49 MiB
duckdb 64 MiB

What the tools are #

ToolEngineNeeds cgo
sqlySQLite, as pure Go (modernc.org/sqlite), loaded by filesqlno
trdsqlSQLite through cgoyes
csvqits own engine, in Gono
textqlSQLite through cgo; no release since 2015yes
DuckDBa columnar engine in C++ that reads the file in parallelyes (C++)

sqly, trdsql and textql load every row into SQLite before the query runs, so their cost is mostly the load. sqly and trdsql differ in how that SQLite is built: compiled from C for trdsql, translated into Go for sqly. In return sqly is a single static binary for every platform Go builds for, with no C toolchain needed. DuckDB does not load the file into a table first; it scans only the columns the query needs, on every core, which is why it leads each benchmark by a wide margin.

textql is left out of the export benchmark: it reads the phone number 0389689232 as the number 389689232 and writes that back, so its output differs from the other four.

Results #

aggregate 100k

The top 10 countries by row count of the 100 000 customers in testdata/benchmark/customers100000.csv (12 columns, 17 MB), printed as CSV. Median of 10 runs.

ToolMedianP95Relative to sqlyCPU timePeak memory
sqly277 ms302 ms1.00x438 ms72 MiB
trdsql220 ms238 ms0.79x264 ms22 MiB
csvq193 ms197 ms0.70x629 ms189 MiB
textql294 ms306 ms1.06x343 ms33 MiB
duckdb65 ms70 ms0.23x90 ms48 MiB

filter 100k

The e-mail address and city of every customer in Japan, sorted by address: a scan of all 100 000 rows that keeps 418 of them. Median of 10 runs.

ToolMedianP95Relative to sqlyCPU timePeak memory
sqly247 ms255 ms1.00x407 ms69 MiB
trdsql196 ms219 ms0.79x239 ms20 MiB
csvq175 ms196 ms0.71x488 ms158 MiB
textql271 ms297 ms1.10x318 ms31 MiB
duckdb64 ms68 ms0.26x87 ms45 MiB

export 100k

Every row and column of the customers file written back out as CSV, so reading every value and writing every value is the whole cost. textql is left out: it reads the phone number 0389689232 as the number 389689232 and writes that, so its output differs from the other four. Median of 10 runs.

ToolMedianP95Relative to sqlyCPU timePeak memory
sqly461 ms479 ms1.00x653 ms77 MiB
trdsql347 ms357 ms0.75x403 ms20 MiB
csvq206 ms221 ms0.45x462 ms138 MiB
duckdb94 ms98 ms0.20x158 ms89 MiB

aggregate 1m

The row count and the sum of one column of a generated CSV of 1 000 000 rows (4 columns, 38 MB), where loading the file is nearly the whole cost. Median of 5 runs.

ToolMedianP95Relative to sqlyCPU timePeak memory
sqly970 ms979 ms1.00x1.64 s112 MiB
trdsql654 ms690 ms0.67x783 ms20 MiB
csvq727 ms742 ms0.75x2.07 s678 MiB
textql1.20 s1.27 s1.24x1.35 s49 MiB
duckdb43 ms48 ms0.04x82 ms64 MiB

CPU time is the user plus system time of the process and every child it waited for; more than the wall time means more than one core was busy. Peak memory is the largest resident set of any one process. Each tool gets two warm-up runs, then every round runs the tools in a shuffled order, so a busy moment on the machine lands on all of them.

Measured with himorime v0.5.1 on linux/amd64, AMD RYZEN AI MAX+ 395 w/ Radeon 8060S (32 logical CPUs), 2026-09-28. sqly was built from commit cf4bf82be38c.

  • trdsql: github.com/noborus/trdsql v1.2.3
  • csvq: csvq version 1.18.1
  • textql: github.com/dinedal/textql v0.0.0-20151217051953-1785cd353c68
  • duckdb: v1.5.5 (Variegata) d8cdaa33fd

Measuring it yourself #

The suite is bench/compare/himorime.yaml. With trdsql, csvq, textql and duckdb on PATH, make bench-docs runs it and writes website/data/benchmark.json, which this page’s chart and tables are drawn from. bench/README.md pins the versions measured. Numbers from different machines are not comparable; compare tools on one machine, as this page does.