From cddf485584c3cfb51a64935d98fd371194f9fa3b Mon Sep 17 00:00:00 2001 From: Chris Richards Date: Mon, 6 Jul 2026 19:50:51 +0100 Subject: [PATCH] Record v2.2.0 benchmark results The benchmarks project now defaults to the published 2.2.0 package, and benchmarks.md gains the v2.2.0 comparative and index tables: the read path allocates 355.86 KB where 2.1.0 allocated 704.41 KB (now 0.84x NDbf, near Sylvan's 336.81 KB), and the full-scan query baselines drop 17-26% in time and 72-93% in allocations from column-subset parsing. The v1.1.0 section's reproduce command is corrected to the environment variable form (-p: never reached BenchmarkDotNet's generated child build) and its table is replaced with the run re-verified against a correctly pinned 1.1.0 package - within noise of the previous numbers, as the read path was genuinely unchanged between 1.1.0 and 2.1.0. Co-Authored-By: Claude Fable 5 --- benchmarks.md | 57 +++++++++++++++++-- .../DbfDataReader.Benchmarks.csproj | 6 +- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/benchmarks.md b/benchmarks.md index 3a2673f..816bf2f 100644 --- a/benchmarks.md +++ b/benchmarks.md @@ -32,16 +32,19 @@ Cross-library comparison: read every field of every record of `test/fixtures/tl_2019_01_place.dbf` (586 records), in the style of [MarkPflug/Benchmarks](https://github.com/MarkPflug/Benchmarks/blob/main/source/Benchmarks/DbfDataReaderBenchmarks.cs). Reproduce with: -`dotnet run -c Release --project test/DbfDataReader.Benchmarks -p:DbfDataReaderVersion=1.1.0 -- --filter "*DbfDataReaderBenchmarks*" --job short` +`DbfDataReaderVersion=1.1.0 dotnet run -c Release --project test/DbfDataReader.Benchmarks -- --filter "*DbfDataReaderBenchmarks*" --job short` +(the version must be set through the environment variable — `-p:DbfDataReaderVersion` +does not reach BenchmarkDotNet's generated child build; the benchmark log's +`// DbfDataReader under benchmark:` line shows the version actually measured) BenchmarkDotNet v0.15.8, macOS (Apple M3 Max), .NET 10.0.2, Job=ShortRun (numbers are not comparable with the v0.5.x tables above: different file and hardware) | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio | |-------- |---------:|----------:|---------:|------:|--------:|--------:|--------:|----------:|------------:| -| Sylvan | 284.1 us | 39.52 us | 2.17 us | 0.54 | 0.02 | 41.0156 | 0.9766 | 336.81 KB | 0.80 | -| NDbf | 527.2 us | 429.13 us | 23.52 us | 1.00 | 0.05 | 51.7578 | 0.9766 | 423.52 KB | 1.00 | -| DbfData | 611.1 us | 40.72 us | 2.23 us | 1.16 | 0.04 | 85.9375 | 11.7188 | 704.41 KB | 1.66 | +| Sylvan | 284.6 us | 63.42 us | 3.48 us | 0.52 | 0.01 | 41.0156 | 0.9766 | 336.81 KB | 0.80 | +| NDbf | 542.8 us | 144.89 us | 7.94 us | 1.00 | 0.02 | 51.7578 | 0.9766 | 423.52 KB | 1.00 | +| DbfData | 643.8 us | 19.39 us | 1.06 us | 1.19 | 0.02 | 85.9375 | 11.7188 | 704.4 KB | 1.66 | # v2.1.0 @@ -86,3 +89,49 @@ BenchmarkDotNet v0.15.8, macOS (Apple M3 Max), .NET 10.0.2, Job=ShortRun | | | | | | | | 'scan + sort' | top 10 order by desc | 60,556.31 us | 1.00 | 66357.99 KB | 1.00 | | index | top 10 order by desc | 2,986.39 us | 0.05 | 5661.96 KB | 0.09 | + +# v2.2.0 + +Same comparison against DbfDataReader 2.2.0 — the read path allocates half of what +2.1.0 did: character fields trim their padding before the string is materialized, +numeric and date fields parse straight from the record buffer, and null checks no +longer box (#303). + +| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio | +|-------- |---------:|----------:|---------:|------:|--------:|--------:|-------:|----------:|------------:| +| Sylvan | 297.9 us | 82.40 us | 4.52 us | 0.55 | 0.01 | 41.0156 | 0.9766 | 336.81 KB | 0.80 | +| NDbf | 537.0 us | 175.12 us | 9.60 us | 1.00 | 0.02 | 51.7578 | 0.9766 | 423.52 KB | 1.00 | +| DbfData | 610.0 us | 206.70 us | 11.33 us | 1.14 | 0.03 | 42.9688 | 7.8125 | 355.86 KB | 0.84 | + +# v2.2.0 — automatic index use + +Same generated 50,000-row table and query pairs as the v2.1.0 run above. The index +paths are unchanged, but the full-scan baselines are far cheaper than in v2.1.0: +queries parse only the columns they reference (#296), so for example the +`where ID = 25000` scan parses one binary column for the 49,999 rows that do not +match, and the halved string allocations (#303) shrink whatever must still be parsed. + +Reproduce with: +`dotnet run -c Release --project test/DbfDataReader.Benchmarks -- --filter "*IndexQueryBenchmarks*"` + +BenchmarkDotNet v0.15.8, macOS (Apple M3 Max), .NET 10.0.2, Job=ShortRun + +| Method | Categories | Mean | Ratio | Allocated | Alloc Ratio | +|---------------- |----------------------------- |-------------:|------:|------------:|------------:| +| 'full scan' | character seek (100 rows) | 17,791.35 us | 1.000 | 1976.55 KB | 1.00 | +| index | character seek (100 rows) | 150.55 us | 0.008 | 58.45 KB | 0.03 | +| | | | | | | +| 'read all rows' | count(*) all rows | 20,808.41 us | 1.00 | 7038.84 KB | 1.000 | +| 'status scan' | count(*) all rows | 15,611.37 us | 0.75 | 10.43 KB | 0.001 | +| | | | | | | +| 'full scan' | count(*) filtered (10k rows) | 17,806.75 us | 1.00 | 1183.69 KB | 1.00 | +| 'index only' | count(*) filtered (10k rows) | 1,040.94 us | 0.06 | 1201.06 KB | 1.01 | +| | | | | | | +| 'full scan' | equality seek (1 row) | 17,344.77 us | 1.000 | 1184.37 KB | 1.00 | +| index | equality seek (1 row) | 51.62 us | 0.003 | 32.36 KB | 0.03 | +| | | | | | | +| 'full scan' | range scan (100 rows) | 17,666.93 us | 1.000 | 1193.09 KB | 1.00 | +| index | range scan (100 rows) | 98.77 us | 0.006 | 54.31 KB | 0.05 | +| | | | | | | +| 'scan + sort' | top 10 order by desc | 45,792.49 us | 1.00 | 50999.07 KB | 1.00 | +| index | top 10 order by desc | 3,234.12 us | 0.07 | 5659.65 KB | 0.11 | diff --git a/test/DbfDataReader.Benchmarks/DbfDataReader.Benchmarks.csproj b/test/DbfDataReader.Benchmarks/DbfDataReader.Benchmarks.csproj index 4639151..6fdcc45 100644 --- a/test/DbfDataReader.Benchmarks/DbfDataReader.Benchmarks.csproj +++ b/test/DbfDataReader.Benchmarks/DbfDataReader.Benchmarks.csproj @@ -14,9 +14,11 @@ Release - + - 2.1.0 + 2.2.0