Skip to content

perf(core): Automated performance tuning by Claude#1615

Closed
yamadashy wants to merge 1 commit into
mainfrom
claude/optimistic-goodall-z9N2O
Closed

perf(core): Automated performance tuning by Claude#1615
yamadashy wants to merge 1 commit into
mainfrom
claude/optimistic-goodall-z9N2O

Conversation

@yamadashy

Copy link
Copy Markdown
Owner

Skip two full-content scans in createRenderContext (src/core/output/outputGenerate.ts) that are computed on every run but only consumed by output paths that aren't active in the default run.

Summary

createRenderContext unconditionally computed two values by scanning the entire content of every file:

  • calculateFileLineCounts — a /\n/g regex over every file. Only ever read by the skill-generation path (skillSectionGenerators / skillStatistics). The xml / markdown / plain / json output templates never reference it.
  • calculateMarkdownDelimiter — a backtick-run regex over every file. Only read by the Markdown template and the skill sections (which render Markdown-style code fences). For xml / plain / json it is unused.

This PR gates both behind the paths that actually consume them:

const skillGenerateActive = config.skillGenerate !== undefined;
const fileLineCounts = skillGenerateActive ? calculateFileLineCounts(processedFiles) : {};
const markdownCodeBlockDelimiter =
  config.output.style === 'markdown' || skillGenerateActive
    ? calculateMarkdownDelimiter(processedFiles)
    : '```';

In warm-cache runs the output branch (produceOutput) is the wall-clock critical path — it overlaps with the much cheaper metrics branch — so eliminating these two scans maps almost 1:1 to wall-clock time.

Behavior preservation

