Skip to content

perf(core): Skip unnecessary content scans in createRenderContext#1398

Closed
yamadashy wants to merge 3 commits intomainfrom
perf/skip-unnecessary-content-scans
Closed

perf(core): Skip unnecessary content scans in createRenderContext#1398
yamadashy wants to merge 3 commits intomainfrom
perf/skip-unnecessary-content-scans

Conversation

@yamadashy
Copy link
Copy Markdown
Owner

@yamadashy yamadashy commented Apr 5, 2026

Skip two expensive full-content scans in createRenderContext when their results are unused by the current output format:

  • calculateFileLineCounts: scans all file contents to count newlines. Not referenced by any Handlebars template (XML, markdown, plain) or parsable output generator (XML, JSON). Only used by the skill path, so skip for regular output entirely.
  • calculateMarkdownDelimiter: scans all file contents for backtick sequences. Only used by the markdown template and skill generators. Skip for XML, plain, JSON, and parsable-XML output styles.

Also optimized the implementations for when they do run:

  • calculateMarkdownDelimiter: replaced flatMap + intermediate array allocation with single-pass inline max tracking.
  • calculateFileLineCounts: replaced regex-based newline counting (which allocated arrays of all matches) with indexOf-based loop.

Extracted from #1377. This is a pure performance optimization with no functional changes — output is identical for all formats.

Checklist

  • Run npm run test
  • Run npm run lint

Open with Devin

Skip two expensive full-content scans in createRenderContext when
their results are unused by the current output format:

- calculateFileLineCounts: scans all file contents to count newlines.
  Not referenced by any Handlebars template (XML, markdown, plain) or
  parsable output generator (XML, JSON). Only used by the skill path,
  so skip for regular output entirely.

- calculateMarkdownDelimiter: scans all file contents for backtick
  sequences. Only used by the markdown template and skill generators.
  Skip for XML, plain, JSON, and parsable-XML output styles.

Also optimized the implementations for when they do run:
- calculateMarkdownDelimiter: replaced flatMap + intermediate array
  allocation with single-pass inline max tracking.
- calculateFileLineCounts: replaced regex-based newline counting
  (which allocated arrays of all matches) with indexOf-based loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 5, 2026

⚡ Performance Benchmark

Latest commit:940e1b0 Merge branch 'main' into perf/skip-unnecessary-content-scans
Status:✅ Benchmark complete!
Ubuntu:1.53s (±0.02s) → 1.53s (±0.01s) · -0.01s (-0.5%)
macOS:1.19s (±0.18s) → 1.21s (±0.09s) · +0.02s (+1.9%)
Windows:1.91s (±0.04s) → 1.91s (±0.03s) · +0.01s (+0.3%)
Details
  • Packing the repomix repository with node bin/repomix.cjs
  • Warmup: 2 runs (discarded), interleaved execution
  • Measurement: 20 runs / 30 on macOS (median ± IQR)
  • Workflow run
History

67c06ff refactor(core): Use character scan instead of regex in calculateMarkdownDelimiter

Ubuntu:1.53s (±0.01s) → 1.54s (±0.02s) · +0.00s (+0.1%)
macOS:1.03s (±0.14s) → 1.02s (±0.11s) · -0.01s (-1.3%)
Windows:1.90s (±0.02s) → 1.89s (±0.04s) · -0.01s (-0.3%)

f890c50 perf(core): Skip unnecessary content scans in createRenderContext

Ubuntu:1.54s (±0.03s) → 1.53s (±0.02s) · -0.01s (-0.8%)
macOS:1.06s (±0.16s) → 1.02s (±0.21s) · -0.04s (-4.0%)
Windows:1.92s (±0.05s) → 1.90s (±0.04s) · -0.02s (-0.9%)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 5, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9ce62023-a770-4e06-8245-5a325353003c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactored markdown delimiter calculation using single-pass regex and introduced conditional computation in render context generation. Added CreateRenderContextOptions interface to allow selective computation of file line counts and markdown delimiters based on a forceAll flag, optimizing rendering for non-markdown output.

Changes

Cohort / File(s) Summary
Render Context Configuration
src/core/output/outputGenerate.ts
Optimized calculateMarkdownDelimiter with single-pass regex scanning; added countNewlines helper; introduced CreateRenderContextOptions interface; modified createRenderContext to conditionally compute fileLineCounts and markdownCodeBlockDelimiter based on options.forceAll flag.
Skill Reference Integration
src/core/skill/packSkill.ts
Updated generateSkillReferences to invoke createRenderContext with { forceAll: true } option for full context computation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PR #129: Directly builds on modifications to the createRenderContext API introducing the forceAll conditional computation option in src/core/output/outputGenerate.ts.
  • PR #998: Extends createRenderContext with CreateRenderContextOptions.forceAll flag and updates generateSkillReferences usage in src/core/skill/packSkill.ts.
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: skipping unnecessary content scans for performance in createRenderContext.
Description check ✅ Passed The description covers what was optimized, why those scans can be skipped, implementation improvements, and confirms testing. The provided template is minimal and both checklist items are completed.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/skip-unnecessary-content-scans

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 5, 2026

Codecov Report

❌ Patch coverage is 93.10345% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.42%. Comparing base (4acbbc0) to head (940e1b0).

Files with missing lines Patch % Lines
src/core/output/outputGenerate.ts 92.85% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1398      +/-   ##
==========================================
+ Coverage   87.40%   87.42%   +0.01%     
==========================================
  Files         116      116              
  Lines        4392     4414      +22     
  Branches     1018     1024       +6     
