xlsx/scale-cells--n100

Stress test that writes a single worksheet populated with N unique string values laid out in a 100-column grid (so N/100 rows). Each cell gets a distinct 'c<i>' string so the sharedStrings table grows linearly with N. Parameterised across N = 100, 10000, 100000 to exercise the sharedStrings allocator at real-world spreadsheet sizes.

Library verdicts: python-xlsx: — xlsxjs: —

Metadata

Feature idxlsx/scale-cells--n100
Formatxlsx
Categoryscale
Familyxlsx/scale-cells
Axis valuessize=n100
Spececma-376-5-part-1 § 18.4.8 sst

XPath assertions

ID / partPredicateXPathpython-xlsxxlsxjs
sst-unique-count-n100
xl/sharedStrings.xml
equal = 100
The sharedStrings table's uniqueCount attribute must equal N.
/x:sst/@uniqueCount
sst-si-count-n100
xl/sharedStrings.xml
equal = 100
The sharedStrings table must contain exactly N <si> elements.
string(count(/x:sst/x:si))
first-cell-marker-n100
xl/sharedStrings.xml
exist
The first <si> entry must be the 'c0' marker string, guarding against head-of-stream reorder / truncation.
/x:sst/x:si[1]/x:t[normalize-space()='c0']
last-cell-marker-n100
xl/sharedStrings.xml
exist
The last <si> entry must be the expected 'c<N-1>' marker, guarding against tail truncation or reorder.
/x:sst/x:si[last()]/x:t[normalize-space()='c99']

Render assertions

No render assertions declared.

Generator source

scripts/gen_scale_cells.py

#!/usr/bin/env python3
"""Generate ``fixtures/xlsx/scale-cells--<n>.xlsx``.

Parameterised stress fixture for the ``xlsx/scale-cells`` family. Writes
a single-sheet workbook whose active sheet contains ``--count`` cells:
a 100-column grid filled row-by-row. Each cell carries a short
deterministic string so the sharedStrings table grows proportionally.

Assertions target:
  - a specific cell value in the first row, exercising the A1 corner
  - a specific cell value in the final row, exercising the far corner
    past the row-height dimension threshold

The generator writes strings (not numbers) on purpose: this exercises
the sharedStrings table and surfaces any scaling bug in its index
allocator / lookup.

Usage::

    python scripts/gen_scale_cells.py \
        --count 10000 \
        --out fixtures/xlsx/scale-cells--n10000.xlsx
"""

from __future__ import annotations

import argparse
import datetime as _dt
from pathlib import Path

from xlsx import Workbook

_REPRODUCIBLE_DT = (1980, 1, 1, 0, 0, 0)

_REPO_ROOT = Path(__file__).resolve().parent.parent

# Grid width. 100k cells -> 1000 rows x 100 cols, safely inside any
# SpreadsheetML row/column limits.
_COLS = 100


