Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 41 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,45 @@ cross-language context lane (Apache POI, Excelize, zavora-xlsx). Tier 4

## Results at a Glance

> Competitor snapshot: 2026-10-02 | WolfXL 2.0.5 (latest PyPI) | 17 fidelity adapters incl. SheetJS CE 0.20.3, ExcelJS 4.4.0, LibreOffice 26.8.0.3 | [Snapshot README](results-2026-10-02/README.md) | [Fidelity](results-2026-10-02/xlsx/README.md) | [Mutation](results-2026-10-02/mutation/README.md) | [Calc](results-2026-10-02/calc/README.md) | [Cross-language](results-2026-10-02/cross-language/README.md)
> Competitor snapshot: 2026-10-03 | WolfXL 2.0.8 (latest PyPI) | 17 fidelity adapters incl. SheetJS CE 0.20.3, ExcelJS 4.4.0, LibreOffice 26.8.0.3 | [Snapshot README](results-2026-10-03/README.md) | [Fidelity](results-2026-10-03/xlsx/README.md) | [Mutation](results-2026-10-03/mutation/README.md) | [Calc](results-2026-10-03/calc/README.md) | [Cross-language](results-2026-10-03/cross-language/README.md)
>
> Green features in that snapshot (read / write, out of 21¹): openpyxl
> 21/21 / 21/21, WolfXL 19/21 / 18/21, ExcelJS 12/21 / 15/21,
> aspose-cells-foss 11/21 / 14/21, LibreOffice 12/21 / 12/21, SheetJS CE
> 9/21 / 8/21; write-only xlsxwriter 15/21.
> 21/21 / 21/21 and WolfXL 21/21 / 21/21 (both pass all 133 tests in each
> mode), ExcelJS 12/21 / 15/21, aspose-cells-foss 11/21 / 14/21,
> LibreOffice 12/21 / 12/21, SheetJS CE 9/21 / 8/21; write-only
> xlsxwriter 15/21.
>
> Mutation suite (two-cell edit on a corporate template, content-model
> preservation; features kept / features in the template): WolfXL
> Preserved 17/17; openpyxl 16/17 (drops custom XML); LibreOffice 15/17
> (cell styles, page setup); aspose-cells-foss 15/17 (drops 87 string
> cells, custom XML); SheetJS 8/17 and zavora-xlsx 8/17 (charts, tables,
> styles and more); ExcelJS fails to load the template. Every engine that
> ran applied both edits. Wall times in that run are not comparable (host
> under heavy unrelated load).
> ran applied both edits.
>
> Calc tier (133-formula financial DAG, cache-free fixture, LibreOffice
> oracle): LibreOffice 133/133, aspose-cells-foss 25/133, WolfXL 0/133,
> zavora 0/133. SheetJS CE, ExcelJS, and the other non-calculating
> adapters are not applicable.
> oracle): LibreOffice 26.8.0.3, WolfXL Community 2.0.8 and WolfXL
> Commercial 2.3.0 133/133; aspose-cells-foss 25/133; zavora 0/133.
> Community's `save()` writes no calculated values (0/133 saved), while
> Commercial saves all 133. SheetJS CE, ExcelJS, and the other
> non-calculating adapters are not applicable.
>
> Cross-language context (write lane): Apache POI 5.5.1 18/21, Excelize
> 2.10.1 18/21, zavora-xlsx 0.1.2 8/21. The POI and Excelize misses are
> 2.11.0 18/21, zavora-xlsx 0.1.2 8/21. The POI and Excelize misses are
> the three Tier 4 features, which the ExcelBench adapters do not
> implement yet (`NotImplementedError`). They are not library gaps.
>
> ¹ `pivot_tables` is excluded from every denominator because no adapter
> produced a score for it in either competitor snapshot.
> produced a score for it in any competitor snapshot (2026-09-08,
> 2026-10-02, 2026-10-03).
>
> Previous competitor snapshot: [2026-10-02](results-2026-10-02/README.md) (WolfXL 2.0.5: 19/21 read, 18/21 write).
>
> Python release snapshot: 2026-04-29 UTC | wheel-backed WolfXL 2.0 rerun | [Fidelity](results-release-2026-04-28/README.md) | [Perf](results-release-2026-04-28/perf/README.md) | [Dashboard](results-release-2026-04-28/DASHBOARD.md)
>
> In that snapshot, WolfXL reaches `18/18` green features with `100%` pass rate.
>
> Performance snapshot: 2026-10-02 UTC | WolfXL 2.0.5 through its public API (`wolfxl.load_workbook`, `wolfxl.Workbook`) | [Perf](results-2026-10-02/perf/README.md). It supersedes the 2026-04-29 perf numbers for WolfXL, which measured private backend objects.
> Performance snapshot: 2026-10-03 UTC | WolfXL 2.0.8 through its public API (`wolfxl.load_workbook`, `wolfxl.Workbook`) | [Perf](results-2026-10-03/perf/README.md). It supersedes the 2026-10-02 perf snapshot (WolfXL 2.0.5) and the 2026-04-29 perf numbers for WolfXL, which measured private backend objects.
>
> Cross-language context snapshot: [Apache POI `18/18`](results-cross-language/README.md) | [Excelize `18/18`](results-cross-language/README.md)
>
Expand Down Expand Up @@ -97,26 +102,26 @@ Use this when the question is: which engine actually computes a 133-formula fina