==========================================
+ Hits         3839     3859      +20     
- Misses        553      555       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 5, 2026

Deploying repomix with  Cloudflare Pages  Cloudflare Pages

Latest commit: 940e1b0
Status: ✅  Deploy successful!
Preview URL: https://88b467dd.repomix.pages.dev
Branch Preview URL: https://perf-skip-unnecessary-conten.repomix.pages.dev

View logs

gemini-code-assist[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 5, 2026

Code Review - perf(core): Skip unnecessary content scans in createRenderContext

Overall: Looks good. This is a clean, well-scoped performance optimization. The skip logic is correct for all current output paths, the algorithm rewrites are sound, and the PR description accurately reflects the changes. A few suggestions below.

1. Pre-existing bug exposed: fileLineCounts 0 treated as missing in skillStatistics.ts - In skillStatistics.ts, the fallback uses || which treats 0 as falsy. calculateFileLineCounts correctly returns 0 for empty files, but the || fallback overrides it with 1. This is pre-existing, not introduced by this PR, but now forceAll: true makes the 0 return value load-bearing. Consider changing || to ?? in a follow-up.

2. Add a comment near needsMarkdownDelimiter about future output styles - The condition hard-codes that only markdown needs the delimiter. If a new output style is added that uses markdownCodeBlockDelimiter, this condition must be updated. A brief comment pointing to the generateOutput switch would prevent a future oversight.

3. Consider adding tests for the skip logic and rewritten helpers - The new conditional paths and rewritten algorithms have no dedicated test coverage. The forceAll: true path (the only one calling the rewritten calculateFileLineCounts) is fully mocked in packSkill.test.ts, so the real countNewlines is never exercised by any test.

4. Minor: restore the removed comment about trailing-newline edge case - The original calculateFileLineCounts had a useful comment explaining the trailing-newline logic. The PR removes it, but the behavior is non-obvious. A brief comment would help future readers.

No Security Issues - The markdown fence escape mechanism is never bypassed for any output path that renders code fences. The fallback triple-backtick for non-markdown styles is safe since those templates never reference the delimiter.

Performance Assessment - The optimizations are meaningful: skipping calculateFileLineCounts saves O(total-bytes), skipping calculateMarkdownDelimiter saves a full regex pass for XML/JSON/plain, indexOf-based countNewlines reduces GC pressure, and the single-pass calculateMarkdownDelimiter eliminates flatMap intermediate array allocations.


Reviewed with Claude

yamadashy and others added 2 commits April 5, 2026 14:43
…ownDelimiter

Replace RegExp.exec loop with a simple character-by-character scan
to avoid regex object and match object allocations entirely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 5, 2026

Code Review (follow-up) — perf(core): Skip unnecessary content scans in createRenderContext

Overall: Approve. Clean, well-scoped performance optimization. The skip logic is correct for all current output paths. Adding a few observations beyond the prior review.

New findings

1. forceAll is redundant for markdownCodeBlockDelimiter in the skill path

packSkill.ts already sets style: 'markdown' via markdownConfig (line 98–104), so needsMarkdownDelimiter would already be true via the style === 'markdown' branch — forceAll only adds value for needsLineCounts. This isn't a bug, but it means the forceAll flag's semantics are slightly misleading: it appears to force "everything" but one of its two effects is already covered by the style override. Worth a comment for future readers.

2. calculateMarkdownDelimiter could use indexOf for the non-backtick stretches

The countNewlines helper leverages V8's native SIMD-capable String.prototype.indexOf. The calculateMarkdownDelimiter character scan visits every byte in JS. For typical source files where backticks are sparse, an indexOf('')`-based approach to skip non-backtick stretches would be faster. Minor optimization opportunity for a future PR — not blocking.

3. fileLineCounts: {} as sentinel is a silent footgun

Details

The empty object {} satisfies Record<string, number> but silently returns undefined for any key lookup. Callers like skillStatistics.ts happen to have a || fallback, but future callers won't know the object is intentionally empty vs. just missing entries. Using null with Record<string, number> | null would make the "not computed" state explicit in the type system and force callers to handle it. Low priority since the current code paths are correct.

4. File size note

Details

outputGenerate.ts is now 439 lines, exceeding the project's 250-line guideline. This was pre-existing (the file was already over the limit), but the +39 net lines widen the gap. Consider splitting the helper functions (calculateMarkdownDelimiter, countNewlines, calculateFileLineCounts) into a separate utility file in a follow-up. Not blocking for this PR.

Previously noted (still relevant)

The prior review's points about restoring the trailing-newline comment and the || vs ?? bug in skillStatistics.ts (lines 123, 147) remain actionable. The || issue is pre-existing but becomes more relevant now that forceAll: true makes calculateFileLineCounts the canonical source for the skill path — 0 for empty files gets silently overridden to 1.

Verdict

The optimization is sound, the security implications are nil, and the algorithmic rewrites are correct. The benchmark data confirms real improvements. The suggestions above are all non-blocking improvements that could be addressed in follow-ups.


Reviewed with Claude

@yamadashy
Copy link
Copy Markdown
Owner Author

Closing — benchmark shows no measurable improvement (within noise). The added complexity (forceAll option, conditional logic) isn't justified for this level of gain.

@yamadashy yamadashy closed this Apr 5, 2026
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