def _write(count: int, out: Path) -> None:
    wb = Workbook()
    ws = wb.active
    for i in range(count):
        row = (i // _COLS) + 1
        col = (i % _COLS) + 1
        # 'c' + decimal index. Cheap, unique, deterministic.
        ws.cell(row=row, column=col, value=f"c{i}")
    out.parent.mkdir(parents=True, exist_ok=True)
    wb.save(out, zip_date_time=_REPRODUCIBLE_DT)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--count", required=True, type=int, help="Number of cells to populate.")
    parser.add_argument("--out", required=True, help="Output path for the .xlsx file.")
    args = parser.parse_args()

    out_path = Path(args.out)
    if not out_path.is_absolute():
        out_path = _REPO_ROOT / out_path
    _write(args.count, out_path)
    print(out_path)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "xlsx/scale-cells--n100",
  "kind": "literal",
  "title": "Scale: many cells in one sheet",
  "format": "xlsx",
  "category": "scale",
  "summary": "Stress test that writes a single worksheet populated with N unique string values laid out in a 100-column grid (so N/100 rows). Each cell gets a distinct 'c<i>' string so the sharedStrings table grows linearly with N. Parameterised across N = 100, 10000, 100000 to exercise the sharedStrings allocator at real-world spreadsheet sizes.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "18.4.8",
    "element": "sst",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/SpreadsheetML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/sml.xsd",
    "notes": "python-xlsx writes string cells as <c t=\"s\"><v>INDEX</v></c> where INDEX references a <si> entry in xl/sharedStrings.xml. Filling 100k unique strings is not unusual in a real workbook and exercises both the sharedStrings allocator (map + list) and the worksheet serialiser. The manifest asserts (a) the first and last cell carry their expected marker strings, (b) the sst uniqueCount attribute matches N, (c) the <si> element count matches N. Taken together these catch reuse-index collisions, uniqueCount/element-count drift, and partial writes."
  },
  "fixtures": {
    "machine": "xlsx/scale-cells--n100"
  },
  "generator": {
    "python": "scripts/gen_scale_cells.py",
    "arg_template": "--count {size.n} --out fixtures/xlsx/scale-cells--{size.id}.xlsx"
  },
  "meta": {
    "performance": {
      "recorded_on": "2026-05-05",
      "notes": "Save-time scales sub-linearly from 100 to 10k (startup overhead dominates the 100-cell case) and roughly linearly from 10k to 100k. sharedStrings.xml becomes the dominant part past 10k cells (~220KB at 10k, ~2.2MB at 100k).",
      "cases": [
        {
          "id": "n100",
          "save_seconds": 0.19,
          "file_bytes": 5527,
          "shared_strings_bytes": 1985
        },
        {
          "id": "n10000",
          "save_seconds": 0.29,
          "file_bytes": 60217,
          "shared_strings_bytes": 208987
        },
        {
          "id": "n100000",
          "save_seconds": 1.3,
          "file_bytes": 564068,
          "shared_strings_bytes": 2188988
        }
      ]
    }
  },
  "_expansion": {
    "parent_id": "xlsx/scale-cells",
    "bindings": {
      "size": "n100"
    }
  },
  "assertions": [
    {
      "id": "sst-unique-count-n100",
      "part": "xl/sharedStrings.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "/x:sst/@uniqueCount",
      "must": "equal",
      "value": "100",
      "description": "The sharedStrings table's uniqueCount attribute must equal N."
    },
    {
      "id": "sst-si-count-n100",
      "part": "xl/sharedStrings.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "string(count(/x:sst/x:si))",
      "must": "equal",
      "value": "100",
      "description": "The sharedStrings table must contain exactly N <si> elements."
    },
    {
      "id": "first-cell-marker-n100",
      "part": "xl/sharedStrings.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "/x:sst/x:si[1]/x:t[normalize-space()='c0']",
      "must": "exist",
      "description": "The first <si> entry must be the 'c0' marker string, guarding against head-of-stream reorder / truncation."
    },
    {
      "id": "last-cell-marker-n100",
      "part": "xl/sharedStrings.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "/x:sst/x:si[last()]/x:t[normalize-space()='c99']",
      "must": "exist",
      "description": "The last <si> entry must be the expected 'c<N-1>' marker, guarding against tail truncation or reorder."
    }
  ]
}

Fixture

Download scale-cells--n100.xlsx (5.4 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

python-xlsx writes string cells as <c t="s"><v>INDEX</v></c> where INDEX references a <si> entry in xl/sharedStrings.xml. Filling 100k unique strings is not unusual in a real workbook and exercises both the sharedStrings allocator (map + list) and the worksheet serialiser. The manifest asserts (a) the first and last cell carry their expected marker strings, (b) the sst uniqueCount attribute matches N, (c) the <si> element count matches N. Taken together these catch reuse-index collisions, uniqueCount/element-count drift, and partial writes.