| Library | Caps | Fidelity | Read Speed | Write Speed | Modify |
|---------|:----:|:--------:|:----------:|:-----------:|:------:|
| **wolfxl** | R+W | R 20/21, W 19/21 in 2026-09-08 competitor snapshot (18/18 in 2026-04-29 snapshot) | 1.69x (13.46 ms) | 1.20x (22.28 ms) | Patch (Preserved, 17/17 features in 2026-10-02 mutation suite) |
| openpyxl | R+W | R 21/21, W 21/21 in 2026-09-08 competitor snapshot | 1.00x (22.79 ms, baseline) | 1.00x (26.70 ms, baseline) | Rewrite (16/17 features in 2026-10-02 mutation suite) |
| aspose-cells-foss | R+W | R 11/21, W 14/21 in 2026-09-08 competitor snapshot | not measured | not measured | Rewrite (15/17 features) |
| xlsxwriter | W | 15/18 in 2026-04-29 release snapshot | -- | 0.86x (31.12 ms) | No |
| xlsxwriter-constmem | W | 12/18 in 2026-04-29 release snapshot | -- | 0.86x (31.17 ms) | No |
| python-calamine | R | 1/18 in 2026-04-29 release snapshot | 14.02x (1.63 ms) | -- | No |
| pandas | R+W | 3/18 in 2026-04-29 release snapshot | 0.80x (28.43 ms) | 1.00x (26.66 ms) | Rebuild |
| polars | R | 0/18 in 2026-04-29 release snapshot | 3.72x (6.13 ms) | -- | No |
| **wolfxl** | R+W | R 21/21, W 21/21 in 2026-10-03 competitor snapshot (18/18 in 2026-04-29 snapshot) | 1.73x (13.90 ms) | 2.45x (11.26 ms) | Patch (Preserved, 17/17 features in 2026-10-03 mutation suite) |
| openpyxl | R+W | R 21/21, W 21/21 in 2026-10-03 competitor snapshot | 1.00x (24.01 ms, baseline) | 1.00x (27.59 ms, baseline) | Rewrite (16/17 features in 2026-10-03 mutation suite) |
| aspose-cells-foss | R+W | R 11/21, W 14/21 in 2026-10-03 competitor snapshot | not measured | not measured | Rewrite (15/17 features) |
| xlsxwriter | W | 15/18 in 2026-04-29 release snapshot | -- | 0.82x (33.63 ms) | No |
| xlsxwriter-constmem | W | 12/18 in 2026-04-29 release snapshot | -- | 0.82x (33.69 ms) | No |
| python-calamine | R | 1/18 in 2026-04-29 release snapshot | 13.92x (1.73 ms) | -- | No |
| pandas | R+W | 3/18 in 2026-04-29 release snapshot | 0.82x (29.33 ms) | 0.97x (28.57 ms) | Rebuild |
| polars | R | 0/18 in 2026-04-29 release snapshot | 3.87x (6.21 ms) | -- | No |

