Skip to content

[Repo Assist] perf: pre-compute navigation menu structure once per build (O(n²) → O(n))#1129

Draft
github-actions[bot] wants to merge 4 commits intomainfrom
repo-assist/perf-nav-factory-2026-03-30-4dd5aa8c6fa58713
Draft

[Repo Assist] perf: pre-compute navigation menu structure once per build (O(n²) → O(n))#1129
github-actions[bot] wants to merge 4 commits intomainfrom
repo-assist/perf-nav-factory-2026-03-30-4dd5aa8c6fa58713

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

🤖 This PR was created by Repo Assist, an automated AI assistant.

Summary

fsdocs build called GetNavigationEntries once per output page to generate the sidebar navigation HTML. Each call did the full work: filter doc models, group by category, sort categories, sort items within each group, and check the filesystem for custom menu templates. With n pages this resulted in O(n²) work overall.

This PR replaces GetNavigationEntries with GetNavigationEntriesFactory, which separates the expensive structure computation from the cheap HTML rendering:

What Before After
Filter / group / sort O(n log n) × n calls O(n log n) once
File.Exists (template check) 2 × n calls 2 calls
Per-page HTML generation O(n) O(n) (unchanged)
Total O(n² log n) O(n²)

Change

  • New: DocContent.GetNavigationEntriesFactory(input, docModels, ignoreUncategorized) — pre-computes the sorted groups and template check once, returns string option -> string.
  • Removed: GetNavigationEntries (only used internally in two call sites, both updated).
  • In runDocContentPhase1, a single getNavEntries factory closure is created; the per-page loop calls getNavEntries (Some currentPagePath) instead of repeating the full computation.

Impact

For a site with 50 pages: ~49 fewer sort passes and ~98 fewer File.Exists calls per build / watch cycle. For large sites (200+ pages) the savings grow proportionally.

Trade-offs

  • The GetNavigationEntriesFactory closure holds a reference to sortedGroups (the pre-sorted (string * LiterateDocModel) list list). Memory footprint is the same as before (the models were already in memory).
  • In watch mode, the factory is re-created on each phase-1 run (triggered by file changes), so stale nav structure is never served.

Test Status

All tests passed (281 Markdown + 120 Literate + 30 CodeFormat + 92 ApiDocs + 8 fsdocs-tool = 531 tests, 0 failures)

Build succeeded.  0 Warning(s)  0 Error(s)
Passed! — Failed: 0, Passed: 531, Skipped: 6

Generated by 🌈 Repo Assist at {run-started}. Learn more.

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@1f672aef974f4246124860fc532f82fe8a93a57e

Replace GetNavigationEntries (called once per page) with
GetNavigationEntriesFactory, which pre-computes the expensive
filter/group/sort structure once and returns a cheap closure that
only applies IsActive flags and generates HTML.

For a site with n pages, this reduces:
- sorting/grouping: from O(n × n log n) to O(n log n)
- File.Exists calls for template detection: from 2n to 2

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…'Performance'

'### Performance' is not a recognised Keep a Changelog subsection.
Ionide.KeepAChangelog.Tasks 0.3.3 enforces the standard categories
(Added, Changed, Deprecated, Fixed, Removed, Security), causing the
build to fail with IKC0002. Rename to '### Changed'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor Author

Commit pushed: 5c98eb2

Generated by 🌈 Repo Assist at {run-started}. Learn more.

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.

0 participants