perf(git): parallelize git segment shell-outs to reduce prompt latency#7422
Open
mroth wants to merge 1 commit intoJanDeDobbeleer:mainfrom
Open
perf(git): parallelize git segment shell-outs to reduce prompt latency#7422mroth wants to merge 1 commit intoJanDeDobbeleer:mainfrom
mroth wants to merge 1 commit intoJanDeDobbeleer:mainfrom
Conversation
The git segment shells out to git multiple times sequentially during Enabled(). Each invocation pays process-spawn and I/O overhead, making the total latency additive. With all features enabled, the prior flow was: 1. setUser() → git config user.name, git config user.email 2. setStatus() → git status --porcelain=2 3. setHEADStatus() → git rev-parse, git describe (conditional) 4. setBranchStatus() → (no git calls) 5. setPushStatus() → git rev-list ahead, git rev-list behind 6. getUpstreamIcon() → git remote get-url (fallback only) That's 5-7 sequential git invocations in the common/worst case. Restructure Enabled() to run independent git commands concurrently, reducing the critical path to 2 serial rounds: - Phase 1: setUser() runs concurrently alongside setStatus() - Phase 2: setHEADStatus(), setPushStatus(), and getUpstreamIcon() fan out in parallel after setStatus() completes - setPushStatus() internally parallelizes its two rev-list calls - setUser() consolidated from 2 git config calls to 1 via --get-regexp Each goroutine writes to distinct fields on the Git struct, so no synchronization beyond WaitGroup is needed. All goroutines are joined before Enabled() returns. Measured improvement on Apple M4 Pro / 2TB SSD (median git segment duration, all features enabled): - Small repo: 34ms → 19ms (44% faster) - With 50ms synthetic latency per call: 394ms → 156ms (60% faster) - Linux kernel (93k files): 320ms → 302ms (6% faster) - Kubernetes (25k files): 303ms → 286ms (6% faster) Generated with assistance from Claude Code (claude-opus-4-6), but all code manually reviewed.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR reduces git segment prompt latency by restructuring Git.Enabled() to execute independent git shell-outs concurrently instead of serially, and by consolidating user config lookups into a single git invocation.
Changes:
- Parallelize
setUser()with status collection and fan out additional git-dependent work aftersetStatus(). - Update
setUser()to usegit config --get-regexpinstead of two separategit configcalls. - Parallelize the ahead/behind
rev-list --countcalls insidesetPushStatus().
Owner
|
Love this, great addition! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prerequisites
Description
Restructure
Enabled()to run independent git commands concurrently instead of sequentially, reducing the critical path from 5-7 serial git invocations down to 2 rounds.Problem
With all features enabled (
fetch_status,fetch_user,fetch_push_status,fetch_upstream_icon), the git segment executes up to 7 sequential git shell-outs:setUser()→git config user.name,git config user.emailsetStatus()→git status --porcelain=2setHEADStatus()→git rev-parse,git describe(conditional)setBranchStatus()→ (no git calls)setPushStatus()→git rev-listahead,git rev-listbehindgetUpstreamIcon()→git remote get-url(fallback only)Changes
setUser()runs concurrently alongsidesetStatus()viawg.GosetStatus()completes,setHEADStatus(),setPushStatus(), andgetUpstreamIcon()fan out in parallelsetPushStatus()internally parallelizes its tworev-listcallssetUser()consolidated from 2git configcalls to 1 via--get-regexpEach goroutine writes to distinct fields on the
Gitstruct — no synchronization beyondWaitGroupis needed. All goroutines are joined beforeEnabled()returns.Benchmark results
Measured on Apple M4 Pro / 2TB SSD (median git segment duration, all features enabled):
The improvement is largest when per-call overhead is significant relative to
git statustime (small/medium repos, slow filesystems). On very large repos wheregit statusdominates, the absolute savings are ~17-20ms.Testing
go test "./...")go test -race)golangci-lintcleanGenerated with assistance from Claude Code (claude-opus-4-6), but all code manually reviewed.