Skip to content

Optimize GetInnerText with style caching and incremental computation#206

Merged
FlorianRappl merged 1 commit intoAngleSharp:develfrom
jafin:perf/optimize-getinnertext
Mar 28, 2026
Merged

Optimize GetInnerText with style caching and incremental computation#206
FlorianRappl merged 1 commit intoAngleSharp:develfrom
jafin:perf/optimize-getinnertext

Conversation

@jafin
Copy link
Copy Markdown
Contributor

@jafin jafin commented Mar 28, 2026

Types of Changes

Prerequisites

Please make sure you can check the following two boxes:

  • I have read the CONTRIBUTING document
  • My code follows the code style of this project

Contribution Type

What types of changes does your code introduce? Put an x in all the boxes that apply:

  • Bug fix (non-breaking change which fixes an issue, please reference the issue id)
  • New feature (non-breaking change which adds functionality, make sure to open an associated issue first)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • My change requires a change to the documentation
  • I have updated the documentation accordingly
  • I have added tests to cover my changes
  • All new and existing tests passed

Description

  • Cache computed styles in a dictionary across the entire traversal so the same element is never computed twice (hit as element, parent, and sibling).
  • Hoist StyleCollection creation to the top of GetInnerText and reuse it throughout, eliminating repeated stylesheet enumeration and LINQ allocations.
  • When the parent's computed style is already cached, use incremental computation (ComputeDeclarationsWithParent) that inherits from the cached parent instead of walking all ancestors - replacing the O(depth) ancestor loop with a single UpdateDeclarations call.

Related #55

Benchmark.net results from similar test to #55

Benchmark Original Total Speedup Mem Original Mem Now Mem Reduction
SmallTable (10×5) 10.6 ms 7.7x 13 MB 1.6 MB 88%
LargeTable (200×15) 4,666 ms 10.2x 742 MB 76 MB 90%
LargeTableBody (200×15) 7,558 ms 16.7x 738 MB 76 MB 90%
AllCells individually (3000) 8,039 ms 1.9x 1,309 MB 676 MB 48%

- Cache computed styles in a dictionary across the entire traversal so
  the same element is never computed twice (hit as element, parent, and
  sibling).
- Hoist StyleCollection creation to the top of GetInnerText and reuse it
  throughout, eliminating repeated stylesheet enumeration and LINQ
  allocations.
- When the parent's computed style is already cached, use incremental
  computation (ComputeDeclarationsWithParent) that inherits from the
  cached parent instead of walking all ancestors - replacing the O(depth)
  ancestor loop with a single UpdateDeclarations call.
Copy link
Copy Markdown
Contributor

@FlorianRappl FlorianRappl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely a GREAT one. Thanks a lot!

@FlorianRappl FlorianRappl merged commit 7396751 into AngleSharp:devel Mar 28, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants