[PR #621] [MERGED] April perf improvements #1147

Open
opened 2026-01-29 14:50:26 +00:00 by claunia · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/xoofx/markdig/pull/621
Author: @MihaZupan
Created: 4/15/2022
Status: Merged
Merged: 4/20/2022
Merged by: @xoofx

Base: masterHead: perf-april


📝 Commits (10+)

  • e11a263 Reduce the size of Inline and casting overhead
  • 6eacf8a Reduce casts when rendering
  • e2770d8 Reduce covariance check overhead
  • a946c6d Use local over list access in GetSourcePosition
  • 89e4c29 Inline simple property getters
  • 47a22bc Reduce the size of LiteralInline and EmphasisDelimiterInline
  • b2eeaf7 Store trivia next to DataEntries
  • 53dff53 Make LinkInline SourceSpan fields non-nullable
  • 76e2583 Use method table pointer instead of TypeHandle
  • ee732e5 Add ToHtml helper accepting a TextWriter

📊 Changes

31 files changed (+674 additions, -238 deletions)

View changed files

src/Markdig.Tests/TestLazySubstring.cs (+67 -0)
📝 src/Markdig.Tests/TestMarkdigCoreApi.cs (+29 -0)
📝 src/Markdig/Extensions/AutoLinks/AutoLinkParser.cs (+47 -47)
📝 src/Markdig/Extensions/Bootstrap/BootstrapExtension.cs (+12 -9)
📝 src/Markdig/Helpers/ArrayHelper.cs (+1 -0)
src/Markdig/Helpers/BlockWrapper.cs (+30 -0)
📝 src/Markdig/Helpers/CharHelper.cs (+1 -1)
src/Markdig/Helpers/LazySubstring.cs (+44 -0)
📝 src/Markdig/Markdown.cs (+22 -0)
📝 src/Markdig/MarkdownPipeline.cs (+1 -1)
📝 src/Markdig/Parsers/BlockProcessor.cs (+21 -13)
📝 src/Markdig/Parsers/InlineProcessor.cs (+49 -9)
📝 src/Markdig/Parsers/Inlines/CodeInlineParser.cs (+12 -3)
📝 src/Markdig/Parsers/Inlines/EmphasisInlineParser.cs (+14 -9)
📝 src/Markdig/Parsers/Inlines/LiteralInlineParser.cs (+7 -5)
📝 src/Markdig/Parsers/ListBlockParser.cs (+1 -1)
📝 src/Markdig/Parsers/NumberedListItemParser.cs (+11 -1)
📝 src/Markdig/Renderers/Html/Inlines/CodeInlineRenderer.cs (+2 -2)
📝 src/Markdig/Renderers/Normalize/Inlines/CodeInlineRenderer.cs (+9 -8)
📝 src/Markdig/Renderers/RendererBase.cs (+40 -9)

...and 11 more files

📄 Description

Interesting changes:

  • Reducing the size of AST objects (mainly Inlines)
  • Reducing the cost of type checks by using spare bits on MarkdownObject to store the type and then doing Unsafe casts
  • Avoiding substring allocations to store CodeInline.Content
  • Adding various fast-paths to parsing for the advanced pipeline
  • Faster rendering loop by replacing Type.GetTypeHandle with the method table pointer

Perf:

Default pipeline

Parsing (~5% speedup, ~8% fewer allocated bytes)

Method SourceTexts Mean Error StdDev Median Allocated
Parse MarkdigReadme 279.842 us 0.8528 us 1.2231 us 280.632 us 139,241 B
Parse TracingArticle 37.482 us 0.0957 us 0.1373 us 37.439 us 18,592 B
Parse YarpDocs 2,727.076 us 13.5193 us 19.3889 us 2,740.968 us 1,320,172 B
Method SourceTexts Mean Error StdDev Median Allocated
Parse MarkdigReadme 264.739 us 1.1761 us 1.7240 us 264.500 us 127,737 B
Parse TracingArticle 36.518 us 0.1381 us 0.2024 us 36.614 us 17,088 B
Parse YarpDocs 2,593.829 us 13.2596 us 19.8464 us 2,594.943 us 1,218,524 B

Rendering (2-10% speedup)

Method SourceTexts Mean Error StdDev Median
RenderWithoutToString MarkdigReadme 62.725 us 0.6066 us 0.8891 us 63.415 us
RenderWithoutToString TracingArticle 8.028 us 0.0060 us 0.0088 us 8.028 us
RenderWithoutToString YarpDocs 825.007 us 6.1494 us 9.0137 us 817.198 us
Method SourceTexts Mean Error StdDev Median
RenderWithoutToString MarkdigReadme 61.584 us 1.1683 us 1.5992 us 62.792 us
RenderWithoutToString TracingArticle 7.328 us 0.0051 us 0.0077 us 7.329 us
RenderWithoutToString YarpDocs 786.404 us 4.9851 us 7.3071 us 791.788 us
Advanced pipeline

Parsing (7-16% speedup, ~7% fewer allocated bytes)

Method SourceTexts Mean Error StdDev Median Allocated
Parse MarkdigReadme 414.583 us 2.2855 us 3.4208 us 413.392 us 153,857 B
Parse TracingArticle 69.383 us 0.5513 us 0.7907 us 69.452 us 23,288 B
Parse YarpDocs 6,137.069 us 38.8496 us 51.8631 us 6,175.543 us 2,199,560 B
Method SourceTexts Mean Error StdDev Median Allocated
Parse MarkdigReadme 387.088 us 11.0335 us 16.5144 us 380.891 us 142,273 B
Parse TracingArticle 61.665 us 0.0889 us 0.1217 us 61.675 us 21,784 B
Parse YarpDocs 5,141.475 us 13.8774 us 20.3413 us 5,148.576 us 2,062,000 B

Rendering

Method SourceTexts Mean Error StdDev Median Allocated
RenderWithoutToString MarkdigReadme 72.981 us 0.2856 us 0.3909 us 73.300 us 1,944 B
RenderWithoutToString TracingArticle 8.533 us 0.0166 us 0.0232 us 8.529 us -
RenderWithoutToString YarpDocs 975.213 us 2.5721 us 3.7701 us 977.482 us 2,746 B
Method SourceTexts Mean Error StdDev Median Allocated
RenderWithoutToString MarkdigReadme 69.082 us 0.0590 us 0.0864 us 69.055 us 1,944 B
RenderWithoutToString TracingArticle 7.807 us 0.0147 us 0.0215 us 7.809 us -
RenderWithoutToString YarpDocs 917.742 us 2.4117 us 3.5351 us 918.906 us 2,745 B

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/xoofx/markdig/pull/621 **Author:** [@MihaZupan](https://github.com/MihaZupan) **Created:** 4/15/2022 **Status:** ✅ Merged **Merged:** 4/20/2022 **Merged by:** [@xoofx](https://github.com/xoofx) **Base:** `master` ← **Head:** `perf-april` --- ### 📝 Commits (10+) - [`e11a263`](https://github.com/xoofx/markdig/commit/e11a2630b80ccc477da5093d3c814989f3206e38) Reduce the size of Inline and casting overhead - [`6eacf8a`](https://github.com/xoofx/markdig/commit/6eacf8a17089f328d7341f500fc95514acb28308) Reduce casts when rendering - [`e2770d8`](https://github.com/xoofx/markdig/commit/e2770d8c11915fd7071f8b5bc8c32fe857357191) Reduce covariance check overhead - [`a946c6d`](https://github.com/xoofx/markdig/commit/a946c6d0b41a1383a82ced7c3e6231d13d21140a) Use local over list access in GetSourcePosition - [`89e4c29`](https://github.com/xoofx/markdig/commit/89e4c29f9fad836a4524b9c55bd0ab58b6ba54a6) Inline simple property getters - [`47a22bc`](https://github.com/xoofx/markdig/commit/47a22bc5e864b708408e2bd4284389144062ff46) Reduce the size of LiteralInline and EmphasisDelimiterInline - [`b2eeaf7`](https://github.com/xoofx/markdig/commit/b2eeaf7185dd0c8a5a415bc4d5b4da46dfaa7761) Store trivia next to DataEntries - [`53dff53`](https://github.com/xoofx/markdig/commit/53dff53260c1d21fa947813d2f409e235f77bf4b) Make LinkInline SourceSpan fields non-nullable - [`76e2583`](https://github.com/xoofx/markdig/commit/76e25833ad9031c5c36e79b7aac60232ef8448ee) Use method table pointer instead of TypeHandle - [`ee732e5`](https://github.com/xoofx/markdig/commit/ee732e5a4272fdadbbce7a40b37d5b197244536a) Add ToHtml helper accepting a TextWriter ### 📊 Changes **31 files changed** (+674 additions, -238 deletions) <details> <summary>View changed files</summary> ➕ `src/Markdig.Tests/TestLazySubstring.cs` (+67 -0) 📝 `src/Markdig.Tests/TestMarkdigCoreApi.cs` (+29 -0) 📝 `src/Markdig/Extensions/AutoLinks/AutoLinkParser.cs` (+47 -47) 📝 `src/Markdig/Extensions/Bootstrap/BootstrapExtension.cs` (+12 -9) 📝 `src/Markdig/Helpers/ArrayHelper.cs` (+1 -0) ➕ `src/Markdig/Helpers/BlockWrapper.cs` (+30 -0) 📝 `src/Markdig/Helpers/CharHelper.cs` (+1 -1) ➕ `src/Markdig/Helpers/LazySubstring.cs` (+44 -0) 📝 `src/Markdig/Markdown.cs` (+22 -0) 📝 `src/Markdig/MarkdownPipeline.cs` (+1 -1) 📝 `src/Markdig/Parsers/BlockProcessor.cs` (+21 -13) 📝 `src/Markdig/Parsers/InlineProcessor.cs` (+49 -9) 📝 `src/Markdig/Parsers/Inlines/CodeInlineParser.cs` (+12 -3) 📝 `src/Markdig/Parsers/Inlines/EmphasisInlineParser.cs` (+14 -9) 📝 `src/Markdig/Parsers/Inlines/LiteralInlineParser.cs` (+7 -5) 📝 `src/Markdig/Parsers/ListBlockParser.cs` (+1 -1) 📝 `src/Markdig/Parsers/NumberedListItemParser.cs` (+11 -1) 📝 `src/Markdig/Renderers/Html/Inlines/CodeInlineRenderer.cs` (+2 -2) 📝 `src/Markdig/Renderers/Normalize/Inlines/CodeInlineRenderer.cs` (+9 -8) 📝 `src/Markdig/Renderers/RendererBase.cs` (+40 -9) _...and 11 more files_ </details> ### 📄 Description Interesting changes: - Reducing the size of AST objects (mainly `Inline`s) - Reducing the cost of type checks by using spare bits on `MarkdownObject` to store the type and then doing `Unsafe` casts - Avoiding substring allocations to store `CodeInline.Content` - Adding various fast-paths to parsing for the advanced pipeline - Faster rendering loop by replacing `Type.GetTypeHandle` with the method table pointer Perf: <details> <summary>Default pipeline</summary> ### Parsing (~5% speedup, ~8% fewer allocated bytes) | Method | SourceTexts | Mean | Error | StdDev | Median | Allocated | |---------------------- |--------------- |-------------:|-----------:|-----------:|-------------:|------------:| | Parse | MarkdigReadme | 279.842 us | 0.8528 us | 1.2231 us | 280.632 us | 139,241 B | | Parse | TracingArticle | 37.482 us | 0.0957 us | 0.1373 us | 37.439 us | 18,592 B | | Parse | YarpDocs | 2,727.076 us | 13.5193 us | 19.3889 us | 2,740.968 us | 1,320,172 B | | Method | SourceTexts | Mean | Error | StdDev | Median | Allocated | |---------------------- |--------------- |-------------:|-----------:|-----------:|-------------:|------------:| | Parse | MarkdigReadme | 264.739 us | 1.1761 us | 1.7240 us | 264.500 us | 127,737 B | | Parse | TracingArticle | 36.518 us | 0.1381 us | 0.2024 us | 36.614 us | 17,088 B | | Parse | YarpDocs | 2,593.829 us | 13.2596 us | 19.8464 us | 2,594.943 us | 1,218,524 B | ### Rendering (2-10% speedup) | Method | SourceTexts | Mean | Error | StdDev | Median | |---------------------- |--------------- |-------------:|-----------:|-----------:|-------------:| | RenderWithoutToString | MarkdigReadme | 62.725 us | 0.6066 us | 0.8891 us | 63.415 us | | RenderWithoutToString | TracingArticle | 8.028 us | 0.0060 us | 0.0088 us | 8.028 us | | RenderWithoutToString | YarpDocs | 825.007 us | 6.1494 us | 9.0137 us | 817.198 us | | Method | SourceTexts | Mean | Error | StdDev | Median | |---------------------- |--------------- |-------------:|-----------:|-----------:|-------------:| | RenderWithoutToString | MarkdigReadme | 61.584 us | 1.1683 us | 1.5992 us | 62.792 us | | RenderWithoutToString | TracingArticle | 7.328 us | 0.0051 us | 0.0077 us | 7.329 us | | RenderWithoutToString | YarpDocs | 786.404 us | 4.9851 us | 7.3071 us | 791.788 us | </details> <details> <summary>Advanced pipeline</summary> ### Parsing (7-16% speedup, ~7% fewer allocated bytes) | Method | SourceTexts | Mean | Error | StdDev | Median | Allocated | |---------------------- |--------------- |-------------:|-----------:|-----------:|-------------:|------------:| | Parse | MarkdigReadme | 414.583 us | 2.2855 us | 3.4208 us | 413.392 us | 153,857 B | | Parse | TracingArticle | 69.383 us | 0.5513 us | 0.7907 us | 69.452 us | 23,288 B | | Parse | YarpDocs | 6,137.069 us | 38.8496 us | 51.8631 us | 6,175.543 us | 2,199,560 B | | Method | SourceTexts | Mean | Error | StdDev | Median | Allocated | |---------------------- |--------------- |-------------:|-----------:|-----------:|-------------:|------------:| | Parse | MarkdigReadme | 387.088 us | 11.0335 us | 16.5144 us | 380.891 us | 142,273 B | | Parse | TracingArticle | 61.665 us | 0.0889 us | 0.1217 us | 61.675 us | 21,784 B | | Parse | YarpDocs | 5,141.475 us | 13.8774 us | 20.3413 us | 5,148.576 us | 2,062,000 B | ### Rendering | Method | SourceTexts | Mean | Error | StdDev | Median | Allocated | |---------------------- |--------------- |-------------:|-----------:|-----------:|-------------:|------------:| | RenderWithoutToString | MarkdigReadme | 72.981 us | 0.2856 us | 0.3909 us | 73.300 us | 1,944 B | | RenderWithoutToString | TracingArticle | 8.533 us | 0.0166 us | 0.0232 us | 8.529 us | - | | RenderWithoutToString | YarpDocs | 975.213 us | 2.5721 us | 3.7701 us | 977.482 us | 2,746 B | | Method | SourceTexts | Mean | Error | StdDev | Median | Allocated | |---------------------- |--------------- |-------------:|-----------:|-----------:|-------------:|------------:| | RenderWithoutToString | MarkdigReadme | 69.082 us | 0.0590 us | 0.0864 us | 69.055 us | 1,944 B | | RenderWithoutToString | TracingArticle | 7.807 us | 0.0147 us | 0.0215 us | 7.809 us | - | | RenderWithoutToString | YarpDocs | 917.742 us | 2.4117 us | 3.5351 us | 918.906 us | 2,745 B | </details> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
claunia added the pull-request label 2026-01-29 14:50:26 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/markdig#1147