This is a pure performance change; output is unchanged.

  • The skill path always has config.skillGenerate !== undefined in production (guarded at packager.ts before packSkill, and set by the MCP generateSkillTool), so fileLineCounts is still fully computed wherever it's consumed.
  • The Markdown template still always receives the real delimiter (style === 'markdown'). For xml/plain/json — which never read the field — the default '```' fence is substituted (and equals what calculateMarkdownDelimiter returns for zero-backtick content anyway).
  • Output verified byte-identical across xml / markdown / plain (the only diff observed was the edited source file itself appearing in repomix's self-pack).
  • json and parsable-xml paths confirmed to reference neither field.
  • Reviewed by two independent agents for edge cases (empty repo, multi-root, --style json, parsable variants, MCP) — no production behavior change.

Benchmark

repomix self-pack, warm token-count cache, paired interleaved A/B (base vs changed lib, alternating order to cancel drift), node process spawn measuring full CLI wall-clock, 60 pairs:

metric value
base mean 1242.3 ms
paired delta 23.8 ms = 1.91%
95% CI [0.94%, 2.88%] (i.e. [11.7, 35.8] ms)
changed faster in 43 / 60 pairs

The point estimate lands just under the 2% target and within measurement noise of it (the container is shared/noisy; per-sample SD ≈ 47 ms). The win is a genuine ~24 ms of eliminated dead work with zero behavioral risk, so it's offered here as a safe, low-risk improvement for review.

Note: this branch is claude/optimistic-goodall-z9N2O (the session's mandated working branch) rather than perf/auto-perf-tuning.

Checklist

  • Run npm run test — 1320/1320 passing
  • Run npm run lint — biome / oxlint / tsgo clean

Generated by Claude Code

…on-markdown output

intent(perf): automated perf tuning targeting >=2% of a ~1.2s default CLI run, behavior-preserving
decision(render-context): gate calculateFileLineCounts behind config.skillGenerate, since only the
  skill-generation path (skillSectionGenerators / skillStatistics) consumes fileLineCounts; the
  xml/markdown/plain Handlebars templates never reference it
decision(render-context): gate calculateMarkdownDelimiter behind style==='markdown' || skillGenerate,
  since only the Markdown template and skill sections render Markdown-style code fences; xml/plain
  ignore markdownCodeBlockDelimiter, so fall back to the default '```' fence
learned(critical-path): in warm-cache runs the output branch (produceOutput) is the wall-clock
  critical path — it overlaps with the cheap metrics branch — so removing these two full-content
  scans from createRenderContext maps ~1:1 to wall-clock; both scans walk every file's entire
  content (newline regex + backtick regex), which the profile attributed to ~17ms of main-thread CPU
constraint(behavior): output verified byte-identical across xml/markdown/plain (the only diff was the
  edited source file itself appearing in repomix's self-pack); full suite 1320/1320 passing

Benchmark (repomix self-pack, warm cache, paired interleaved A/B, 60 pairs, node spawn wall-clock):
  base mean    1242.3ms
  paired delta 23.8ms  =  1.91%  (95% CI [0.94%, 2.88%]); changed faster in 43/60 pairs
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

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: 2cf6e8ff-44d9-4ff4-8772-2f4a60a1995c

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/optimistic-goodall-z9N2O

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.

@yamadashy yamadashy added the automated label Jun 2, 2026 — with Claude
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

⚡ Performance Benchmark

Latest commit:57a0041 perf(output): Skip unused render-context computations for non-skill/non-markdown output
Status:✅ Benchmark complete!
Ubuntu:0.43s (±0.01s) → 0.42s (±0.01s) · -0.01s (-1.6%)
macOS:0.78s (±0.26s) → 0.76s (±0.25s) · -0.02s (-2.1%)
Windows:1.09s (±0.03s) → 1.09s (±0.03s) · -0.00s (-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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request optimizes createRenderContext by conditionally calculating fileLineCounts and markdownCodeBlockDelimiter only when specific configuration flags are active, avoiding unnecessary full-content scans. The reviewer suggests using lazy getters on the returned RenderContext object instead of hardcoded conditional checks. This approach decouples the context creation from specific templates, ensures safety for programmatic users, and maintains efficiency by executing the expensive calculations on-demand and caching the results.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +79 to +94
const { config, processedFiles } = outputGeneratorContext;

// `fileLineCounts` is only consumed by the skill-generation path
// (skillSectionGenerators / skillStatistics). For the standard xml/markdown/plain
// output templates it is never referenced, so skip the full-content newline scan
// over every file unless skill generation is active.
const skillGenerateActive = config.skillGenerate !== undefined;
const fileLineCounts = skillGenerateActive ? calculateFileLineCounts(processedFiles) : {};

// `markdownCodeBlockDelimiter` is only referenced by the Markdown template and the
// skill sections (which also render Markdown-style code fences). For xml/plain output
// it is unused, so skip scanning every file for backtick runs and fall back to the
// default fence.
const markdownCodeBlockDelimiter =
config.output.style === 'markdown' || skillGenerateActive ? calculateMarkdownDelimiter(processedFiles) : '```';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Instead of hardcoding conditional checks based on specific configuration flags (like config.skillGenerate or config.output.style), we can use lazy getters on the returned RenderContext object.

This approach is more robust because:

  1. Decoupling: It decouples context creation from the specific templates/styles that consume these fields. If a new style is added or an existing template is modified to use these fields, it will work automatically without needing to update the gating logic here.
  2. Safety: It is 100% safe for programmatic users of the library who might call createRenderContext directly and expect these fields to be populated.
  3. Efficiency: The expensive calculations (calculateFileLineCounts and calculateMarkdownDelimiter) are only executed on-demand when they are actually accessed, and the results are cached for subsequent accesses.
  const { processedFiles } = outputGeneratorContext;

  let cachedFileLineCounts: Record<string, number> | null = null;
  let cachedMarkdownDelimiter: string | null = null;

  const getFileLineCounts = () => {
    if (cachedFileLineCounts === null) {
      cachedFileLineCounts = calculateFileLineCounts(processedFiles);
    }
    return cachedFileLineCounts;
  };

  const getMarkdownDelimiter = () => {
    if (cachedMarkdownDelimiter === null) {
      cachedMarkdownDelimiter = calculateMarkdownDelimiter(processedFiles);
    }
    return cachedMarkdownDelimiter;
  };

Comment on lines +108 to +113
fileLineCounts,
fileSummaryEnabled: outputGeneratorContext.config.output.fileSummary,
directoryStructureEnabled: outputGeneratorContext.config.output.directoryStructure,
filesEnabled: outputGeneratorContext.config.output.files,
escapeFileContent: outputGeneratorContext.config.output.parsableStyle,
markdownCodeBlockDelimiter: calculateMarkdownDelimiter(outputGeneratorContext.processedFiles),
markdownCodeBlockDelimiter,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Use the lazy getters defined above to populate fileLineCounts and markdownCodeBlockDelimiter on-demand.

Suggested change
fileLineCounts,
fileSummaryEnabled: outputGeneratorContext.config.output.fileSummary,
directoryStructureEnabled: outputGeneratorContext.config.output.directoryStructure,
filesEnabled: outputGeneratorContext.config.output.files,
escapeFileContent: outputGeneratorContext.config.output.parsableStyle,
markdownCodeBlockDelimiter: calculateMarkdownDelimiter(outputGeneratorContext.processedFiles),
markdownCodeBlockDelimiter,
get fileLineCounts() {
return getFileLineCounts();
},
fileSummaryEnabled: outputGeneratorContext.config.output.fileSummary,
directoryStructureEnabled: outputGeneratorContext.config.output.directoryStructure,
filesEnabled: outputGeneratorContext.config.output.files,
escapeFileContent: outputGeneratorContext.config.output.parsableStyle,
get markdownCodeBlockDelimiter() {
return getMarkdownDelimiter();
},

@codecov

codecov Bot commented Jun 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.87%. Comparing base (c4eac37) to head (57a0041).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1615   +/-   ##
=======================================
  Coverage   90.86%   90.87%           
=======================================
  Files         121      121           
  Lines        4698     4702    +4     
  Branches     1096     1098    +2     
=======================================
+ Hits         4269     4273    +4     
  Misses        429      429           

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

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying repomix with  Cloudflare Pages  Cloudflare Pages

Latest commit: 57a0041
Status: ✅  Deploy successful!
Preview URL: https://926c754d.repomix.pages.dev
Branch Preview URL: https://claude-optimistic-goodall-z9.repomix.pages.dev

View logs

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