Repository navigation
Performance and Benchmarks
Every figure on this page was measured, and every benchmark can be re-run from the repository. Unless a table says otherwise: an Intel i7-12700H laptop (20 logical cores) under Windows 11, each figure the best of 3 runs after a warm-up run applied to every contender alike.
Contents: Headline numbers · Wide tables · One large file, section by section · Queries · Shared files · LINQ · Memory and priority · Conversion · Re-run them · Measure your own data
200,000 customer records with 8 fields.
| Measure | Newtonsoft | System.Text.Json | JAZMIN |
|---|---|---|---|
| File size | 30,529 KB | not measured | 1,297 KB (deflate) / 838 KB (brotli) |
| File size, gzipped JSON for comparison | 3,324 KB | ||
| File size with 3 indexes + AES-256 encryption | 2,235 KB | ||
| Find one record by id (open the file → result) | 265 ms | 104 ms | 1.4 ms (1.3 ms encrypted) |
Filter Country == "NA" && Age > 80
|
276 ms | 13 ms | |
| Memory allocated for one lookup | 196 MB | 1.1 MB | |
| Deserialize every record | 211 ms | 91 ms | 32 ms |
| Serialize every record | 103 ms | 57 ms | 42 ms (94 ms with 3 indexes) |
| Measure | JSON | JAZMIN |
|---|---|---|
| File size | 31,307 KB (gzip: 3,338 KB) | 1,292 KB (deflate) / 838 KB (brotli) |
| Find one record by id | 109 ms | 1.7 ms (1.7 ms encrypted) |
| Read every record | 109 ms | 45 ms |
| Write every record | 277 ms | 169 ms (309 ms with 3 indexes) |
| Broad filter matching 1 row in 8, spread through the file | 116 ms | 13 ms |
Text search (contains) |
112 ms | 21 ms |
Memory to answer a lookup (--expose-gc) |
42 MB | under 0.1 MB |
On Node 26 (same day), JAZMIN takes 1.8 ms per lookup and 58 ms to read every record; JSON.parse is about 10% faster
there than on Node 24.
What the numbers mean:
- Size: about 24× smaller than JSON and 61% smaller than gzipped JSON with deflate; with Brotli, 75% smaller than gzipped JSON. This is regular benchmark data; expect less on very varied data.
- Finding records: about 75–190× faster than parsing JSON in .NET (System.Text.Json and Newtonsoft), about 64× in Node.
-
Reading a whole file: faster than
JSON.parseand System.Text.Json. -
Writing: without indexes, faster than
JSON.stringifyand both .NET serializers, while also compressing. With 3 indexes: a little faster than Newtonsoft, about 1.6× System.Text.Json, and about 1.1×JSON.stringify.
History: measured 5 October 2026 and re-run for 1.1.0 on 6 October, every figure within 10%. Since 1.0.0, query planning took lookups from 2.7 to 1.7 ms in Node and from 3.7 to 1.9 ms in .NET, and the filter from 56 to 13 ms and from 40 to 13 ms. In 1.2.0, .NET readers of files with the same columns share their compiled row code, which took the .NET lookup from about 2.0 to 1.4 ms. Checked again for 1.2.0 on 7 October against 1.1.0 side by side (runs alternating, on a busier machine): Node unchanged within noise; .NET lookups 2.6–2.7 ms (1.1.0) against 1.9 ms (1.2.0); file sizes identical.
300 million values: a table nobody can hold in memory as JSON. Written and read as streams on both sides, each test in its own process; JSON read with each platform's streaming parser. Memory is the peak working set of the process.
| Measure | JSON (Node) | JAZMIN (Node) | JSON (.NET 10, System.Text.Json) | JAZMIN (.NET 10) |
|---|---|---|---|---|
| File size | 6,346 MB (gzip 1,393 MB) | 643 MB | 6,212 MB | 649 MB |
| Open the file | — | 0.01 s, 63 MB | — | 0.03 s, 34 MB |
| Look up one id | 52.0 s, 291 MB | 0.03 s, 76 MB | 24.1 s, 47 MB | 0.08 s, 45 MB |
| Sum 3 of the 300 columns | 70.6 s, 293 MB | 3.4 s, 121 MB | 35.5 s, 47 MB | 1.1 s, 67 MB |
| Filter on 2 columns | 75.4 s, 293 MB | 3.4 s, 134 MB | 33.5 s, 49 MB | 1.2 s, 67 MB |
| Read every row | 100.2 s, 292 MB | 15.9 s, 204 MB (98 MB¹) | 38.2 s, 47 MB | 10.4 s, 64 MB |
| Write | 81.8 s, 251 MB | 69 s, 338 MB² | 14.8 s, 48 MB | 11.3 s, 144 MB |
Times include generating the rows, done the same way on both sides. Repeated runs vary by up to 20%. ¹ With
node --max-semi-space-size=8. ² With 2 worker threads (the default); on the main thread only, 90 s, 319 MB.
- Reads decode only the columns a query uses, and chunk statistics load per column on demand, so opening costs the same however many columns there are.
- .NET keeps values in typed arrays until you read them, decodes chunks ahead on worker threads within a memory budget, and encodes, compresses and encrypts on worker threads when writing.
- Node keeps about 11 MB alive during a full scan; most of the rest is garbage V8 hasn't collected yet.
A 271 MB JSON file of 1.37 million statement lines in 5,000 sections (about 274 lines each), one PDF per section:
| Reload the JSON per section | JAZMIN | |
|---|---|---|
| Time to get one section | 1.3 s | 0.3–0.4 ms (p99: 3.4 ms) |
| All 5,000 sections | ≈ 1.8 hours | 1.5–2 s |
| Peak memory of the process | 1,659 MB | 73–85 MB (an idle Node process is ~50 MB) |
| One-off conversion | — | 9 s, 71 MB peak, file 23.6 MB |
Against streaming JSON readers (Newtonsoft JsonTextReader, System.Text.Json DeserializeAsyncEnumerable, a Node
streaming parser), which fix JSON's memory problem but not its access problem:
| Same 271 MB file | Streaming JSON | JAZMIN |
|---|---|---|
| Section near the start | 2–48 ms | 0.3–1.8 ms |
| Section in the middle | 0.7–1.3 s | 0.3–1.8 ms |
| Section at the end | 1.3–2.6 s | 0.3–1.8 ms |
| All 5,000 sections, one at a time | ≈ 1.2–1.7 hours | 1.4–2.3 s |
| Memory allocated per section (.NET) | up to 416 MB (STJ), 747 MB (Newtonsoft) | ≈ 3 MB |
| Full sequential read, Node | 3.2 s | 1.3 s |
| Full sequential read, .NET, raw rows | STJ 1.2 s, Newtonsoft 1.7 s | 0.9 s |
| Full sequential read, .NET, into C# objects | STJ 1.2 s, Newtonsoft 1.7 s | 1.4 s |
The only case where streaming JSON leads is reading everything once into C# objects with System.Text.Json, by about 15%.
Selective filters returning whole rows (1.3.0). 2.5M order lines, every column returned, best of 3. The columns a query returns are decoded only for matching rows:
| Query | Node 1.2 | Node 1.3 | .NET 1.2 | .NET 1.3 |
|---|---|---|---|---|
order_id in 2,000 ids (4,971 rows) |
774 ms | 347 ms | 344 ms, 375 MB allocated | 261 ms, 132 MB |
| One product's lines (2,474 rows) | 1,106 ms | 344 ms | 369 ms, 318 MB allocated | 231 ms, 130 MB |
.NET JSON columns (1.3.0) are read from their stored UTF-8 straight into typed members:
| A LINQ report over 200,000 orders (customer and items as JSON columns) | Time | Allocated |
|---|---|---|
| JAZMIN 1.2.0 | 6.8 s | 1,629 MB |
| JAZMIN 1.3.0 | 3.1 s | 965 MB |
| System.Text.Json, the same data as one JSON file | 4.4 s | 828 MB |
| System.Text.Json, streamed | 2.8 s | 638 MB |
.NET LINQ sub-queries per row (1.3.0) are read once. 5,000 customers and 50,000 orders, with products and payments:
| Query | 1.2.0 | 1.3.0 | Same code over in-memory lists |
|---|---|---|---|
| Latest 5 orders of each customer, with product and payment | 64 s, 9.9 GB allocated | 0.43 s, 56 MB | 2.1 s |
SelectMany per customer + First / FirstOrDefault + GroupBy report |
107 s, 16.5 GB | 0.44 s, 45 MB | 2.1 s |
.NET LINQ (1.3.0) reads only the columns a query uses, through GroupBy, SelectMany, Join and anonymous
objects: grouping 200,000 orders by region and summing amounts went from 1.1 s and 261 MB to 0.25 s and 57 MB
(details).
200,000 transactions (npm run bench:proposals), bytes read and time per query, 1.0.0 against 1.2.0:
| Query | 1.0.0 | 1.2.0 |
|---|---|---|
| Deep page (offset 150,000) | 3,563 KB, 59 ms | 97 KB, 1.7 ms |
| Newest 50 rows | 4,709 KB, 78 ms | 82 KB, 1.5 ms |
| A page of one account's rows after a time (file sorted by account) | 3,785 KB, 109 ms | 102 KB, 2.2 ms |
| One day across all accounts | 7,467 KB, 206 ms | 4,938 KB, 70 ms |
| 12 monthly counts over a year (file sorted by time) | 5,771 KB, 104 ms | 1,062 KB, 15 ms |
One account's count (sorted by time, account indexed) |
4,766 KB, 81 ms | 57 KB, 1.2 ms |
| Fetching one day's 549 rows by position | Read | Time |
|---|---|---|
| In time order, as they came | 53,815 KB | 673 ms |
| Sorted by position first | 4,895 KB | 62 ms |
| Sorted index over unique values, open + one lookup (Node) | Index in one piece (before 1.0) | Index in pages |
|---|---|---|
| 1,000,000 rows | 149 ms, 189 MB, 7.6 MB read | 8.5 ms, 56 MB, 72 KB read |
| 5,000,000 rows | 886 ms, 730 MB, 36 MB read | 10.6 ms, 58 MB, 110 KB read |
| Compact indexes (New in 1.2), 200,000 rows, indexes on an id, a code and a date | Off | On |
|---|---|---|
| File size | 4.5 MB | 1.8 MB |
| Lookup time | 5–16% less |
| Column arrays, 200,000 rows of a date and an amount, memory kept | Row objects | columnArrays() |
|---|---|---|
| Node | 30.7 MB | 3.3 MB |
| Browser reader | 34.8 MB | 3.8 MB |
columnArrays() over one million rows of two columns: 121–135 ms in Node, 300–305 ms in the browser reader.
1.37M lines, 5,000 sections, one client key per section:
| JavaScript | .NET | |
|---|---|---|
| Write | 2.0 s | 1.2 s |
| Client: open the file + read their section | 1.7 ms | 1.6 ms |
| Owner: open the file + read one section | 2.1 ms | 1.8 ms |
| Owner: each further section, file open | 0.27 ms | 0.21 ms |
| Owner: find one record by id among 250,000 | JavaScript | .NET |
|---|---|---|
| 100 partitions | 1.7 ms (4.9 ms without the chunk map) | 3.1 ms (5.0 ms without) |
| 1,000 partitions | 2.0 ms (49 ms without) | 2.7 ms (28 ms without) |
- A client's open stays about the same however many keys the file has: 1.4 ms in .NET with 1, 5,000 or 50,000 keys.
- A one-record append takes about the same with 1,000 partitions as with 100 (JavaScript 16 and 12 ms, .NET 23 and 14 ms).
- Changing one section of the 1.37M-row file:
update()2.7 s,append()63 ms. - A year of syncs (50 people, 37,500 appends), compacted with
regroup: 538 KB and 50 chunks instead of 7,241 KB and 37,500; one person's 3,000 rows read 10 KB in 2 ms instead of 125 KB in 23 ms. - Filing a batch of returned records: 20–25 ms, whether 1 record or 50, and with 10,000 or 250,000 records in the file.
- 1.6.0: a full count of a shared file of 250,000 rows written as 250,000 chunks (every row a different partition) uses 152 MB instead of 234 MB in JavaScript, and is 8% faster; other reads are unchanged (median of 7 rounds against the published 1.5.0).
- 1.6.0,
reader.storage()(is compacting worth it?): 0.1 ms on a 1,000,000-row file, 3 ms after 200 appends, 31 ms in JavaScript and 14 ms in .NET for a shared file of 250,000 rows in 1,000 partitions. - Key rotation without decoding rows, 176 MB file: 0.7 s (Node, 138 MB; .NET, 45 MB), against 14–33 s for a rewrite.
.NET 10, 200,000 rows × 300 columns, a type mapping 9 of them; 1.1.0's Query<T> followed by LINQ to Objects, against
1.2's AsQueryable:
| Query | 1.1.0 | AsQueryable |
|---|---|---|
| Every row as objects | 3.2–3.7 s | 0.30 s |
Where + Select of two columns |
3.6 s | 0.35–0.37 s |
Count(condition) |
3.3–3.9 s, 63 MB | 0.25 s, 56 MB |
Skip(150_000).Take(10) |
2.5–2.7 s, 63 MB | 0.05 s, 35 MB |
OrderBy(o => o.Id).Take(10) (file sorted by id) |
3.5 s, 100 MB | 0.05 s, 35 MB |
Sum(o => o.Qty) |
3.2–3.4 s | 0.25 s |
First(o => o.Id == 123_456) |
0.08–0.09 s | 0.07 s |
200,000 rows × 300 columns, medians of 3–5 runs, each in its own process, peak memory of the process:
memory |
balanced (default) |
speed |
|
|---|---|---|---|
| .NET: sum 3 columns | 0.76 s, 49 MB | 0.27 s, 54 MB | 0.19 s, 65 MB |
| .NET: filter on 2 columns | 0.74 s, 49 MB | 0.26 s, 54 MB | 0.19 s, 65 MB |
| .NET: read every row | 2.33 s, 60 MB | 2.29 s, 60 MB | 2.10 s, 95 MB |
| .NET: write | 11.2 s, 68 MB | 4.3 s, 79 MB | 4.5 s, 82 MB |
Node: sum 3 columns (columnArrays) |
0.66 s, 125 MB | 0.66 s, 125 MB | 0.24 s, 196 MB |
| Node: filter on 2 columns | 0.70 s, 96 MB | 0.70 s, 96 MB | 0.23 s, 162 MB |
| Node: read every row | 4.80 s, 226 MB | 4.80 s, 226 MB | 4.34 s, 256 MB |
| Node: write | 19.6 s, 233 MB | 15.0 s, 257 MB | as balanced |
| Node, 1M × 300 columns | Default | --max-semi-space-size=8 |
|---|---|---|
| Read every row | 15.9 s, 204 MB | 16.1 s, 98 MB |
| Sum 3 columns | 3.4 s, 121 MB | 3.2 s, 118 MB |
| Write | 87.1 s, 299 MB | 86.1 s, 302 MB |
| A 1 GB file | CSV (10 million rows) | XML (4.6 million rows) |
|---|---|---|
| .NET | 14 s, 63 MB | 11 s, 65 MB |
Node, --max-old-space-size=64 --max-semi-space-size=2
|
44 s, 92 MB | 48 s, 86 MB |
| Node, default flags | 32 s, 316 MB | 41 s, 250 MB |
| Export shapes, 1M transactions for 10,000 clients, nested lines | Node | .NET |
|---|---|---|
| File sorted by the grouping column | 2.3 s | 1.8 s, 66 MB |
| Not sorted | 8.7 s | 5.6 s, 153 MB |
| 1.3.0: sorted | 1.9 s, 147 MB | 1.6–1.8 s, 69 MB |
1.3.0: not sorted, priority: 'speed'
|
4.0 s, 559 MB | 5.3 s, 381 MB |
| Export shapes across tables (1.3.0): 100,000 customers, 1M orders, 2.5M lines (360 MB of JSON) | Node | .NET |
|---|---|---|
| Each linked table sorted by its link | 7.2 s, 220 MB | 4.5 s, 84 MB |
| The same, one customer | 0.08 s | 0.17 s |
| Orders and lines in time order (batches), by default | 27 s, 578 MB | 19 s, 238 MB |
| For comparison: the three tables exported one by one | 4.8 s, 200 MB | 2.6 s, 70 MB |
An in list of 20,000 values against 100,000 rows |
Node | .NET |
|---|---|---|
| 1.2.0 (each row compared with every value) | 24 s | 7.3 s |
| 1.3.0 (a hash set) | 18 ms | 36 ms |
| Rows as JSON (.NET), 200,000 × 300 columns (1.3 GB of JSON) | Time | Memory |
|---|---|---|
JazminJsonStream |
7.1 s | 63 MB |
JazminJsonReader (120 million tokens) |
4.8 s | 62 MB |
DeserializeAsyncEnumerable over the stream |
13 s | 70 MB |
| Several tables: 20,000 clients, 300,000 transactions (Node) | Flat | Two tables |
|---|---|---|
| File size | 3.70 MB | 3.39 MB |
| Write | 1.28 s | 0.94 s |
| Read every transaction | 176 ms | 145 ms |
For reference, the same 200,000 customers in formats built for other jobs:
- Parquet: columnar analytics files;
- Arrow IPC: columns as they are in memory;
- SQLite: an embedded SQL database with B-tree indexes;
- MessagePack: binary JSON.
Each format's library is used with its defaults, each query opens the file, and every answer is checked. They were
measured on 9 October 2026 on a laptop that was about a third busy with other work, so compare across a row. Each
cell is the best of 2-3 runs. Some rows were measured again later the same day, after changes of 1.4, each as
the best of 2 runs: Node's text search, "Find one row by id" and "Write every row" rows. The .NET tables were measured
again, whole, on the quiet machine after those changes. JAZMIN counts without building an object per row, as its
guides show: count() in Node, AsQueryable<T>().Count(predicate) in .NET.
.NET 10 (Parquet.Net, Apache.Arrow, Microsoft.Data.Sqlite, MessagePack-CSharp):
| Measure | JAZMIN | Parquet | Arrow IPC | SQLite | MessagePack |
|---|---|---|---|---|---|
| File size | 2,232 KB | 3,659 KB | 13,980 KB | 15,340 KB | 11,872 KB |
| Write every row | 123 ms | 98 ms | 51 ms | 476 ms | 34 ms |
| Read every row (objects) | 39 ms | 162 ms | 323 ms | 233 ms | 46 ms |
| Sum one column | 26.7 ms | 2.8 ms | 5.5 ms | 14.0 ms | 48.7 ms |
| Find one row by id (open → row) | 0.9 ms | 95.8 ms | 5.3 ms | 0.4 ms | 45.6 ms |
| Filter: country = NA, age > 80 | 6.0 ms | 12.4 ms | 5.8 ms | 12.9 ms | 44.3 ms |
| Text search: name contains 'Ndlovu' | 6.8 ms | 15.6 ms | 9.0 ms | 18.7 ms | 47.2 ms |
Node.js 24 (hyparquet, apache-arrow, node:sqlite, msgpackr):
| Measure | JAZMIN | Parquet | Arrow IPC | SQLite | MessagePack |
|---|---|---|---|---|---|
| File size | 2,227 KB | 2,935 KB | 12,027 KB | 15,340 KB | 21,626 KB |
| Write every row | 326 ms | 424 ms | 418 ms | 405 ms | 110 ms |
| Read every row (objects) | 109 ms | 158 ms | 612 ms | 717 ms | 255 ms |
| Sum one column | 30.6 ms | 32.3 ms | 15.4 ms | 29.8 ms | 265.8 ms |
| Find one row by id (open → row) | 1.9 ms | 82.5 ms | 7.3 ms | 0.7 ms | 162.7 ms |
| Filter: country = NA, age > 80 | 23.9 ms | 77.2 ms | 14.6 ms | 27.0 ms | 243.0 ms |
| Text search: name contains 'Ndlovu' | 16.6 ms | 17.3 ms | 15.8 ms | 18.5 ms | 168.1 ms |
Memory (peak memory added; each operation in a process of its own, after the same operation on a 1,000-row file so each library's code is loaded, with memory sampled throughout; native memory counts too):
| Node.js 24 | JAZMIN | Parquet | Arrow IPC | SQLite | MessagePack |
|---|---|---|---|---|---|
| Write every row | 124.1 MB | 179.9 MB | 230.1 MB | 16.8 MB | 44.1 MB |
| Read every row (objects) | 75.5 MB | 114.1 MB | 102.2 MB | 282.5 MB | 153.8 MB |
| Sum one column | 11.1 MB | 35.1 MB | 12.9 MB | 1.9 MB | 154.3 MB |
| Find one row by id | 0.3 MB | 87.5 MB | 12.9 MB | 0.1 MB | 151.2 MB |
| Filter | 11.3 MB | 48.3 MB | 14.0 MB | 1.7 MB | 150.9 MB |
| Text search | 12.1 MB | 19.5 MB | 15.4 MB | 2.0 MB | 151.9 MB |
| .NET 10 | JAZMIN | Parquet | Arrow IPC | SQLite | MessagePack |
|---|---|---|---|---|---|
| Write every row | 72.8 MB | 58.0 MB | 46.7 MB | 18.2 MB | 23.8 MB |
| Read every row (objects) | 59.1 MB | 99.5 MB | 77.9 MB | 12.6 MB | 55.7 MB |
| Sum one column | 19.3 MB | 10.5 MB | 14.1 MB | 1.8 MB | 55.8 MB |
| Find one row by id | 0.7 MB | 83.0 MB | 13.8 MB | 0.1 MB | 55.7 MB |
| Filter | 15.9 MB | 27.7 MB | 15.4 MB | 1.8 MB | 55.7 MB |
| Text search | 10.7 MB | 33.7 MB | 23.5 MB | 1.8 MB | 55.7 MB |
SQLite needs almost no memory for a query: it reads its file a page at a time through a small cache.
-
Where JAZMIN leads:
- the smallest file, with its 3 indexes;
- the fastest read of every row (in Node, also with the least memory);
- in .NET, the fastest text search (6.8 ms), and filters level with Arrow;
- finding one row by id second only to SQLite;
- the only one with keys, encryption and per-key access.
-
What it can work towards:
- finding one row as fast as SQLite on a process's first calls. In 1.4:
- Node and the viewer decode only the row's values: Node 2.7 -> 1.9 ms here, and 1.15 -> 0.70 ms once Node has optimised the code, as fast as SQLite; the viewer 1.8 -> 1.3-1.4 ms with the file open;
- .NET decodes a lookup's chunk on the query's thread and reads each column only as far as the row: 1.6 -> 0.9 ms here, 6.3 -> 0.7 MB, and a LINQ lookup 0.63 -> 0.49 ms once warm;
- sums of one column, where Parquet.Net (2.8 ms) and Arrow (5.5 ms) are 5-10 times faster in .NET: JAZMIN compresses each chunk whole, all its columns together, so a sum of one column decompresses all of them. Closing this needs columns compressed apart or a faster codec, a format change for after 1.4;
- filters in Node, where Arrow is about twice as fast;
- writing, where MessagePack is 3-5 times faster;
- memory for writing (in 1.4, Node writes this file with 124 MB, down from 181-185 MB).
- finding one row as fast as SQLite on a process's first calls. In 1.4:
- Text search, in 1.4: a search reads only the parts of the trigram index it needs, and scans when the word is in most rows. Node 35.0 -> 16.6 ms and 34 -> 12 MB; .NET 6.8 ms and 11 MB, with the index read into a reused buffer.
-
Run them yourself:
- Node:
npm installthennpm run benchinjs/bench/formats; - .NET:
dotnet run -c Release --project bench/Jazmin.FormatBenchmarksindotnet/.
- Node:
From a clone of the repository:
| Benchmark | Node (in js/) |
.NET (in dotnet/) |
|---|---|---|
| Headline (200,000 records vs JSON) | npm run bench |
dotnet run -c Release -f net10.0 --project bench/Jazmin.Benchmarks |
| Wide tables | node bench/wide.js 1000000 300 |
dotnet run -c Release -f net10.0 --project bench/Jazmin.Benchmarks -- wide 1000000 300 |
| Sections of one large file | node bench/sections.js 300 5000 |
... -- streaming statements.json 5000 |
| Shared files | node bench/access.js |
... -- access |
| Typical statement queries (bytes, rows, time) | npm run bench:proposals |
|
| Filing returned records | npm run bench:filing |
|
reader.storage() (1.6.0) |
npm run bench:storage |
|
Changing indexes, the sort order and tables; set (1.6.0) |
npm run bench:layout |
|
| Checking links between tables (1.6.0) | npm run bench:links |
Each measurement runs in its own process and reports time and peak memory. Run .NET benchmarks with -c Release;
debug builds are several times slower.
Timings depend on the machine, so this script prints them rather than checking them. Run it on your own data shape by changing the row generator:
import { performance } from 'node:perf_hooks';
import { open, write } from '@smithsoft-studios/jazmin';
const rows = Array.from({ length: 200_000 }, (_, i) => ({
id: i + 1, name: `Customer ${i + 1}`, country: ['ZA', 'NA', 'BW', 'KE'][i % 4], age: 18 + (i % 70), balance: (i * 7919) % 100000 / 100,
}));
// Like the published benchmarks: a warm-up run, then the best of 5
const time = (label, fn) => {
let result = fn();
let best = Infinity;
for (let run = 0; run < 5; run++) {
const start = performance.now();
result = fn();
best = Math.min(best, performance.now() - start);
}
console.log(label.padEnd(28), `${best.toFixed(1)} ms`);
return result;
};
const json = time('JSON.stringify', () => JSON.stringify(rows));
const jzm = time('JAZMIN write', () => write(null, rows, { sortedBy: ['id'], indexes: { country: 'sorted' } }));
console.log('sizes'.padEnd(28), `${Math.round(json.length / 1024)} KB JSON, ${Math.round(jzm.length / 1024)} KB JAZMIN`);
time('JSON: find one by id', () => JSON.parse(json).find((r) => r.id === 123_456));
time('JAZMIN: find one by id', () => [...open(jzm).find({ id: 123_456 })]);
time('JSON: filter', () => JSON.parse(json).filter((r) => r.country === 'NA' && r.age > 80).length);
time('JAZMIN: filter', () => open(jzm).count({ country: 'NA', age: { gt: 80 } }));One run on the benchmark laptop (7 October 2026, Node 24):
JSON.stringify 43.7 ms
JAZMIN write 163.4 ms
sizes 15363 KB JSON, 1115 KB JAZMIN
JSON: find one by id 48.4 ms
JAZMIN: find one by id 1.0 ms
JSON: filter 48.7 ms
JAZMIN: filter 12.7 msWriting costs more than JSON.stringify here because JAZMIN also builds an index and compresses; the JSON would still
need gzip and encryption to compare like with like. Every read is faster.
For a measured view of what a query reads (bytes, chunks, columns) rather than its time, use
explain(filter, { analyze: true }): see Reading and Querying.
JAZMIN 1.2 · file format 1.0 · Repository · npm · NuGet · MIT license · Examples in this wiki are run and checked by tools/check-examples.mjs
Start
Using files
- Writing Files
- Nested Columns
- Reading and Querying
- Filter Language
- .NET Serializer and LINQ
- Updating and Appending
- Several Tables
- Converting JSON, CSV, XML
- Export Shapes
Security and sharing
- Encryption and Keys
- Access Control
- Masked Columns
- Time-Limited Access
- Sending Records Back
- Security Model
Documents and apps
Performance
Reference