> Speed columns come from the [2026-10-02 perf snapshot](results-2026-10-02/perf/README.md): sum of per-feature p50 wall times over the same 19 xlsx features for every library (warmup 3, 25 iterations, Apple M4 Pro, Python 3.12.3), shown as openpyxl's total divided by the library's total. WolfXL 2.0.5 is measured through its public API (`wolfxl.load_workbook`, `wolfxl.Workbook`), like every other library. aspose-cells-foss was not part of the perf run. Always cite the artifact date, workload, and profile. See [METHODOLOGY.md](METHODOLOGY.md) and [Public Reporting Status](docs/public-reporting.md).
> Speed columns come from the [2026-10-03 perf snapshot](results-2026-10-03/perf/README.md): sum of per-feature p50 wall times over the same 19 xlsx features for every library (warmup 3, 25 iterations, Apple M4 Pro, Python 3.12.3), shown as openpyxl's total divided by the library's total. WolfXL 2.0.8 is measured through its public API (`wolfxl.load_workbook`, `wolfxl.Workbook`), like every other library. python-calamine and polars read faster than WolfXL; openpyxl writes faster than xlsxwriter and pandas. aspose-cells-foss was not part of the perf run. Always cite the artifact date, workload, and profile. See [METHODOLOGY.md](METHODOLOGY.md) and [Public Reporting Status](docs/public-reporting.md).

### Key Findings
- **High-fidelity libraries are rare**: in the 2026-04-29 release snapshot, only openpyxl and WolfXL reached 18/18 green features; in the 2026-09-08 competitor snapshot (21 scored features; `pivot_tables` unscored), openpyxl holds 21/21 read and write while WolfXL 2.1.0 reaches 20/21 read and 19/21 write
- **WolfXL 2.1.0 regression signal**: conditional-formatting read scores 0 and named-range / print-title writes score 2 in the 2026-09-08 snapshot; tracked for the WolfXL repo
- **Patch modify is structurally different**: WolfXL's `load_workbook(path, modify=True)` uses surgical ZIP patching; it is the only engine whose output keeps every template feature in the 2026-10-02 mutation suite
- **High-fidelity libraries are rare**: in the 2026-04-29 release snapshot, only openpyxl and WolfXL reached 18/18 green features; in the 2026-10-03 competitor snapshot (21 scored features; `pivot_tables` unscored), openpyxl 3.1.5 and WolfXL 2.0.8 both hold 21/21 read and 21/21 write and pass all 133 tests in each mode. The next best are ExcelJS (15/21 write) and xlsxwriter (15/21 write)
- **WolfXL 2.1.0 regression signal (2026-09-08)**: conditional-formatting read scored 0 and named-range / print-title writes scored 2 in the 2026-09-08 snapshot; WolfXL 2.0.8 scores 3 on all three in the 2026-10-03 snapshot
- **Patch modify is structurally different**: WolfXL's `load_workbook(path, modify=True)` uses surgical ZIP patching; it is the only engine whose output keeps every template feature in the 2026-10-03 mutation suite
- **The abstraction tax is real**: pandas wraps openpyxl but drops from 16 to 3 green features due to DataFrame coercion (errors become NaN)
- **Speed vs fidelity tradeoff is measurable**: use the perf snapshot together with the fidelity matrix rather than quoting one without the other
- **Optimization modes have clear costs**: openpyxl-readonly loses 13 green features for streaming speed
- **Cross-language context is now strong too**: `Apache POI` and `Excelize` land at `18/18` in the scored write lane; `zavora-xlsx` 0.1.2 writes fast but corrupts hyperlink relationships on mutate and cannot recalculate
- **Calculation is a differentiator**: in the 2026-10-02 calc rerun, LibreOffice, WolfXL Community 2.0.7, and WolfXL Commercial 2.3.0 compute all 133 formulas; aspose-cells-foss covers 25/133 and zavora-xlsx 0/133. Community's `save()` writes no calculated values (0/133 saved), while Commercial saves all 133
- **Calculation is a differentiator**: in the 2026-10-03 calc lane, LibreOffice, WolfXL Community 2.0.8, and WolfXL Commercial 2.3.0 compute all 133 formulas; aspose-cells-foss covers 25/133 and zavora-xlsx 0/133. Community's `save()` writes no calculated values (0/133 saved), while Commercial saves all 133


