Skip to content

Conversation

@MaxFangX
Copy link
Contributor

@MaxFangX MaxFangX commented Nov 5, 2025

Builds on top of #422 and #423.

Description

Fixes performance degradation during staging operations by suppressing redundant VGitSync broadcasts. Achieves 99.6% reduction in buffer refresh calls (1,380 -> 6 LiveGutter:fetch calls) and 95% reduction in total git operations (10,421 -> 564 calls across 41 staging operations).

When staging hunks, every git index change triggered a cascade: filesystem watcher detects change -> VGitSync event fires -> git_buffer_store.for_each() iterates all tracked buffers -> each buffer runs LiveGutter:fetch() -> GitBuffer:diff() -> 3-4 git commands per buffer. With 15 tracked buffers, this meant 15x unnecessary buffer refreshes per stage operation, resulting in thousands of redundant git command calls.

This adds suppress_sync_and_refresh(buffer, ms) API to git_buffer_store that temporarily suppresses VGitSync broadcasts and refreshes only the specified buffer after a delay. Applied to all DiffScreen staging methods (stage_hunk, unstage_hunk, reset_hunk, stage, unstage, reset) and Hunks buffer operations (stage_all, cursor_stage).

Note: External git operations during the suppression window (~200ms) won't trigger a refresh until the next VGitSync event — an acceptable tradeoff given the short window.

Tested with 15 tracked buffers over 41 staging operations. No functional regressions observed - all git operations complete successfully, gutter signs update correctly, and UI remains responsive.

Performance Summary

Improvement:

  • Total calls: 95% reduction (10,421 -> 564)
  • LiveGutter:fetch: 99.6% reduction (1,380 -> 6)
  • GitBuffer:diff: 99.6% reduction (1,381 -> 6)
  • git ls-files: 94% reduction (1,463 -> 87)

Profiling Details

Environment: 15 tracked buffers, ~40 stage operations (DiffScreen:stage_hunk)

Before:

Unique operations: 36
Total calls: 10,421

156054ms total (1380x, 113ms avg) - vgit.features.buffer.LiveGutter:fetch
102827ms total (1381x, 74ms avg) - vgit.git.GitBuffer:diff
69420ms total (1838x, 38ms avg) - vgit.git.git_buffer_store.dispatch
41300ms total (1463x, 28ms avg) - gitcli.run: git ls-files
20863ms total (768x, 27ms avg) - gitcli.run: git ls-files -u
18357ms total (468x, 39ms avg) - gitcli.run: git show
11166ms total (39x, 286ms avg) - vgit.features.screens.DiffScreen:stage_hunk

After:

Unique operations: 21
Total calls: 564

6502ms total (41x, 159ms avg) - vgit.features.screens.DiffScreen:stage_hunk
1410ms total (87x, 16ms avg) - gitcli.run: git ls-files
723ms total (38x, 19ms avg) - vgit.git.git_stager.stage_hunk
339ms total (6x, 57ms avg) - vgit.git.git_buffer_store.dispatch
242ms total (6x, 40ms avg) - vgit.features.buffer.LiveGutter:fetch
241ms total (6x, 40ms avg) - vgit.git.GitBuffer:diff

@MaxFangX MaxFangX force-pushed the 2025-11-05-suppress-unneeded-vgitsync branch 2 times, most recently from 33c16f2 to 8cf1214 Compare November 5, 2025 18:30
@luisdavim
Copy link

This seems like a great improvement, any reason not to merge it?

@MaxFangX MaxFangX force-pushed the 2025-11-05-suppress-unneeded-vgitsync branch from 8cf1214 to a3925e7 Compare December 3, 2025 01:55
@MaxFangX MaxFangX changed the title fix(multi): Suppress VGitSync during staging; 95%+ performance improvement fix: Suppress VGitSync during staging; 95%+ performance improvement Dec 3, 2025
@MaxFangX
Copy link
Contributor Author

MaxFangX commented Dec 3, 2025

This seems like a great improvement, any reason not to merge it?

I think it was because there were merge conflicts after #422 and #423 were merged (which this PR built on top of).

Rebased.

Adds suppress_sync_and_refresh() API to git_buffer_store. Instead of
refreshing all tracked buffers on git index changes, this suppresses the
VGitSync broadcast and refreshes only the affected buffer after a delay.

Applied in DiffScreen (stage/unstage/reset operations) and Hunks
(stage_all, cursor_stage).

Performance from local profiling:
95% reduction in calls (10,421 → 564 per session staging 39 hunks),
99.6% reduction in LiveGutter:fetch (1,380 → 6).
@MaxFangX MaxFangX force-pushed the 2025-11-05-suppress-unneeded-vgitsync branch from a3925e7 to 291a030 Compare December 3, 2025 02:43
@MaxFangX
Copy link
Contributor Author

MaxFangX commented Dec 3, 2025

Pushed a change which does the same thing with a different approach: suppress_sync_and_refresh. Instead of having to remember to manually refresh that buffer, we require it in the params and handle the buffer refresh after a timeout. The VGitSync suppression behavior is still intact, preventing our staging operations from triggering a refresh of all buffers. I also applied the suppression to Hunks buffer operations outside of DiffScreen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants