Skip to content

perf: eliminate strings.Split allocation in validateExpressionSizes#45282

Closed
pelikhan with Copilot wants to merge 5 commits into
mainfrom
copilot/performance-regression-fix
Closed

perf: eliminate strings.Split allocation in validateExpressionSizes#45282
pelikhan with Copilot wants to merge 5 commits into
mainfrom
copilot/performance-regression-fix

Conversation

Copilot AI commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

validateExpressionSizes was calling strings.Split(yamlContent, "\n") on every compilation, allocating a ~21KB []string slice for the compiled YAML and then scanning it twice (per-line length check + block-scalar check).

Changes

  • runtime_validation.go: Replaces the two-pass approach with validateExpressionSizesSinglePass, a single strings.SplitSeq pass that combines per-line and block-scalar validation, eliminating the slice allocation entirely.
// Before: allocates 1273-element []string, then loops twice
lines := strings.Split(yamlContent, "\n")
for lineNum, line := range lines { /* per-line check */ }
return validateBlockScalarExpressionSizes(lines, maxSize)

// After: one pass, zero slice allocation
for line := range strings.SplitSeq(yamlContent, "\n") { /* both checks */ }
  • validateBlockScalarExpressionSizes([]string, int) is preserved unchanged for its unit tests.

Benchmark (benchstat, 6+ runs)

metric change p-value
B/op −2.10% 0.004
allocs/op −0.05% 0.043
sec/op −1.2% 0.177 (ns)

Memory reduction is statistically significant; timing is within noise. A walkRunBlockLines fast-path was also evaluated (adding an early strings.Contains skip for non-run-block lines) but reverted after profiling showed the Contains overhead on ~913 lines/compilation exceeded the savings from skipping TrimLeft+findRunValue.

Copilot AI and others added 2 commits July 13, 2026 16:26
Replace the two-pass approach (strings.Split + per-line loop +
validateBlockScalarExpressionSizes loop) with a single strings.SplitSeq
pass in validateExpressionSizesSinglePass. This removes the 1273-element
[]string allocation (~21KB/op) that strings.Split was creating for each
compiled workflow YAML.

Benchstat with 6+ runs shows a statistically significant -2.10% reduction
in B/op (p=0.004) with no regression in throughput (p=0.177).

The separate validateBlockScalarExpressionSizes([]string, int) function is
preserved for backward compatibility with its unit tests.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix regression in CompileSimpleWorkflow performance perf: eliminate strings.Split allocation in validateExpressionSizes Jul 13, 2026
Copilot AI requested a review from pelikhan July 13, 2026 16:31
@github-actions

Copy link
Copy Markdown
Contributor

PR Triage (run 29275650494)\n\nCategory: perf | Risk: low | Score: 45/100 | Action: batch_review | Batch: draft-quality\n\nScore: Impact 20/50 + Urgency 12/30 + Quality 13/20\n\nEliminates strings.Split allocation in validateExpressionSizes via SplitSeq. Single file, +83/-24. DRAFT. Can be reviewed alongside #45280 and #45298.

Generated by 🔧 PR Triage Agent · 181.5 AIC · ⌖ 7.71 AIC · ⊞ 5.6K ·

Copilot AI 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.

Pull request overview

Optimizes expression-size validation by replacing an allocating, two-pass scan with a single strings.SplitSeq pass.

Changes:

  • Combines per-line and block-scalar validation.
  • Avoids allocating a YAML line slice.
  • Retains the legacy block-scalar helper for existing tests.
Show a summary per file
File Description
pkg/workflow/runtime_validation.go Implements single-pass expression-size validation.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment thread pkg/workflow/runtime_validation.go
Comment on lines +149 to +152
if colonIdx := strings.Index(trimmed, ":"); colonIdx > 0 {
afterColon := strings.TrimSpace(trimmed[colonIdx+1:])
if afterColon == "|" || afterColon == ">" || strings.HasPrefix(afterColon, "|-") || strings.HasPrefix(afterColon, ">-") {
inBlock = true

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in commit 40d9367: extracted isBlockScalarHeader that covers all YAML-spec block scalar variants — |, >, |-, >-, |+, >+, |2|9, |2-, |-2, >+3, etc. Both validateExpressionSizesSinglePass and validateBlockScalarExpressionSizes now use it. New test cases verify the previously-missed chomping/indent-indicator variants are caught.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address any unresolved review feedback, and rerun checks once the branch is up to date.

Generated by 👨‍🍳 PR Sous Chef · 6.38 AIC · ⌖ 9.66 AIC · ⊞ 4.2K ·
Comment /souschef to run again

Copilot AI and others added 2 commits July 13, 2026 22:20
- Add isBlockScalarHeader helper to cover |+, >+, |2, |2-, |-2 etc.
  in both validateExpressionSizesSinglePass and
  validateBlockScalarExpressionSizes (previously only |- and >- were
  caught; keep-chomping and explicit-indent indicators were silently
  missed, letting oversized expression blocks bypass validation)
- Add TestValidateExpressionSizesSinglePass with parity cases against
  the legacy helper plus per-line size check and EOF-block coverage
- Add chomping/indent-indicator cases to TestValidateBlockScalarExpressionSizes

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address any unresolved review feedback, and rerun checks once the branch is up to date.

...

Done. Two issues addressed in commits fix: extend block-scalar header detection and fix: correct test name:

Thread 1 (line 77 — missing tests for validateExpressionSizesSinglePass): Added TestValidateExpressionSizesSinglePass in block_scalar_expression_size_test.go with full parity coverage against the legacy helper, plus two cases unique to the single-pass path: a per-line oversized value check and an EOF-open-block check.

Thread 2 (lines 149-152 — chomping/indent indicators missed): Extracted an isBlockScalarHeader helper that recognises all YAML-spec block scalar header variants (|, >, |-, >-, |+, >+, |2|9, |2-, |-2, >+3, etc.). Both validateExpressionSizesSinglePass and validateBlockScalarExpressionSizes now use it. New test cases (three for each function) confirm the previously-missed indicators are caught.

All make fmt, make lint, and make test-unit pass locally.

Copilot AI requested a review from gh-aw-bot July 13, 2026 22:21
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

pr-sous-chef
@copilot please run the pr-finisher skill, address these unresolved review comments, and rerun checks once the branch is up to date:

Run: https://github.com/github/gh-aw/actions/runs/29289965782

Generated by 👨‍🍳 PR Sous Chef · 10.9 AIC · ⌖ 3.94 AIC · ⊞ 7.5K ·
Comment /souschef to run again

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.

[performance] Regression in CompileSimpleWorkflow: 28.6% slower

4 participants