From 22a00c8b45c2433909d873471d79dba3b2f8587c Mon Sep 17 00:00:00 2001 From: Wei Lin Date: Thu, 10 Sep 2026 11:07:09 +0800 Subject: [PATCH] Expand benchmark to three runtimes --- README.es.md | 20 ++++--- README.fr.md | 20 ++++--- README.ja.md | 20 ++++--- README.md | 21 ++++--- README.zh-CN.md | 19 ++++--- README.zh-TW.md | 19 ++++--- benchmarks/nuget-v1-query/Program.cs | 6 +- docs/dotnet-v1-query-benchmark.md | 23 +++++--- docs/dotnet-v1-query-benchmark.zh-CN.md | 22 +++++--- docs/i18n/README.es.md | 20 ++++--- docs/i18n/README.fr.md | 20 ++++--- docs/i18n/README.ja.md | 20 ++++--- docs/i18n/README.zh-CN.md | 19 ++++--- docs/i18n/README.zh-TW.md | 19 ++++--- miniexcel/examples/stress_query.rs | 67 +++++++++++++++++++--- scripts/compare-nuget-v1-rust.ps1 | 74 +++++++++++++++++-------- 16 files changed, 274 insertions(+), 135 deletions(-) diff --git a/README.es.md b/README.es.md index 2bce0fd..b7abad2 100644 --- a/README.es.md +++ b/README.es.md @@ -114,14 +114,18 @@ Consulta la [matriz de compatibilidad](docs/compatibility.md), el [contrato de a ### Últimos Resultados NuGet -La última prueba Windows x64 comparó `MiniExcel 1.46.0` con `MiniExcel.Rust 0.1.0-preview.1` -sobre 100.000 filas x 10 columnas, usando cinco procesos nuevos por runtime y escenario. Antes de -medir se verificaron todas las filas, columnas y valores normalizados. - -| Escenario | MiniExcel | MiniExcel.Rust | Aceleración Rust | Reducción asignaciones | Reducción working set | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2.052,91 ms | 1.278,59 ms | 1,61x | 88,6 % | 4,6 % | -| Steady | 4.635,98 ms | 3.380,16 ms | 1,37x | 88,6 % | 10,1 % | +La última prueba Windows x64 comparó `MiniExcel 1.46.0`, el wrapper .NET +`MiniExcel.Rust 0.1.0-preview.1` y `MiniExcel Rust 0.4.0` nativo sobre 100.000 filas x 10 columnas, +con cinco procesos nuevos por runtime y escenario. Todos los valores se verificaron antes de medir. + +| Escenario | Runtime | Mediana | Filas/s | Primera fila | Asignación administrada | Peak working set | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2.598,50 ms | 38.484 | 1.051,97 ms | 2.541,93 MB | 50,93 MB | +| Cold | MiniExcel.Rust (.NET) | 1.899,42 ms | 52.648 | 450,85 ms | 289,36 MB | 49,64 MB | +| Cold | MiniExcel.Rust | 1.220,82 ms | 81.912 | 537,17 ms | n/a | 4,14 MB | +| Steady | MiniExcel | 5.997,76 ms | 50.019 | 616,40 ms | 7.625,15 MB | 52,80 MB | +| Steady | MiniExcel.Rust (.NET) | 4.202,10 ms | 71.393 | 442,59 ms | 868,05 MB | 48,79 MB | +| Steady | MiniExcel.Rust | 3.344,20 ms | 89.708 | 467,30 ms | n/a | 4,20 MB | Los resultados dependen de la máquina. Ejecuta `pwsh ./scripts/compare-nuget-v1-rust.ps1` con workbooks representativos; consulta la [metodología](docs/dotnet-v1-query-benchmark.md). diff --git a/README.fr.md b/README.fr.md index e599bec..0e8efd5 100644 --- a/README.fr.md +++ b/README.fr.md @@ -114,14 +114,18 @@ Voir la [matrice de compatibilité](docs/compatibility.md), le [contrat analyse/ ### Derniers Résultats NuGet -Le dernier test Windows x64 compare `MiniExcel 1.46.0` et `MiniExcel.Rust 0.1.0-preview.1` -sur 100 000 lignes x 10 colonnes, avec cinq processus neufs par runtime et scénario. Toutes les -lignes, colonnes et valeurs normalisées sont vérifiées avant la mesure. - -| Scénario | MiniExcel | MiniExcel.Rust | Accélération Rust | Réduction allocations | Réduction working set | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2 052,91 ms | 1 278,59 ms | 1,61x | 88,6 % | 4,6 % | -| Steady | 4 635,98 ms | 3 380,16 ms | 1,37x | 88,6 % | 10,1 % | +Le dernier test Windows x64 compare `MiniExcel 1.46.0`, le wrapper .NET +`MiniExcel.Rust 0.1.0-preview.1` et `MiniExcel Rust 0.4.0` natif sur 100 000 lignes x 10 colonnes, +avec cinq processus neufs par runtime et scénario. Toutes les valeurs sont vérifiées avant la mesure. + +| Scénario | Runtime | Temps médian | Lignes/s | Première ligne | Allocation managée | Working set maximal | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2 598,50 ms | 38 484 | 1 051,97 ms | 2 541,93 MB | 50,93 MB | +| Cold | MiniExcel.Rust (.NET) | 1 899,42 ms | 52 648 | 450,85 ms | 289,36 MB | 49,64 MB | +| Cold | MiniExcel.Rust | 1 220,82 ms | 81 912 | 537,17 ms | n/a | 4,14 MB | +| Steady | MiniExcel | 5 997,76 ms | 50 019 | 616,40 ms | 7 625,15 MB | 52,80 MB | +| Steady | MiniExcel.Rust (.NET) | 4 202,10 ms | 71 393 | 442,59 ms | 868,05 MB | 48,79 MB | +| Steady | MiniExcel.Rust | 3 344,20 ms | 89 708 | 467,30 ms | n/a | 4,20 MB | Les résultats dépendent de la machine. Relancez `pwsh ./scripts/compare-nuget-v1-rust.ps1` sur des workbooks représentatifs ; voir la [méthodologie](docs/dotnet-v1-query-benchmark.md). diff --git a/README.ja.md b/README.ja.md index 1e5771b..9732e1d 100644 --- a/README.ja.md +++ b/README.ja.md @@ -113,14 +113,18 @@ MiniExcel::save_as("book.xlsx", &[row])?; ### 最新 NuGet 結果 -最新の Windows x64 テストでは、100,000 行 x 10 列の workbook で `MiniExcel 1.46.0` と -`MiniExcel.Rust 0.1.0-preview.1` を比較し、runtime とシナリオごとに 5 個の新規 process を使用しました。 -計測前に全行・全列・正規化した値の一致を確認しています。 - -| シナリオ | MiniExcel | MiniExcel.Rust | Rust 高速化 | Allocation 削減 | Working set 削減 | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2,052.91 ms | 1,278.59 ms | 1.61x | 88.6% | 4.6% | -| Steady | 4,635.98 ms | 3,380.16 ms | 1.37x | 88.6% | 10.1% | +最新の Windows x64 テストでは、100,000 行 x 10 列の workbook で `MiniExcel 1.46.0`、 +.NET wrapper `MiniExcel.Rust 0.1.0-preview.1`、native `MiniExcel Rust 0.4.0` を比較しました。 +runtime とシナリオごとに 5 個の新規 process を使用し、計測前にすべての値を検証しています。 + +| シナリオ | Runtime | 中央値 | 行/秒 | 最初の行 | Managed allocation | Peak working set | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2,598.50 ms | 38,484 | 1,051.97 ms | 2,541.93 MB | 50.93 MB | +| Cold | MiniExcel.Rust (.NET) | 1,899.42 ms | 52,648 | 450.85 ms | 289.36 MB | 49.64 MB | +| Cold | MiniExcel.Rust | 1,220.82 ms | 81,912 | 537.17 ms | n/a | 4.14 MB | +| Steady | MiniExcel | 5,997.76 ms | 50,019 | 616.40 ms | 7,625.15 MB | 52.80 MB | +| Steady | MiniExcel.Rust (.NET) | 4,202.10 ms | 71,393 | 442.59 ms | 868.05 MB | 48.79 MB | +| Steady | MiniExcel.Rust | 3,344.20 ms | 89,708 | 467.30 ms | n/a | 4.20 MB | 結果はマシンに依存します。代表的な workbook で `pwsh ./scripts/compare-nuget-v1-rust.ps1` を実行し、 [測定方法](docs/dotnet-v1-query-benchmark.md)を参照してください。 diff --git a/README.md b/README.md index 8d93d9c..da08377 100644 --- a/README.md +++ b/README.md @@ -114,14 +114,19 @@ See the [compatibility matrix](docs/compatibility.md), [analytics and RAG contra ### Latest NuGet Results -The latest Windows x64 run compared public `MiniExcel 1.46.0` with -`MiniExcel.Rust 0.1.0-preview.1` over 100,000 rows x 10 columns, using five fresh processes per -runtime and scenario. Every row, column, and normalized value matched before timing. - -| Scenario | MiniExcel | MiniExcel.Rust | Rust speedup | Allocation reduction | Working-set reduction | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2,052.91 ms | 1,278.59 ms | 1.61x | 88.6% | 4.6% | -| Steady | 4,635.98 ms | 3,380.16 ms | 1.37x | 88.6% | 10.1% | +The latest Windows x64 run compared public `MiniExcel 1.46.0`, the +`MiniExcel.Rust 0.1.0-preview.1` .NET wrapper, and native `MiniExcel Rust 0.4.0` over 100,000 rows +x 10 columns, using five fresh processes per runtime and scenario. Every row, column, and +normalized value matched before timing. + +| Scenario | Runtime | Median elapsed | Rows/s | First row | Managed allocation | Peak working set | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2,598.50 ms | 38,484 | 1,051.97 ms | 2,541.93 MB | 50.93 MB | +| Cold | MiniExcel.Rust (.NET) | 1,899.42 ms | 52,648 | 450.85 ms | 289.36 MB | 49.64 MB | +| Cold | MiniExcel.Rust | 1,220.82 ms | 81,912 | 537.17 ms | n/a | 4.14 MB | +| Steady | MiniExcel | 5,997.76 ms | 50,019 | 616.40 ms | 7,625.15 MB | 52.80 MB | +| Steady | MiniExcel.Rust (.NET) | 4,202.10 ms | 71,393 | 442.59 ms | 868.05 MB | 48.79 MB | +| Steady | MiniExcel.Rust | 3,344.20 ms | 89,708 | 467.30 ms | n/a | 4.20 MB | Results are machine-specific. Run `pwsh ./scripts/compare-nuget-v1-rust.ps1` on representative workbooks; see the [benchmark methodology](docs/dotnet-v1-query-benchmark.md). diff --git a/README.zh-CN.md b/README.zh-CN.md index 607ba2c..88d139e 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -112,13 +112,18 @@ MiniExcel::save_as("book.xlsx", &[row])?; ### 最新 NuGet 结果 -最新 Windows x64 测试使用 100,000 行 x 10 列工作簿,对比公开的 `MiniExcel 1.46.0` 与 -`MiniExcel.Rust 0.1.0-preview.1`;每个 runtime、每个场景运行 5 个独立进程。计时前已逐行、逐列、逐值验证一致。 - -| 场景 | MiniExcel | MiniExcel.Rust | Rust 加速 | 分配内存降低 | 工作集降低 | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2,052.91 ms | 1,278.59 ms | 1.61x | 88.6% | 4.6% | -| Steady | 4,635.98 ms | 3,380.16 ms | 1.37x | 88.6% | 10.1% | +最新 Windows x64 测试使用 100,000 行 x 10 列工作簿,对比公开的 `MiniExcel 1.46.0`、 +`.NET` 包装器 `MiniExcel.Rust 0.1.0-preview.1` 与原生 `MiniExcel Rust 0.4.0`;每个 runtime、 +每个场景运行 5 个独立进程。计时前已逐行、逐列、逐值验证一致。 + +| 场景 | Runtime | 耗时中位数 | 行/秒 | 首行延迟 | 托管分配 | 峰值工作集 | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2,598.50 ms | 38,484 | 1,051.97 ms | 2,541.93 MB | 50.93 MB | +| Cold | MiniExcel.Rust (.NET) | 1,899.42 ms | 52,648 | 450.85 ms | 289.36 MB | 49.64 MB | +| Cold | MiniExcel.Rust | 1,220.82 ms | 81,912 | 537.17 ms | 不适用 | 4.14 MB | +| Steady | MiniExcel | 5,997.76 ms | 50,019 | 616.40 ms | 7,625.15 MB | 52.80 MB | +| Steady | MiniExcel.Rust (.NET) | 4,202.10 ms | 71,393 | 442.59 ms | 868.05 MB | 48.79 MB | +| Steady | MiniExcel.Rust | 3,344.20 ms | 89,708 | 467.30 ms | 不适用 | 4.20 MB | 结果与机器和工作簿相关。请用 `pwsh ./scripts/compare-nuget-v1-rust.ps1` 在代表性工作簿上复测; 详见[测试方法](docs/dotnet-v1-query-benchmark.zh-CN.md)。 diff --git a/README.zh-TW.md b/README.zh-TW.md index 0765e44..e003492 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -112,13 +112,18 @@ MiniExcel::save_as("book.xlsx", &[row])?; ### 最新 NuGet 結果 -最新 Windows x64 測試使用 100,000 列 x 10 欄 workbook,比較公開的 `MiniExcel 1.46.0` 與 -`MiniExcel.Rust 0.1.0-preview.1`;每個 runtime、每個場景執行 5 個獨立 process。計時前已逐列、逐欄、逐值驗證一致。 - -| 場景 | MiniExcel | MiniExcel.Rust | Rust 加速 | 配置記憶體降低 | Working set 降低 | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2,052.91 ms | 1,278.59 ms | 1.61x | 88.6% | 4.6% | -| Steady | 4,635.98 ms | 3,380.16 ms | 1.37x | 88.6% | 10.1% | +最新 Windows x64 測試使用 100,000 列 x 10 欄 workbook,比較公開的 `MiniExcel 1.46.0`、 +`.NET` 包裝器 `MiniExcel.Rust 0.1.0-preview.1` 與原生 `MiniExcel Rust 0.4.0`;每個 runtime、 +每個場景執行 5 個獨立 process。計時前已逐列、逐欄、逐值驗證一致。 + +| 場景 | Runtime | 耗時中位數 | 列/秒 | 首列延遲 | Managed allocation | Peak working set | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2,598.50 ms | 38,484 | 1,051.97 ms | 2,541.93 MB | 50.93 MB | +| Cold | MiniExcel.Rust (.NET) | 1,899.42 ms | 52,648 | 450.85 ms | 289.36 MB | 49.64 MB | +| Cold | MiniExcel.Rust | 1,220.82 ms | 81,912 | 537.17 ms | 不適用 | 4.14 MB | +| Steady | MiniExcel | 5,997.76 ms | 50,019 | 616.40 ms | 7,625.15 MB | 52.80 MB | +| Steady | MiniExcel.Rust (.NET) | 4,202.10 ms | 71,393 | 442.59 ms | 868.05 MB | 48.79 MB | +| Steady | MiniExcel.Rust | 3,344.20 ms | 89,708 | 467.30 ms | 不適用 | 4.20 MB | 結果會受機器與 workbook 影響。請用 `pwsh ./scripts/compare-nuget-v1-rust.ps1` 在代表性 workbook 上重測; 詳見[測試方法](docs/dotnet-v1-query-benchmark.md)。 diff --git a/benchmarks/nuget-v1-query/Program.cs b/benchmarks/nuget-v1-query/Program.cs index 21402bf..1aacfb5 100644 --- a/benchmarks/nuget-v1-query/Program.cs +++ b/benchmarks/nuget-v1-query/Program.cs @@ -15,7 +15,7 @@ "generate" => Generate(args), "verify" => Verify(args), "managed" => Benchmark(args, useRust: false), - "rust" => Benchmark(args, useRust: true), + "rust-dotnet" => Benchmark(args, useRust: true), _ => Usage() }; @@ -90,7 +90,7 @@ static int Benchmark(string[] arguments, bool useRust) stopwatch.Stop(); Console.WriteLine(JsonSerializer.Serialize(new BenchmarkResult( - useRust ? "MiniExcel.Rust" : "MiniExcel", + useRust ? "MiniExcel.Rust (.NET)" : "MiniExcel", Environment.Version.ToString(), measuredPasses, rows, @@ -222,7 +222,7 @@ static int Usage() Console.Error.WriteLine("Usage:"); Console.Error.WriteLine(" NuGetV1Query generate "); Console.Error.WriteLine(" NuGetV1Query verify "); - Console.Error.WriteLine(" NuGetV1Query [passes] [warmup-passes]"); + Console.Error.WriteLine(" NuGetV1Query [passes] [warmup-passes]"); return 2; } diff --git a/docs/dotnet-v1-query-benchmark.md b/docs/dotnet-v1-query-benchmark.md index 38a91a6..0292562 100644 --- a/docs/dotnet-v1-query-benchmark.md +++ b/docs/dotnet-v1-query-benchmark.md @@ -20,10 +20,10 @@ pwsh ./scripts/compare-nuget-v1-rust.ps1 ``` This harness builds a local `MiniExcel.Rust` package, resolves the latest stable public MiniExcel v1 -package, restores both into an isolated `net8.0` consumer, and compares `MiniExcel.Query` with -`MiniExcelRust.Query`. Before timing, it verifies every row, column, and normalized value. Cold and -steady scenarios run in alternating fresh processes and report query time, first-row latency, -managed allocation, peak working set, and peak private memory to +package, restores both into an isolated `net8.0` consumer, and compares `MiniExcel.Query`, +`MiniExcelRust.Query`, and native `MiniExcel::query`. Before timing, it verifies every row, column, +and normalized value across all three paths. Cold and steady scenarios run in rotating fresh +processes and report query time, first-row latency, managed allocation where applicable, peak working set, and peak private memory to `target/benchmarks/nuget-v1/benchmark-.{json,md}`. Use `-Rows`, `-Columns`, `-Iterations`, `-Passes`, and `-WarmupPasses` to change the load. The @@ -33,12 +33,17 @@ Arm64. Pass `-MiniExcelVersion 1.46.0` to pin a historical baseline for reproduc #### Latest NuGet Result The 2026-09-10 Windows x64 run used 100,000 rows x 10 columns and five fresh processes per runtime -and scenario. `MiniExcel 1.46.0` and `MiniExcel.Rust 0.1.0-preview.1` matched every normalized value. +and scenario. `MiniExcel 1.46.0`, `MiniExcel.Rust 0.1.0-preview.1` through .NET, and native +`MiniExcel Rust 0.4.0` matched every normalized value. -| Scenario | MiniExcel | MiniExcel.Rust | Rust speedup | Allocation reduction | Working-set reduction | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2,052.91 ms | 1,278.59 ms | 1.61x | 88.6% | 4.6% | -| Steady | 4,635.98 ms | 3,380.16 ms | 1.37x | 88.6% | 10.1% | +| Scenario | Runtime | Median elapsed | Rows/s | First row | Managed allocation | Peak working set | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2,598.50 ms | 38,484 | 1,051.97 ms | 2,541.93 MB | 50.93 MB | +| Cold | MiniExcel.Rust (.NET) | 1,899.42 ms | 52,648 | 450.85 ms | 289.36 MB | 49.64 MB | +| Cold | MiniExcel.Rust | 1,220.82 ms | 81,912 | 537.17 ms | n/a | 4.14 MB | +| Steady | MiniExcel | 5,997.76 ms | 50,019 | 616.40 ms | 7,625.15 MB | 52.80 MB | +| Steady | MiniExcel.Rust (.NET) | 4,202.10 ms | 71,393 | 442.59 ms | 868.05 MB | 48.79 MB | +| Steady | MiniExcel.Rust | 3,344.20 ms | 89,708 | 467.30 ms | n/a | 4.20 MB | ## Fairness Controls diff --git a/docs/dotnet-v1-query-benchmark.zh-CN.md b/docs/dotnet-v1-query-benchmark.zh-CN.md index 03bd6a5..52d5a54 100644 --- a/docs/dotnet-v1-query-benchmark.zh-CN.md +++ b/docs/dotnet-v1-query-benchmark.zh-CN.md @@ -20,9 +20,10 @@ pwsh ./scripts/compare-nuget-v1-rust.ps1 ``` 该脚本先构建本地 `MiniExcel.Rust` 包,自动解析 NuGet 上最新的稳定 MiniExcel v1,再创建隔离的 -`net8.0` 消费者并对比 `MiniExcel.Query` 与 `MiniExcelRust.Query`。计时前会逐行、逐列、 -逐值验证结果;Cold 与 Steady 场景使用交替的新进程执行,并将 Query 耗时、首行延迟、托管分配、 -峰值工作集与峰值私有内存写入 `target/benchmarks/nuget-v1/benchmark-.{json,md}`。 +`net8.0` 消费者,并对比 `MiniExcel.Query`、`MiniExcelRust.Query` 与原生 `MiniExcel::query`。 +计时前会在三条路径间逐行、逐列、逐值验证结果;Cold 与 Steady 场景使用轮换的新进程执行, +并将 Query 耗时、首行延迟、适用时的托管分配、峰值工作集与峰值私有内存写入 +`target/benchmarks/nuget-v1/benchmark-.{json,md}`。 可通过 `-Rows`、`-Columns`、`-Iterations`、`-Passes` 和 `-WarmupPasses` 调整压力。 GitHub 的 `NuGet Benchmark` workflow 会在 Windows、Linux、macOS 的 x64 与 Arm64 环境运行同一套测试。 @@ -31,12 +32,17 @@ GitHub 的 `NuGet Benchmark` workflow 会在 Windows、Linux、macOS 的 x64 与 #### 最新 NuGet 结果 2026-09-10 的 Windows x64 测试使用 100,000 行 x 10 列工作簿,每个 runtime、每个场景运行 -5 个独立进程。`MiniExcel 1.46.0` 与 `MiniExcel.Rust 0.1.0-preview.1` 的所有规范化值完全一致。 +5 个独立进程。`MiniExcel 1.46.0`、通过 .NET 调用的 `MiniExcel.Rust 0.1.0-preview.1` 与原生 +`MiniExcel Rust 0.4.0` 的所有规范化值完全一致。 -| 场景 | MiniExcel | MiniExcel.Rust | Rust 加速 | 分配内存降低 | 工作集降低 | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2,052.91 ms | 1,278.59 ms | 1.61x | 88.6% | 4.6% | -| Steady | 4,635.98 ms | 3,380.16 ms | 1.37x | 88.6% | 10.1% | +| 场景 | Runtime | 耗时中位数 | 行/秒 | 首行延迟 | 托管分配 | 峰值工作集 | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2,598.50 ms | 38,484 | 1,051.97 ms | 2,541.93 MB | 50.93 MB | +| Cold | MiniExcel.Rust (.NET) | 1,899.42 ms | 52,648 | 450.85 ms | 289.36 MB | 49.64 MB | +| Cold | MiniExcel.Rust | 1,220.82 ms | 81,912 | 537.17 ms | 不适用 | 4.14 MB | +| Steady | MiniExcel | 5,997.76 ms | 50,019 | 616.40 ms | 7,625.15 MB | 52.80 MB | +| Steady | MiniExcel.Rust (.NET) | 4,202.10 ms | 71,393 | 442.59 ms | 868.05 MB | 48.79 MB | +| Steady | MiniExcel.Rust | 3,344.20 ms | 89,708 | 467.30 ms | 不适用 | 4.20 MB | ## 公平性控制 diff --git a/docs/i18n/README.es.md b/docs/i18n/README.es.md index c9eec33..a63f448 100644 --- a/docs/i18n/README.es.md +++ b/docs/i18n/README.es.md @@ -292,14 +292,18 @@ Consulta la [matriz de compatibilidad](../compatibility.md), el [contrato de an ### Últimos Resultados NuGet -La última prueba Windows x64 comparó `MiniExcel 1.46.0` con `MiniExcel.Rust 0.1.0-preview.1` -sobre 100.000 filas x 10 columnas, usando cinco procesos nuevos por runtime y escenario. Antes de -medir se verificaron todas las filas, columnas y valores normalizados. - -| Escenario | MiniExcel | MiniExcel.Rust | Aceleración Rust | Reducción asignaciones | Reducción working set | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2.052,91 ms | 1.278,59 ms | 1,61x | 88,6 % | 4,6 % | -| Steady | 4.635,98 ms | 3.380,16 ms | 1,37x | 88,6 % | 10,1 % | +La última prueba Windows x64 comparó `MiniExcel 1.46.0`, el wrapper .NET +`MiniExcel.Rust 0.1.0-preview.1` y `MiniExcel Rust 0.4.0` nativo sobre 100.000 filas x 10 columnas, +con cinco procesos nuevos por runtime y escenario. Todos los valores se verificaron antes de medir. + +| Escenario | Runtime | Mediana | Filas/s | Primera fila | Asignación administrada | Peak working set | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2.598,50 ms | 38.484 | 1.051,97 ms | 2.541,93 MB | 50,93 MB | +| Cold | MiniExcel.Rust (.NET) | 1.899,42 ms | 52.648 | 450,85 ms | 289,36 MB | 49,64 MB | +| Cold | MiniExcel.Rust | 1.220,82 ms | 81.912 | 537,17 ms | n/a | 4,14 MB | +| Steady | MiniExcel | 5.997,76 ms | 50.019 | 616,40 ms | 7.625,15 MB | 52,80 MB | +| Steady | MiniExcel.Rust (.NET) | 4.202,10 ms | 71.393 | 442,59 ms | 868,05 MB | 48,79 MB | +| Steady | MiniExcel.Rust | 3.344,20 ms | 89.708 | 467,30 ms | n/a | 4,20 MB | Los resultados dependen de la máquina. Ejecuta `pwsh ./scripts/compare-nuget-v1-rust.ps1` con workbooks representativos; consulta la [metodología](../dotnet-v1-query-benchmark.md). diff --git a/docs/i18n/README.fr.md b/docs/i18n/README.fr.md index 09c7a77..46070da 100644 --- a/docs/i18n/README.fr.md +++ b/docs/i18n/README.fr.md @@ -293,14 +293,18 @@ Voir la [matrice de compatibilité](../compatibility.md), le [contrat analyse/RA ### Derniers Résultats NuGet -Le dernier test Windows x64 compare `MiniExcel 1.46.0` et `MiniExcel.Rust 0.1.0-preview.1` -sur 100 000 lignes x 10 colonnes, avec cinq processus neufs par runtime et scénario. Toutes les -lignes, colonnes et valeurs normalisées sont vérifiées avant la mesure. - -| Scénario | MiniExcel | MiniExcel.Rust | Accélération Rust | Réduction allocations | Réduction working set | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2 052,91 ms | 1 278,59 ms | 1,61x | 88,6 % | 4,6 % | -| Steady | 4 635,98 ms | 3 380,16 ms | 1,37x | 88,6 % | 10,1 % | +Le dernier test Windows x64 compare `MiniExcel 1.46.0`, le wrapper .NET +`MiniExcel.Rust 0.1.0-preview.1` et `MiniExcel Rust 0.4.0` natif sur 100 000 lignes x 10 colonnes, +avec cinq processus neufs par runtime et scénario. Toutes les valeurs sont vérifiées avant la mesure. + +| Scénario | Runtime | Temps médian | Lignes/s | Première ligne | Allocation managée | Working set maximal | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2 598,50 ms | 38 484 | 1 051,97 ms | 2 541,93 MB | 50,93 MB | +| Cold | MiniExcel.Rust (.NET) | 1 899,42 ms | 52 648 | 450,85 ms | 289,36 MB | 49,64 MB | +| Cold | MiniExcel.Rust | 1 220,82 ms | 81 912 | 537,17 ms | n/a | 4,14 MB | +| Steady | MiniExcel | 5 997,76 ms | 50 019 | 616,40 ms | 7 625,15 MB | 52,80 MB | +| Steady | MiniExcel.Rust (.NET) | 4 202,10 ms | 71 393 | 442,59 ms | 868,05 MB | 48,79 MB | +| Steady | MiniExcel.Rust | 3 344,20 ms | 89 708 | 467,30 ms | n/a | 4,20 MB | Les résultats dépendent de la machine. Relancez `pwsh ./scripts/compare-nuget-v1-rust.ps1` sur des workbooks représentatifs ; voir la [méthodologie](../dotnet-v1-query-benchmark.md). diff --git a/docs/i18n/README.ja.md b/docs/i18n/README.ja.md index 53e0515..43940da 100644 --- a/docs/i18n/README.ja.md +++ b/docs/i18n/README.ja.md @@ -288,14 +288,18 @@ Workbook editing、Template、formula、formatting に依存する前に、 ### 最新 NuGet 結果 -最新の Windows x64 テストでは、100,000 行 x 10 列の workbook で `MiniExcel 1.46.0` と -`MiniExcel.Rust 0.1.0-preview.1` を比較し、runtime とシナリオごとに 5 個の新規 process を使用しました。 -計測前に全行・全列・正規化した値の一致を確認しています。 - -| シナリオ | MiniExcel | MiniExcel.Rust | Rust 高速化 | Allocation 削減 | Working set 削減 | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2,052.91 ms | 1,278.59 ms | 1.61x | 88.6% | 4.6% | -| Steady | 4,635.98 ms | 3,380.16 ms | 1.37x | 88.6% | 10.1% | +最新の Windows x64 テストでは、100,000 行 x 10 列の workbook で `MiniExcel 1.46.0`、 +.NET wrapper `MiniExcel.Rust 0.1.0-preview.1`、native `MiniExcel Rust 0.4.0` を比較しました。 +runtime とシナリオごとに 5 個の新規 process を使用し、計測前にすべての値を検証しています。 + +| シナリオ | Runtime | 中央値 | 行/秒 | 最初の行 | Managed allocation | Peak working set | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2,598.50 ms | 38,484 | 1,051.97 ms | 2,541.93 MB | 50.93 MB | +| Cold | MiniExcel.Rust (.NET) | 1,899.42 ms | 52,648 | 450.85 ms | 289.36 MB | 49.64 MB | +| Cold | MiniExcel.Rust | 1,220.82 ms | 81,912 | 537.17 ms | n/a | 4.14 MB | +| Steady | MiniExcel | 5,997.76 ms | 50,019 | 616.40 ms | 7,625.15 MB | 52.80 MB | +| Steady | MiniExcel.Rust (.NET) | 4,202.10 ms | 71,393 | 442.59 ms | 868.05 MB | 48.79 MB | +| Steady | MiniExcel.Rust | 3,344.20 ms | 89,708 | 467.30 ms | n/a | 4.20 MB | 結果はマシンに依存します。代表的な workbook で `pwsh ./scripts/compare-nuget-v1-rust.ps1` を実行し、 [測定方法](../dotnet-v1-query-benchmark.md)を参照してください。 diff --git a/docs/i18n/README.zh-CN.md b/docs/i18n/README.zh-CN.md index 8aaf335..5147687 100644 --- a/docs/i18n/README.zh-CN.md +++ b/docs/i18n/README.zh-CN.md @@ -282,13 +282,18 @@ cargo run -p miniexcel-cli -- rag-export book.xlsx --header --chunk-rows 25 --fo ### 最新 NuGet 结果 -最新 Windows x64 测试使用 100,000 行 x 10 列工作簿,对比公开的 `MiniExcel 1.46.0` 与 -`MiniExcel.Rust 0.1.0-preview.1`;每个 runtime、每个场景运行 5 个独立进程。计时前已逐行、逐列、逐值验证一致。 - -| 场景 | MiniExcel | MiniExcel.Rust | Rust 加速 | 分配内存降低 | 工作集降低 | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2,052.91 ms | 1,278.59 ms | 1.61x | 88.6% | 4.6% | -| Steady | 4,635.98 ms | 3,380.16 ms | 1.37x | 88.6% | 10.1% | +最新 Windows x64 测试使用 100,000 行 x 10 列工作簿,对比公开的 `MiniExcel 1.46.0`、 +`.NET` 包装器 `MiniExcel.Rust 0.1.0-preview.1` 与原生 `MiniExcel Rust 0.4.0`;每个 runtime、 +每个场景运行 5 个独立进程。计时前已逐行、逐列、逐值验证一致。 + +| 场景 | Runtime | 耗时中位数 | 行/秒 | 首行延迟 | 托管分配 | 峰值工作集 | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2,598.50 ms | 38,484 | 1,051.97 ms | 2,541.93 MB | 50.93 MB | +| Cold | MiniExcel.Rust (.NET) | 1,899.42 ms | 52,648 | 450.85 ms | 289.36 MB | 49.64 MB | +| Cold | MiniExcel.Rust | 1,220.82 ms | 81,912 | 537.17 ms | 不适用 | 4.14 MB | +| Steady | MiniExcel | 5,997.76 ms | 50,019 | 616.40 ms | 7,625.15 MB | 52.80 MB | +| Steady | MiniExcel.Rust (.NET) | 4,202.10 ms | 71,393 | 442.59 ms | 868.05 MB | 48.79 MB | +| Steady | MiniExcel.Rust | 3,344.20 ms | 89,708 | 467.30 ms | 不适用 | 4.20 MB | 结果与机器和工作簿相关。请用 `pwsh ./scripts/compare-nuget-v1-rust.ps1` 在代表性工作簿上复测; 详见[测试方法](../dotnet-v1-query-benchmark.zh-CN.md)。 diff --git a/docs/i18n/README.zh-TW.md b/docs/i18n/README.zh-TW.md index 3b28249..c7cda70 100644 --- a/docs/i18n/README.zh-TW.md +++ b/docs/i18n/README.zh-TW.md @@ -282,13 +282,18 @@ Template、公式或格式功能前,請先在[相容性矩陣](../compatibilit ### 最新 NuGet 結果 -最新 Windows x64 測試使用 100,000 列 x 10 欄 workbook,比較公開的 `MiniExcel 1.46.0` 與 -`MiniExcel.Rust 0.1.0-preview.1`;每個 runtime、每個場景執行 5 個獨立 process。計時前已逐列、逐欄、逐值驗證一致。 - -| 場景 | MiniExcel | MiniExcel.Rust | Rust 加速 | 配置記憶體降低 | Working set 降低 | -| --- | ---: | ---: | ---: | ---: | ---: | -| Cold | 2,052.91 ms | 1,278.59 ms | 1.61x | 88.6% | 4.6% | -| Steady | 4,635.98 ms | 3,380.16 ms | 1.37x | 88.6% | 10.1% | +最新 Windows x64 測試使用 100,000 列 x 10 欄 workbook,比較公開的 `MiniExcel 1.46.0`、 +`.NET` 包裝器 `MiniExcel.Rust 0.1.0-preview.1` 與原生 `MiniExcel Rust 0.4.0`;每個 runtime、 +每個場景執行 5 個獨立 process。計時前已逐列、逐欄、逐值驗證一致。 + +| 場景 | Runtime | 耗時中位數 | 列/秒 | 首列延遲 | Managed allocation | Peak working set | +| --- | --- | ---: | ---: | ---: | ---: | ---: | +| Cold | MiniExcel | 2,598.50 ms | 38,484 | 1,051.97 ms | 2,541.93 MB | 50.93 MB | +| Cold | MiniExcel.Rust (.NET) | 1,899.42 ms | 52,648 | 450.85 ms | 289.36 MB | 49.64 MB | +| Cold | MiniExcel.Rust | 1,220.82 ms | 81,912 | 537.17 ms | 不適用 | 4.14 MB | +| Steady | MiniExcel | 5,997.76 ms | 50,019 | 616.40 ms | 7,625.15 MB | 52.80 MB | +| Steady | MiniExcel.Rust (.NET) | 4,202.10 ms | 71,393 | 442.59 ms | 868.05 MB | 48.79 MB | +| Steady | MiniExcel.Rust | 3,344.20 ms | 89,708 | 467.30 ms | 不適用 | 4.20 MB | 結果會受機器與 workbook 影響。請用 `pwsh ./scripts/compare-nuget-v1-rust.ps1` 在代表性 workbook 上重測; 詳見[測試方法](../dotnet-v1-query-benchmark.md)。 diff --git a/miniexcel/examples/stress_query.rs b/miniexcel/examples/stress_query.rs index ac3a698..3771da5 100644 --- a/miniexcel/examples/stress_query.rs +++ b/miniexcel/examples/stress_query.rs @@ -4,15 +4,22 @@ use std::io; use std::path::{Path, PathBuf}; use std::time::Instant; -use miniexcel::MiniExcel; +use miniexcel::{CellValue, MiniExcel}; use serde::Serialize; +use sha2::{Digest, Sha256}; #[derive(Serialize)] #[serde(rename_all = "PascalCase")] struct QueryResult { + runtime: &'static str, + dot_net_runtime: Option, + passes: usize, rows: u64, cells: u64, - query_elapsed_ms: f64, + content_hash: String, + elapsed_milliseconds: f64, + first_row_milliseconds: f64, + allocated_bytes: Option, } fn main() -> Result<(), Box> { @@ -36,24 +43,68 @@ fn main() -> Result<(), Box> { .into()); } - run_query(&path, warmup_passes)?; + run_query(&path, warmup_passes, None)?; let started = Instant::now(); - let (rows, cells) = run_query(&path, measured_passes)?; - let result = - QueryResult { rows, cells, query_elapsed_ms: started.elapsed().as_secs_f64() * 1000.0 }; + let (rows, cells, content_hash, first_row_milliseconds) = + run_query(&path, measured_passes, Some(started))?; + let result = QueryResult { + runtime: "MiniExcel.Rust", + dot_net_runtime: None, + passes: measured_passes, + rows, + cells, + content_hash, + elapsed_milliseconds: started.elapsed().as_secs_f64() * 1000.0, + first_row_milliseconds, + allocated_bytes: None, + }; println!("{}", serde_json::to_string(&result)?); Ok(()) } -fn run_query(path: &Path, passes: usize) -> Result<(u64, u64), Box> { +fn run_query( + path: &Path, + passes: usize, + started: Option, +) -> Result<(u64, u64, String, f64), Box> { let mut rows = 0_u64; let mut cells = 0_u64; + let mut first_row_milliseconds = 0.0; + let mut hasher = Sha256::new(); for _ in 0..passes { for row in MiniExcel::query(path)? { let row = row?; + if rows == 0 { + first_row_milliseconds = + started.map_or(0.0, |value| value.elapsed().as_secs_f64() * 1000.0); + } rows += 1; cells += row.len() as u64; + for (name, value) in &row { + append_text(&mut hasher, name); + append_text(&mut hasher, &normalize(value)); + } } } - Ok((rows, cells)) + Ok((rows, cells, format!("{:X}", hasher.finalize()), first_row_milliseconds)) +} + +fn append_text(hasher: &mut Sha256, value: &str) { + let bytes = value.as_bytes(); + hasher.update((bytes.len() as i32).to_le_bytes()); + hasher.update(bytes); +} + +fn normalize(value: &CellValue) -> String { + match value { + CellValue::Empty => "null".to_owned(), + CellValue::Bool(value) => if *value { "True" } else { "False" }.to_owned(), + CellValue::Int(value) => value.to_string(), + CellValue::Float(value) => value.to_string(), + CellValue::String(value) | CellValue::Error(value) => value.clone(), + CellValue::Date(value) => value.to_string(), + CellValue::Time(value) => value.to_string(), + CellValue::DateTime(value) => value.to_string(), + CellValue::Duration(value) => value.to_string(), + } } diff --git a/scripts/compare-nuget-v1-rust.ps1 b/scripts/compare-nuget-v1-rust.ps1 index 4bccbd3..4deb1a8 100644 --- a/scripts/compare-nuget-v1-rust.ps1 +++ b/scripts/compare-nuget-v1-rust.ps1 @@ -38,6 +38,8 @@ $restoreDirectory = Join-Path $repositoryRoot 'target/nuget/benchmark-restore' $restoreConfig = Join-Path $restoreDirectory 'nuget.config' $project = Join-Path $repositoryRoot 'benchmarks/nuget-v1-query/NuGetV1Query.csproj' $runner = Join-Path $repositoryRoot 'benchmarks/nuget-v1-query/bin/Release/net8.0/NuGetV1Query.dll' +$nativeSuffix = if ($env:OS -eq 'Windows_NT') { '.exe' } else { '' } +$nativeRunner = Join-Path $repositoryRoot "target/release/examples/stress_query$nativeSuffix" if ([string]::IsNullOrWhiteSpace($OutputDirectory)) { $OutputDirectory = Join-Path $repositoryRoot 'target/benchmarks/nuget-v1' } @@ -93,6 +95,8 @@ if ($LASTEXITCODE -ne 0) { throw 'Benchmark restore failed.' } -p:MiniExcelVersion=$MiniExcelVersion ` -p:MiniExcelRustPackageVersion=$MiniExcelRustVersion if ($LASTEXITCODE -ne 0) { throw 'Benchmark build failed.' } +& cargo +1.85.0 build --release -p miniexcel --example stress_query --locked +if ($LASTEXITCODE -ne 0) { throw 'Native Rust benchmark build failed.' } & dotnet $runner generate $workbook $Rows $Columns if ($LASTEXITCODE -ne 0) { throw 'Benchmark workbook generation failed.' } @@ -107,17 +111,16 @@ function Invoke-MeasuredProcess { ) $startInfo = [Diagnostics.ProcessStartInfo]::new() - $startInfo.FileName = 'dotnet' + $startInfo.FileName = if ($Runtime -eq 'rust-native') { $nativeRunner } else { 'dotnet' } $startInfo.UseShellExecute = $false $startInfo.RedirectStandardOutput = $true $startInfo.RedirectStandardError = $true - foreach ($argument in @( - $runner, - $Runtime, - $workbook, - "$($BenchmarkScenario.Passes)", - "$($BenchmarkScenario.WarmupPasses)" - )) { + $arguments = if ($Runtime -eq 'rust-native') { + @($workbook, "$($BenchmarkScenario.Passes)", "$($BenchmarkScenario.WarmupPasses)") + } else { + @($runner, $Runtime, $workbook, "$($BenchmarkScenario.Passes)", "$($BenchmarkScenario.WarmupPasses)") + } + foreach ($argument in $arguments) { $startInfo.ArgumentList.Add($argument) } @@ -147,7 +150,11 @@ function Invoke-MeasuredProcess { ContentHash = $measurement.ContentHash ElapsedMs = [Math]::Round($measurement.ElapsedMilliseconds, 2) FirstRowMs = [Math]::Round($measurement.FirstRowMilliseconds, 2) - AllocatedMB = [Math]::Round($measurement.AllocatedBytes / 1MB, 2) + AllocatedMB = if ($null -eq $measurement.AllocatedBytes) { + $null + } else { + [Math]::Round($measurement.AllocatedBytes / 1MB, 2) + } ProcessElapsedMs = [Math]::Round($processStopwatch.Elapsed.TotalMilliseconds, 2) PeakWorkingSetMB = [Math]::Round($peakWorkingSet / 1MB, 2) PeakPrivateMB = [Math]::Round($peakPrivateBytes / 1MB, 2) @@ -173,17 +180,21 @@ if ($Scenario -in @('Steady', 'Both')) { $scenarios += [pscustomobject]@{ Name = 'Steady'; Passes = $Passes; WarmupPasses = $WarmupPasses } } -foreach ($runtime in @('managed', 'rust')) { +foreach ($runtime in @('managed', 'rust-dotnet', 'rust-native')) { $null = Invoke-MeasuredProcess -Runtime $runtime ` -BenchmarkScenario ([pscustomobject]@{ Name = 'Preflight'; Passes = 1; WarmupPasses = 0 }) ` -Iteration 0 } $results = [Collections.Generic.List[object]]::new() +$runtimeKeys = @('managed', 'rust-dotnet', 'rust-native') for ($scenarioIndex = 0; $scenarioIndex -lt $scenarios.Count; $scenarioIndex++) { $benchmarkScenario = $scenarios[$scenarioIndex] foreach ($iteration in 1..$Iterations) { - $order = if (($iteration + $scenarioIndex) % 2 -eq 1) { @('managed', 'rust') } else { @('rust', 'managed') } + $offset = ($iteration + $scenarioIndex - 1) % $runtimeKeys.Count + $order = @(0..($runtimeKeys.Count - 1) | ForEach-Object { + $runtimeKeys[($_ + $offset) % $runtimeKeys.Count] + }) foreach ($runtime in $order) { $results.Add((Invoke-MeasuredProcess -Runtime $runtime -BenchmarkScenario $benchmarkScenario -Iteration $iteration)) } @@ -195,21 +206,26 @@ foreach ($benchmarkScenario in $scenarios) { if (($scenarioResults.Rows | Select-Object -Unique).Count -ne 1 -or ($scenarioResults.Cells | Select-Object -Unique).Count -ne 1 -or ($scenarioResults.ContentHash | Select-Object -Unique).Count -ne 1) { - throw "$($benchmarkScenario.Name): MiniExcel and MiniExcel.Rust returned different results." + throw "$($benchmarkScenario.Name): the three runners returned different results." } } $summary = foreach ($benchmarkScenario in $scenarios) { - foreach ($runtime in @('MiniExcel', 'MiniExcel.Rust')) { + foreach ($runtime in @('MiniExcel', 'MiniExcel.Rust (.NET)', 'MiniExcel.Rust')) { $group = @($results | Where-Object { $_.Scenario -eq $benchmarkScenario.Name -and $_.Runtime -eq $runtime }) $medianElapsed = Get-Median ([double[]]$group.ElapsedMs) + $allocatedValues = @($group.AllocatedMB | Where-Object { $null -ne $_ }) [pscustomobject]@{ Scenario = $benchmarkScenario.Name Runtime = $runtime MedianElapsedMs = [Math]::Round($medianElapsed, 2) RowsPerSecond = [Math]::Round($group[0].Rows / ($medianElapsed / 1000), 0) MedianFirstRowMs = [Math]::Round((Get-Median ([double[]]$group.FirstRowMs)), 2) - MedianAllocatedMB = [Math]::Round((Get-Median ([double[]]$group.AllocatedMB)), 2) + MedianAllocatedMB = if ($allocatedValues.Count -eq 0) { + $null + } else { + [Math]::Round((Get-Median ([double[]]$allocatedValues)), 2) + } MedianPeakWorkingSetMB = [Math]::Round((Get-Median ([double[]]$group.PeakWorkingSetMB)), 2) MedianPeakPrivateMB = [Math]::Round((Get-Median ([double[]]$group.PeakPrivateMB)), 2) } @@ -218,12 +234,19 @@ $summary = foreach ($benchmarkScenario in $scenarios) { $comparison = foreach ($benchmarkScenario in $scenarios) { $managed = $summary | Where-Object { $_.Scenario -eq $benchmarkScenario.Name -and $_.Runtime -eq 'MiniExcel' } - $rust = $summary | Where-Object { $_.Scenario -eq $benchmarkScenario.Name -and $_.Runtime -eq 'MiniExcel.Rust' } - [pscustomobject]@{ - Scenario = $benchmarkScenario.Name - RustSpeedup = [Math]::Round($managed.MedianElapsedMs / $rust.MedianElapsedMs, 2) - AllocationReductionPercent = [Math]::Round((1 - $rust.MedianAllocatedMB / $managed.MedianAllocatedMB) * 100, 1) - WorkingSetReductionPercent = [Math]::Round((1 - $rust.MedianPeakWorkingSetMB / $managed.MedianPeakWorkingSetMB) * 100, 1) + foreach ($runtime in @('MiniExcel.Rust (.NET)', 'MiniExcel.Rust')) { + $rust = $summary | Where-Object { $_.Scenario -eq $benchmarkScenario.Name -and $_.Runtime -eq $runtime } + [pscustomobject]@{ + Scenario = $benchmarkScenario.Name + Runtime = $runtime + RustSpeedup = [Math]::Round($managed.MedianElapsedMs / $rust.MedianElapsedMs, 2) + AllocationReductionPercent = if ($null -eq $rust.MedianAllocatedMB) { + $null + } else { + [Math]::Round((1 - $rust.MedianAllocatedMB / $managed.MedianAllocatedMB) * 100, 1) + } + WorkingSetReductionPercent = [Math]::Round((1 - $rust.MedianPeakWorkingSetMB / $managed.MedianPeakWorkingSetMB) * 100, 1) + } } } @@ -235,6 +258,8 @@ $report = [ordered]@{ DotNetSdk = (& dotnet --version).Trim() MiniExcelVersion = $MiniExcelVersion MiniExcelRustVersion = $MiniExcelRustVersion + MiniExcelRustNativeVersion = '0.4.0' + RustRevision = (& git -C $repositoryRoot rev-parse HEAD).Trim() Rows = $Rows Columns = $Columns WorkbookSha256 = (Get-FileHash $workbook -Algorithm SHA256).Hash.ToLowerInvariant() @@ -251,22 +276,25 @@ $results | Format-Table Scenario,Runtime,Iteration,ElapsedMs,FirstRowMs,Allocate $summary | Format-Table Scenario,Runtime,MedianElapsedMs,RowsPerSecond,MedianFirstRowMs,MedianAllocatedMB,MedianPeakWorkingSetMB -AutoSize $markdown = [Collections.Generic.List[string]]::new() -$markdown.Add("# MiniExcel v1 vs MiniExcel.Rust ($Rid)") +$markdown.Add("# MiniExcel v1 vs MiniExcel.Rust (.NET) vs MiniExcel.Rust ($Rid)") $markdown.Add('') $markdown.Add("- Date (UTC): $($report.TimestampUtc)") $markdown.Add("- MiniExcel: $MiniExcelVersion") $markdown.Add("- MiniExcel.Rust: $MiniExcelRustVersion") +$markdown.Add("- MiniExcel.Rust native: 0.4.0 ($($report.RustRevision))") $markdown.Add("- Workbook: $Rows rows x $Columns columns") $markdown.Add("- Iterations: $Iterations fresh processes per runtime and scenario") $markdown.Add('') $markdown.Add('| Scenario | Runtime | Median elapsed (ms) | Rows/s | First row (ms) | Allocated (MB) | Peak working set (MB) |') $markdown.Add('| --- | --- | ---: | ---: | ---: | ---: | ---: |') foreach ($item in $summary) { - $markdown.Add("| $($item.Scenario) | $($item.Runtime) | $($item.MedianElapsedMs) | $($item.RowsPerSecond) | $($item.MedianFirstRowMs) | $($item.MedianAllocatedMB) | $($item.MedianPeakWorkingSetMB) |") + $allocated = if ($null -eq $item.MedianAllocatedMB) { 'n/a' } else { $item.MedianAllocatedMB } + $markdown.Add("| $($item.Scenario) | $($item.Runtime) | $($item.MedianElapsedMs) | $($item.RowsPerSecond) | $($item.MedianFirstRowMs) | $allocated | $($item.MedianPeakWorkingSetMB) |") } $markdown.Add('') foreach ($item in $comparison) { - $markdown.Add("- $($item.Scenario): Rust speedup $($item.RustSpeedup)x; allocation reduction $($item.AllocationReductionPercent)%; working-set reduction $($item.WorkingSetReductionPercent)%.") + $allocation = if ($null -eq $item.AllocationReductionPercent) { 'n/a' } else { "$($item.AllocationReductionPercent)%" } + $markdown.Add("- $($item.Scenario), $($item.Runtime): speedup $($item.RustSpeedup)x; allocation reduction $allocation; working-set reduction $($item.WorkingSetReductionPercent)%.") } $markdown | Set-Content $markdownPath Write-Host "Report: $jsonPath"