Conversation
…rmance regression Key optimizations: 1. Add JobManager.WriteJobsYAML(*strings.Builder) to write the jobs section directly to an existing builder instead of allocating a 96KB intermediate buffer + string copy in RenderToYAML. 2. Add JobManager.renderJobTo(*strings.Builder, *Job) that writes each job directly to the provided builder, eliminating one 8KB buffer per job. 3. Reduce generateYAML body/yaml builder initial capacity from 96KB to 64KB (sufficient for ~60KB compiled output). 4. Replace yaml.WriteString(line + "\n") with two separate WriteString/WriteByte calls in generateEngineExecutionSteps and other hot paths to eliminate temporary string allocations. 5. Replace string concatenation in generateWorkflowBody (onSection+"\n\n", etc.) with two WriteString calls. Results: -27% in B/op (992KB → 722KB), -2% in allocs/op, ~21% faster wall time vs the regressed version (1.67ms → 1.32ms). Agent-Logs-Url: https://github.com/github/gh-aw/sessions/e57b41f5-70f8-457a-aa52-7d03cee44cd6 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/e57b41f5-70f8-457a-aa52-7d03cee44cd6 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR optimizes workflow YAML generation to reduce allocations in CompileSimpleWorkflow, primarily by streaming YAML directly into existing builders instead of building intermediate strings.
Changes:
- Added direct-write helpers in
JobManagerso jobs can be rendered into an existingstrings.Builder. - Updated workflow body generation to use the direct-write job path and reduced initial YAML builder capacity from 96 KiB to 64 KiB.
- Replaced several
WriteString(line + "\n")hot-loop patterns withWriteString(line)plusWriteByte('\n').
Show a summary per file
| File | Description |
|---|---|
pkg/workflow/jobs.go |
Adds direct-to-builder job rendering helpers and keeps string-returning wrappers for compatibility. |
pkg/workflow/compiler_yaml.go |
Switches workflow body generation to the new direct-write jobs API and retunes builder sizing/newline writes. |
pkg/workflow/compiler_yaml_ai_execution.go |
Removes temporary string concatenation in AI execution step emission loops. |
pkg/workflow/compiler_yaml_main_job.go |
Applies the same newline-write optimization across several main-job step generation loops. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 4/4 changed files
- Comments generated: 3
| // 64 KB covers the common simple-workflow case without a reallocation; | ||
| // larger workflows will trigger a single grow (to ~128 KB) which is | ||
| // acceptable given the reduction in baseline allocation compared to the | ||
| // previous 96 KB constant. | ||
| const initialBuilderCapacity = 64 * 1024 |
|
|
||
| // RenderToYAML generates the jobs section of a GitHub Actions workflow. | ||
| // Prefer WriteJobsYAML when an existing *strings.Builder is available to avoid | ||
| // an extra allocation. | ||
| func (jm *JobManager) RenderToYAML() string { | ||
| var yaml strings.Builder |
| // Generate jobs section using JobManager — write directly to avoid an | ||
| // intermediate string allocation. | ||
| c.jobManager.WriteJobsYAML(yaml) |
|
@copilot review all comments |
…AML, add WriteJobsYAML test - Restore initialBuilderCapacity to 96KB in generateYAML to avoid regressions on Copilot/Claude workflows (70-90KB). The perf win is from eliminating copies, not from reducing capacity. - Pre-size the RenderToYAML compatibility builder with Grow(64KB) so existing callers don't regress vs the old pre-sized buffer. - Add TestJobManager_WriteJobsYAML with three table cases that verify WriteJobsYAML appends correctly to an already-populated builder and that output matches RenderToYAML(). Agent-Logs-Url: https://github.com/github/gh-aw/sessions/6befe686-4a16-4b17-877f-0b92cceca11e Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Addressed all three review points in commit
Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
CompileSimpleWorkflowregressed 14% (1.47ms → 1.67ms) due to excessive intermediatestrings.Builderallocations in the YAML generation path, driving GC to consume ~30% of CPU time.Root cause
Three compounding allocation sources per compile:
RenderToYAML()internal builderrenderJob()per-job buffer × ~5 jobsgenerateYAMLouteryaml+bodybuildersWriteString(line + "\n")in hot loopsThe
RenderToYAMLandrenderJobpattern built strings into their own builders then copied the result into the caller's builder — paying both the allocation and the copy.Changes
jobs.go— AddWriteJobsYAML(*strings.Builder)andrenderJobTo(*strings.Builder, *Job)that write directly to the caller's builder.RenderToYAML()/renderJob()become thin wrappers for existing callers;RenderToYAMLis pre-sized withGrow(64 KB)so its compatibility builder avoids regrowth on typical outputs.compiler_yaml.go— UseWriteJobsYAMLingenerateWorkflowBody; retain 96 KB initial capacity foryaml/bodybuilders (covers 70–90 KB Copilot/Claude workflows without reallocation); replaces + "\n\n"concatenations with twoWriteStringcalls.compiler_yaml_ai_execution.go,compiler_yaml_main_job.go— ReplaceWriteString(line + "\n")hot-loop pattern withWriteString(line)+WriteByte('\n').jobs_test.go— AddTestJobManager_WriteJobsYAMLwith three table-driven cases that verifyWriteJobsYAMLappends correctly to an already-populated builder and that its output matchesRenderToYAML().Results
Lands ~13% below the historical baseline (1.47 ms).