See the [release snapshot dashboard](results-release-2026-04-28/DASHBOARD.md) for the fresh wheel-backed combined view, or the [historical dashboard](results/DASHBOARD.md) for the older public baseline.
Expand All @@ -136,9 +141,9 @@ See the [release snapshot dashboard](results-release-2026-04-28/DASHBOARD.md) fo

| Library | Version | Lang | Caps | Green Features |
|:--------|:--------|:-----|:-----|:--------------:|
| [WolfXL](https://github.com/SynthGL/wolfxl) | 2.1.0 | Python (Rust core) | R+W | R 20/21, W 19/21 (2026-09-08 snapshot) |
| [openpyxl](https://openpyxl.readthedocs.io/) | 3.1.5 | Python | R+W | R 21/21, W 21/21 (2026-09-08 snapshot) |
| [aspose-cells-foss](https://pypi.org/project/aspose-cells-foss/) | 26.7 | Python (JVM-core FOSS) | R+W | R 11/21, W 14/21 (2026-09-08 snapshot) |
| [WolfXL](https://github.com/SynthGL/wolfxl) | 2.0.8 | Python (Rust core) | R+W | R 21/21, W 21/21 (2026-10-03 snapshot) |
| [openpyxl](https://openpyxl.readthedocs.io/) | 3.1.5 | Python | R+W | R 21/21, W 21/21 (2026-10-03 snapshot) |
| [aspose-cells-foss](https://pypi.org/project/aspose-cells-foss/) | 26.7 | Python (JVM-core FOSS) | R+W | R 11/21, W 14/21 (2026-10-03 snapshot) |
| [XlsxWriter](https://xlsxwriter.readthedocs.io/) | 3.2.9 | Python | W | 15/18 |
| [xlsxwriter-constmem](https://xlsxwriter.readthedocs.io/) | 3.2.9 | Python | W | 12/18 |
| [openpyxl-readonly](https://openpyxl.readthedocs.io/) | 3.1.5 | Python | R | 3/18 |
Expand All @@ -152,9 +157,9 @@ See the [release snapshot dashboard](results-release-2026-04-28/DASHBOARD.md) fo
| [xlrd](https://github.com/python-excel/xlrd) | 2.0.2 | Python | R | .xls only |

> Green-feature counts are per dated snapshot; `x/18` numbers come from the
> 2026-04-29 release snapshot and `x/21` from the 2026-09-08 competitor
> snapshot (Tier 4 added; `pivot_tables` is unscored and excluded from the
> denominator). Never mix counts across snapshots.
> 2026-04-29 release snapshot and `x/21` from the 2026-10-03 competitor
> snapshot (Tier 4 added 2026-09-08; `pivot_tables` is unscored and
> excluded from the denominator). Never mix counts across snapshots.

### Cross-Language Context (oracle helpers)

Expand Down Expand Up @@ -307,14 +312,16 @@ uv run excelbench generate --output fixtures/excel
| **Tier 4** -- Production surfaces (2026-09-08) | Sheet protection, page setup, chart anchoring | 3 |

> Pivot tables are tested but score N/A across all adapters in the current macOS run.
> Green-feature denominators: /18 in the 2026-04-29 release snapshot, /21 in the 2026-09-08 and 2026-10-02 competitor snapshots (22 features modeled; `pivot_tables` is unscored and excluded).
> Green-feature denominators: /18 in the 2026-04-29 release snapshot, /21 in the 2026-09-08, 2026-10-02 and 2026-10-03 competitor snapshots (22 features modeled; `pivot_tables` is unscored and excluded).

### Planned

None currently queued. Tier 4 (charts anchoring, print settings, protection) shipped 2026-09-08.

## Detailed Results

- **[2026-10-03 competitor snapshot](results-2026-10-03/README.md)** -- current: [fidelity](results-2026-10-03/xlsx/README.md) (21 scored features x 17 adapters, WolfXL 2.0.8, heatmap [PNG](results-2026-10-03/xlsx/heatmap.png) / [SVG](results-2026-10-03/xlsx/heatmap.svg)), [mutation](results-2026-10-03/mutation/README.md), [calc](results-2026-10-03/calc/README.md), [cross-language](results-2026-10-03/cross-language/README.md)
- **[2026-10-02 competitor snapshot](results-2026-10-02/README.md)** -- previous: same lanes and adapters, WolfXL 2.0.5
- **[Competitor snapshot fidelity](results-2026-09-08/xlsx/README.md)** -- 22 features x 14 Python adapters, WolfXL 2.1.0 + aspose-cells-foss
- **[Competitor snapshot heatmap](results-2026-09-08/xlsx/heatmap.png)** ([SVG](results-2026-09-08/xlsx/heatmap.svg)) -- 22x14 visual score matrix
- **[Competitor snapshot mutation](results-2026-09-08/mutation/README.md)** -- template mutation: wall time, RSS, preservation
Expand All @@ -325,7 +332,8 @@ None currently queued. Tier 4 (charts anchoring, print settings, protection) shi
- **[XLS results](results/xls/README.md)** -- legacy format results
- **[Performance results](results/perf/README.md)** -- 2026-04-20 perf snapshot (historical; its WolfXL column measured private backend objects)
- **[Release snapshot perf](results-release-2026-04-28/perf/README.md)** -- 2026-04-29 perf snapshot (historical; its WolfXL column measured private backend objects)
- **[2026-10-02 perf snapshot](results-2026-10-02/perf/README.md)** -- current perf: 13 Python adapters, 19 features, WolfXL 2.0.5 through its public API
- **[2026-10-02 perf snapshot](results-2026-10-02/perf/README.md)** -- 2026-10-02 perf snapshot (historical): 13 Python adapters, 19 features, WolfXL 2.0.5 through its public API
- **[2026-10-03 perf snapshot](results-2026-10-03/perf/README.md)** -- current perf: same command, host and adapters as 2026-10-02, WolfXL 2.0.8 through its public API
- **[Dashboard](results/DASHBOARD.md)** -- combined fidelity + performance comparison
- **[Release snapshot dashboard](results-release-2026-04-28/DASHBOARD.md)** -- combined view for the fresh rerun
- **[Heatmap (PNG)](results/xlsx/heatmap.png)** | **[SVG](results/xlsx/heatmap.svg)** -- visual score matrix
Expand Down
2 changes: 1 addition & 1 deletion docs-site/adapters/openpyxl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ wb = load_workbook("data.xlsx", read_only=True)
openpyxl is **slow** by default. For large files:

- Use `read_only=True` for reading (no in-memory model built)
- Consider WolfXL for a 1.69x read speedup (2026-10-02 perf snapshot: WolfXL 2.0.5 `wolfxl.load_workbook` 13.46 ms vs openpyxl 22.79 ms, summed p50 over 19 features); in the 2026-10-02 fidelity snapshot WolfXL has 17/21 features green in both read and write against openpyxl's 21/21
- Consider WolfXL for a 1.73x read and 2.45x write speedup (2026-10-03 perf snapshot: WolfXL 2.0.8 `wolfxl.load_workbook` 13.90 ms vs openpyxl 24.01 ms, write 11.26 ms vs 27.59 ms, summed p50 over 19 features); in the 2026-10-03 fidelity snapshot WolfXL 2.0.8 ties openpyxl at 21/21 features green in both read and write
20 changes: 11 additions & 9 deletions docs-site/adapters/wolfxl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ description: "Rust-backed Python Excel library with an openpyxl-compatible API.

WolfXL is a Rust-backed Python library with an openpyxl-compatible API. ExcelBench drives it through that public API:

| Operation | Public API (WolfXL 2.0.5) | Speed vs openpyxl (2026-10-02 perf snapshot) |
| Operation | Public API (WolfXL 2.0.8) | Speed vs openpyxl (2026-10-03 perf snapshot) |
|-----------|---------------------------|-------------------|
| Read | `wolfxl.load_workbook(path)` | **1.69x faster** (13.46 ms vs 22.79 ms) |
| Write | `wolfxl.Workbook()` then `save(path)` | **1.20x faster** (22.28 ms vs 26.70 ms) |
| Read | `wolfxl.load_workbook(path)` | **1.73x faster** (13.90 ms vs 24.01 ms) |
| Write | `wolfxl.Workbook()` then `save(path)` | **2.45x faster** (11.26 ms vs 27.59 ms) |
| Modify | `wolfxl.load_workbook(path, modify=True)` then `save(path)`: patch mode rewrites only the changed sheet XML and copies every other ZIP entry unchanged | No current measurement |

Speed figures are sums of per-feature p50 wall times over the same 19 xlsx features for both libraries (warmup 3, 25 iterations, Apple M4 Pro, Python 3.12.3), measured on WolfXL 2.0.5 through its public API (`wolfxl.load_workbook`, `wolfxl.Workbook`). Source: `results-2026-10-02/perf/README.md`.
Speed figures are sums of per-feature p50 wall times over the same 19 xlsx features for both libraries (warmup 3, 25 iterations, Apple M4 Pro, Python 3.12.3), measured on WolfXL 2.0.8 through its public API (`wolfxl.load_workbook`, `wolfxl.Workbook`). In the same run python-calamine (13.92x openpyxl) and polars (3.87x) read faster than WolfXL. Source: `results-2026-10-03/perf/README.md`.

## Install

Expand Down Expand Up @@ -52,17 +52,19 @@ Large static content in a template (images, charts, pivot tables) is never parse

## Benchmark Scores

From the 2026-10-02 snapshot (WolfXL 2.0.5; fidelity from `results-2026-10-02/xlsx`, 21 scored features with `pivot_tables` unscored; speed from `results-2026-10-02/perf`):
From the 2026-10-03 snapshot (WolfXL 2.0.8): fidelity from `results-2026-10-03/xlsx` (21 scored features; `pivot_tables` is unscored for every adapter), mutation from `results-2026-10-03/mutation`, calc from `results-2026-10-03/calc`, speed from `results-2026-10-03/perf`:

| Axis | Score |
|------|-------|
| Fidelity | 17/21 features green in both read and write (read 19/21, write 18/21) |
| Read Speed | 1.69x openpyxl (WolfXL 2.0.5 public API) |
| Write Speed | 1.20x openpyxl (WolfXL 2.0.5 public API) |
| Fidelity | 21/21 features green in both read and write, tied with openpyxl 3.1.5 (21/21 read and write) |
| Read Speed | 1.73x openpyxl (WolfXL 2.0.8 public API) |
| Write Speed | 2.45x openpyxl (WolfXL 2.0.8 public API) |
| Feature Coverage | 100% (21/21 features score at least 1 in read or write) |
| Template mutation | Preserved, 17/17 template features kept (openpyxl 16/17) |
| Calc | Community 2.0.8 calculates 133/133 formulas, but its `save()` writes 0/133 calculated values; Commercial 2.3.0 calculates and saves 133/133 |
| Capability Breadth | 100 (R+W+Modify) |

Known gaps: `named_ranges` read 2/3 and write 2/3, `tables` read 2/3, `page_setup` write 2/3, `chart_anchor` write 0/3.
Every scored feature is 3/3 in both read and write, so there are no fidelity gaps in this snapshot.

## Source

Expand Down
Loading
Loading