Skip to content

perf(msexcel): replace O(n) merge-cell scan with O(1) spatial index#3808

Open
TongLing916 wants to merge 1 commit into
docling-project:mainfrom
TongLing916:main
Open

perf(msexcel): replace O(n) merge-cell scan with O(1) spatial index#3808
TongLing916 wants to merge 1 commit into
docling-project:mainfrom
TongLing916:main

Conversation

@TongLing916

Copy link
Copy Markdown

The _find_table_bounds BFS flood-fill and Phase 2 data extraction both did linear scans over sheet.merged_cells.ranges for EVERY cell visited, resulting in O(cells × merge_regions) complexity.

On documents with thousands of merge regions (e.g. A3 engineering spec sheets), this caused 40+ minute parse times on ~21k cells — while a flat Orders.xlsx with 4x more cells parsed in 4.5 seconds.

Changes:

  • Pre-build merge_top_left dict, merge_all_cells set, and hidden_merge_cells set once per sheet in _find_data_tables.
  • has_content() now does O(1) set lookup instead of iterating all ranges.
  • Phase 2 row_span/col_span uses O(1) dict get instead of linear scan.
  • When two merge ranges share the same top-left (pathological), the first wins, matching the old break-on-first-match behaviour.

Benchmark (380 merges, 9400 cells): 423x speedup, output identical.
Benchmark (4079 merges, 72600 cells): 0.57s (old path would be ~hours).

Checklist:

  • Documentation has been updated, if necessary.
  • Examples have been added, if necessary.
  • Tests have been added, if necessary.

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

DCO Check Passed

Thanks @TongLing916, all your commits are properly signed off. 🎉

@mergify

mergify Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

The _find_table_bounds BFS flood-fill and Phase 2 data extraction both
did linear scans over sheet.merged_cells.ranges for EVERY cell visited,
resulting in O(cells × merge_regions) complexity.

On documents with thousands of merge regions (e.g. A3 engineering spec
sheets), this caused 40+ minute parse times on ~21k cells — while a flat
Orders.xlsx with 4x more cells parsed in 4.5 seconds.

Changes:
- Pre-build merge_top_left dict, merge_all_cells set, and
  hidden_merge_cells set once per sheet in _find_data_tables.
- has_content() now does O(1) set lookup instead of iterating all ranges.
- Phase 2 row_span/col_span uses O(1) dict get instead of linear scan.
- When two merge ranges share the same top-left (pathological), the
  first wins, matching the old break-on-first-match behaviour.

Benchmark (380 merges, 9400 cells): 423x speedup, output identical.
Benchmark (4079 merges, 72600 cells): 0.57s (old path would be ~hours).

Signed-off-by: Tong Ling <ling_tong@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant