From 01763b9d2448e347a2ef36debf46ee1bab96ab83 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 May 2026 15:25:41 +0000 Subject: [PATCH 1/3] Initial plan From 76383490480a73a67bba4685a0617613567b604f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 May 2026 15:38:32 +0000 Subject: [PATCH 2/3] perf: fix FindIncludesInContent regression - fast-byte check + single-scan fast path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three targeted optimizations to recover the 17.9% regression: 1. hasDirectiveMarker: single-scan helper using IndexByte (SIMD) to find '@' or '{', avoiding three separate Contains passes for the no-include fast path. Content is now traversed at most once before the line scan. 2. parseIncludePath: first-byte guard skips all three HasPrefix comparisons for the overwhelming majority of regular-text lines that don't start with '@' or '{'. 3. findIncludesInContent: manual IndexByte('\n') loop replaces strings.Lines (iter.Seq), eliminating the yield-function call overhead per line and stripping the trailing '\n' before parseIncludePath sees it. Benchmark (5 s, AMD EPYC): Before: 194 ns/op → After: ~174 ns/op (~10% improvement) Agent-Logs-Url: https://github.com/github/gh-aw/sessions/7cb7b62b-a88d-4dc4-b5ed-0437bae12238 Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/cli/remove_command.go | 58 +++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/pkg/cli/remove_command.go b/pkg/cli/remove_command.go index 14a08b3391b..2f6681a5a77 100644 --- a/pkg/cli/remove_command.go +++ b/pkg/cli/remove_command.go @@ -386,20 +386,62 @@ func cleanupAllIncludes(verbose bool) error { return err } +// hasDirectiveMarker reports whether content contains any @include, @import, or {{#import +// directive marker using a single forward scan, so the content is traversed at most once. +func hasDirectiveMarker(content string) bool { + for i := 0; i < len(content); { + // Find next '@' or '{'—both are rare in workflow prose, so IndexByte skips + // large stretches of uninteresting bytes via SIMD on supported platforms. + atIdx := strings.IndexByte(content[i:], '@') + braceIdx := strings.IndexByte(content[i:], '{') + if atIdx < 0 && braceIdx < 0 { + return false + } + if atIdx >= 0 && (braceIdx < 0 || atIdx <= braceIdx) { + pos := i + atIdx + rest := content[pos:] + if strings.HasPrefix(rest, "@include") || strings.HasPrefix(rest, "@import") { + return true + } + i = pos + 1 + } else { + pos := i + braceIdx + if strings.HasPrefix(content[pos:], "{{#import") { + return true + } + i = pos + 1 + } + } + return false +} + // findIncludesInContent finds all import references in content func findIncludesInContent(content, baseDir string, verbose bool) ([]string, error) { _ = baseDir // unused parameter for now, keeping for potential future use _ = verbose // unused parameter for now, keeping for potential future use - var includes []string - for line := range strings.Lines(content) { - path := parseIncludePath(line) - if path != "" { + // Fast path: skip the line scan entirely when no directive markers are present. + if !hasDirectiveMarker(content) { + return []string{}, nil + } + + var includes []string + // Manual index-based scan avoids the iter.Seq yield overhead of strings.Lines. + for remaining := content; remaining != ""; { + var line string + if idx := strings.IndexByte(remaining, '\n'); idx >= 0 { + line = remaining[:idx] + remaining = remaining[idx+1:] + } else { + line = remaining + remaining = "" + } + if path := parseIncludePath(line); path != "" { includes = append(includes, path) } } - return includes, nil // strings.Lines iterates over an in-memory string; no I/O errors can occur. + return includes, nil } // parseIncludePath extracts the file path from @include/@import/{{#import}} directive lines @@ -412,6 +454,12 @@ func parseIncludePath(line string) string { return "" } + // Fast path: the vast majority of lines are not directives. + // Checking the first byte avoids three full HasPrefix comparisons. + if trimmed[0] != '@' && trimmed[0] != '{' { + return "" + } + var rest string switch { From b1ae17066ae7a6bfb5d06b98e822c43a2731f72f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 3 May 2026 16:05:21 +0000 Subject: [PATCH 3/3] perf: replace dual IndexByte with single IndexAny in hasDirectiveMarker Fixes the double-scan issue flagged in code review: the previous implementation called strings.IndexByte twice per loop iteration (once for '@' and once for '{'), scanning content twice end-to-end for the no-marker case. Using strings.IndexAny("@{") finds the next candidate byte in a single pass, ensuring content is traversed at most once. Agent-Logs-Url: https://github.com/github/gh-aw/sessions/1cd5dff8-a199-4c49-88ec-f11cbd94064b Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/remove_command.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/pkg/cli/remove_command.go b/pkg/cli/remove_command.go index 2f6681a5a77..d88b2b58677 100644 --- a/pkg/cli/remove_command.go +++ b/pkg/cli/remove_command.go @@ -387,30 +387,28 @@ func cleanupAllIncludes(verbose bool) error { } // hasDirectiveMarker reports whether content contains any @include, @import, or {{#import -// directive marker using a single forward scan, so the content is traversed at most once. +// directive marker using a single forward scan of the content. func hasDirectiveMarker(content string) bool { for i := 0; i < len(content); { - // Find next '@' or '{'—both are rare in workflow prose, so IndexByte skips - // large stretches of uninteresting bytes via SIMD on supported platforms. - atIdx := strings.IndexByte(content[i:], '@') - braceIdx := strings.IndexByte(content[i:], '{') - if atIdx < 0 && braceIdx < 0 { + // A single IndexAny call locates the next '@' or '{' in one pass, so + // the content is traversed at most once regardless of which byte appears first. + idx := strings.IndexAny(content[i:], "@{") + if idx < 0 { return false } - if atIdx >= 0 && (braceIdx < 0 || atIdx <= braceIdx) { - pos := i + atIdx - rest := content[pos:] + pos := i + idx + rest := content[pos:] + switch rest[0] { + case '@': if strings.HasPrefix(rest, "@include") || strings.HasPrefix(rest, "@import") { return true } - i = pos + 1 - } else { - pos := i + braceIdx - if strings.HasPrefix(content[pos:], "{{#import") { + case '{': + if strings.HasPrefix(rest, "{{#import") { return true } - i = pos + 1 } + i = pos + 1 } return false }