Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Faster refresh #2841

Merged
merged 9 commits into from
Jul 29, 2023
Merged

Faster refresh #2841

merged 9 commits into from
Jul 29, 2023

Conversation

jesseduffield
Copy link
Owner

@jesseduffield jesseduffield commented Jul 28, 2023

This PR makes various changes to speed up refreshes. Notably we're using more concurrency with waitgroups, and in some places we're avoiding nested loops.

I've got a synchronous refresh of all models (in the lazygit repo) down from 250ms to 100ms. You'll notice for example that switching branches is faster.

Jul 29 09:49:48 |INFO| refreshing all scopes in sync mode
Jul 29 09:49:48 |INFO| refreshed staging in 36.752µs
Jul 29 09:49:48 |INFO| refreshed merge conflicts in 26.978µs
Jul 29 09:49:49 |INFO| git -c log.showSignature=false log -g --abbrev=40 --format=%h%x00%ct%x00%gs%x00%p (21.220731ms)
Jul 29 09:49:49 |INFO| git tag --list -n --sort=-creatordate (21.255125ms)
Jul 29 09:49:49 |INFO| git merge-base HEAD HEAD@{u} (21.504375ms)
Jul 29 09:49:49 |INFO| git merge-base HEAD refs/remotes/origin/master (24.661184ms)
Jul 29 09:49:49 |INFO| postRefreshUpdate for tags took 4.425477ms
Jul 29 09:49:49 |INFO| refreshed tags in 27.256406ms
Jul 29 09:49:49 |INFO| git status --untracked-files=all --porcelain -z (37.026009ms)
Jul 29 09:49:49 |INFO| refreshed files in 37.616105ms
Jul 29 09:49:49 |INFO| git log HEAD --topo-order --oneline --pretty=format:%H%x00%at%x00%aN%x00%ae%x00%D%x00%p%x00%s --abbrev=40 -300 --no-show-signature -- (43.570288ms)
Jul 29 09:49:49 |INFO| git stash list -z --pretty=%gs (43.940535ms)
Jul 29 09:49:49 |INFO| postRefreshUpdate for stash took 2.322359ms
Jul 29 09:49:49 |INFO| refreshed stash in 46.637992ms
Jul 29 09:49:49 |INFO| git branch -r (49.167657ms)
Jul 29 09:49:49 |INFO| postRefreshUpdate for remotes took 1.063829ms
Jul 29 09:49:49 |INFO| postRefreshUpdate for remoteBranches took 10.219µs
Jul 29 09:49:49 |INFO| refreshed remotes in 51.020293ms
Jul 29 09:49:49 |INFO| postRefreshUpdate for commits took 7.805133ms
Jul 29 09:49:49 |INFO| refreshed commits and commit files in 51.970354ms
Jul 29 09:49:49 |INFO| postRefreshUpdate for reflogCommits took 52.564081ms
Jul 29 09:49:49 |INFO| git for-each-ref --sort=-committerdate --format=%(HEAD)%00%(refname:short)%00%(upstream:short)%00%(upstream:track)%00%(subject)%00%(objectname:short=8) refs/heads (15.701524ms)
Jul 29 09:49:49 |INFO| postRefreshUpdate for localBranches took 839.021µs
Jul 29 09:49:49 |INFO| refreshed reflog and branches in 94.863103ms
Jul 29 09:49:49 |INFO| Refresh took 95.125968ms
Jul 29 09:49:49 |INFO| postRefreshUpdate for submodules took 20.714µs
Jul 29 09:49:49 |INFO| postRefreshUpdate for files took 171.282µs

The next big opportunity for speeding things up is in how we write content to views. Right now we write all of our list to the list view, and if we have lots of items in our list, it takes longer. Notice that refreshing the reflog and branches took 94ms which is roughly the total refresh time. Within that, it takes 52ms to write the reflog commits to the reflog view. So that's the current bottleneck. If reflog commits write to the view instantly then the total refresh time goes down to ~50ms. The solution is to only write enough to the view to fill the viewport, and then refresh the viewport as we scroll.

If anybody wants to look into performance in future, I tested this all locally by changing the refresh that happens upon pressing shift+R to be synchronous (meaning it waits for all refreshes to be done before returning)

@@ -128,7 +128,7 @@ func (self *GlobalController) createCustomPatchOptionsMenu() error {
 }
 
 func (self *GlobalController) refresh() error {
-	return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
+	return self.c.Refresh(types.RefreshOptions{Mode: types.SYNC})
 }
 
 func (self *GlobalController) nextScreenMode() error {

Then I ran lazygit with LOG_LEVEL=info lazygit -debug and in another terminal pane I viewed the logs with lazygit --logs.

  • Please check if the PR fulfills these requirements
  • Cheatsheets are up-to-date (run go run scripts/cheatsheet/main.go generate)
  • Code has been formatted (see here)
  • Tests have been added/updated (see here for the integration test guide)
  • Text is internationalised (see here)
  • Docs (specifically docs/Config.md) have been updated if necessary
  • You've read through your own file changes for silly mistakes etc

This will help us diagnose performance issues
Notably, the reflog view is taking ages here because it's got a
few thousand lines to write to the view.

In future we should only populate the view's viewport.
The speedup is most noticeable on first load, when we haven't yet fetched out main branches.
I saw a speedup from 105ms to 60ms. On subsequent loads the gain is more modest;
54ms to 40ms
We're:
* using concurrency with wait groups
* avoiding regex
* processing lines of input as they come rather than storing everything in one string
* avoiding an inner loop by creating a mapping of remote names to branches
No functional changes
Previously our synchronous refreshes took far longer because nothing
was happening concurrently. We now run refresh functions concurrently
and use a wait group to ensure they're all done before returning
// whenever we change commits, we should update branches because the upstream/downstream
// counts can change. Whenever we change branches we should probably also change commits
// e.g. in the case of switching branches.
func (self *RefreshHelper) refreshCommits() {
Copy link
Owner Author

Choose a reason for hiding this comment

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

no need for a nested wait group so I've split this function into two

@jesseduffield jesseduffield added the enhancement New feature or request label Jul 29, 2023
Now that we run code concurrently in our loaders, we need to handle that in our tests.
We could enforce a deterministic ordering by mocking waitgroup or something like that,
but I think it's fine to let our tests handle some randomness given that prod itself
will have that randomness.

I've removed the patch test file because it was clunky, not providing much value, and
it would have been hard to refactor to the new pattern
@jesseduffield jesseduffield merged commit 2f01161 into master Jul 29, 2023
12 checks passed
@jesseduffield jesseduffield deleted the faster-loading branch July 29, 2023 02:39
renovate bot referenced this pull request in scottames/dots Aug 5, 2023
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[GoogleContainerTools/skaffold](https://togithub.com/GoogleContainerTools/skaffold)
| patch | `v2.6.2` -> `v2.6.3` |
| [ajeetdsouza/zoxide](https://togithub.com/ajeetdsouza/zoxide) | patch
| `v0.9.1` -> `v0.9.2` |
| [aquaproj/aqua-registry](https://togithub.com/aquaproj/aqua-registry)
| patch | `v4.32.0` -> `v4.32.2` |
| [jesseduffield/lazygit](https://togithub.com/jesseduffield/lazygit) |
minor | `v0.39.4` -> `v0.40.0` |
| [weaveworks/eksctl](https://togithub.com/weaveworks/eksctl) | minor |
`v0.150.0` -> `v0.151.0` |

---

### Release Notes

<details>
<summary>GoogleContainerTools/skaffold
(GoogleContainerTools/skaffold)</summary>

###
[`v2.6.3`](https://togithub.com/GoogleContainerTools/skaffold/releases/tag/v2.6.3):
Release

[Compare
Source](https://togithub.com/GoogleContainerTools/skaffold/compare/v2.6.2...v2.6.3)

##### v2.6.3 Release - 2023-08-04

**Linux amd64**
`curl -Lo skaffold
https://storage.googleapis.com/skaffold/releases/v2.6.3/skaffold-linux-amd64
&& chmod +x skaffold && sudo mv skaffold /usr/local/bin`

**Linux arm64**
`curl -Lo skaffold
https://storage.googleapis.com/skaffold/releases/v2.6.3/skaffold-linux-arm64
&& chmod +x skaffold && sudo mv skaffold /usr/local/bin`

**macOS amd64**
`curl -Lo skaffold
https://storage.googleapis.com/skaffold/releases/v2.6.3/skaffold-darwin-amd64
&& chmod +x skaffold && sudo mv skaffold /usr/local/bin`

**macOS arm64**
`curl -Lo skaffold
https://storage.googleapis.com/skaffold/releases/v2.6.3/skaffold-darwin-arm64
&& chmod +x skaffold && sudo mv skaffold /usr/local/bin`

**Windows**

https://storage.googleapis.com/skaffold/releases/v2.6.3/skaffold-windows-amd64.exe

**Docker image**
`gcr.io/k8s-skaffold/skaffold:v2.6.3`

**Full Changelog**:
GoogleContainerTools/skaffold@v2.6.2...v2.6.3

</details>

<details>
<summary>ajeetdsouza/zoxide (ajeetdsouza/zoxide)</summary>

###
[`v0.9.2`](https://togithub.com/ajeetdsouza/zoxide/releases/tag/v0.9.2):
0.9.2

[Compare
Source](https://togithub.com/ajeetdsouza/zoxide/compare/v0.9.1...v0.9.2)

##### Added

-   Short option `-a` for `zoxide query --all`.

##### Fixed

-   PowerShell: use `global` scope for variables / functions.

</details>

<details>
<summary>aquaproj/aqua-registry (aquaproj/aqua-registry)</summary>

###
[`v4.32.2`](https://togithub.com/aquaproj/aqua-registry/releases/tag/v4.32.2)

[Compare
Source](https://togithub.com/aquaproj/aqua-registry/compare/v4.32.1...v4.32.2)


[Issues](https://togithub.com/aquaproj/aqua-registry/issues?q=is%3Aissue+milestone%3Av4.32.2)
| [Pull
Requests](https://togithub.com/aquaproj/aqua-registry/pulls?q=is%3Apr+milestone%3Av4.32.2)
| aquaproj/aqua-registry@v4.32.1...v4.32.2

##### Fixes


[#&#8203;14327](https://togithub.com/aquaproj/aqua-registry/issues/14327)
Rename kyleconroy/sqlc to sqlc-dev/sqlc as of repository migration
[@&#8203;ichizero](https://togithub.com/ichizero)

[#&#8203;14339](https://togithub.com/aquaproj/aqua-registry/issues/14339)
sqlc-dev/sqlc: Support old versions

###
[`v4.32.1`](https://togithub.com/aquaproj/aqua-registry/releases/tag/v4.32.1)

[Compare
Source](https://togithub.com/aquaproj/aqua-registry/compare/v4.32.0...v4.32.1)


[Issues](https://togithub.com/aquaproj/aqua-registry/issues?q=is%3Aissue+milestone%3Av4.32.1)
| [Pull
Requests](https://togithub.com/aquaproj/aqua-registry/pulls?q=is%3Apr+milestone%3Av4.32.1)
| aquaproj/aqua-registry@v4.32.0...v4.32.1

#### Fixes


[#&#8203;14275](https://togithub.com/aquaproj/aqua-registry/issues/14275)
[#&#8203;14276](https://togithub.com/aquaproj/aqua-registry/issues/14276)
[#&#8203;14277](https://togithub.com/aquaproj/aqua-registry/issues/14277)
[#&#8203;14278](https://togithub.com/aquaproj/aqua-registry/issues/14278)
[domoritz/arrow-tools/{csv2arrow,csv2parquet,json2arrow,json2parquet}](https://togithub.com/domoritz/arrow-tools):
Follow up changes of asset names

</details>

<details>
<summary>jesseduffield/lazygit (jesseduffield/lazygit)</summary>

###
[`v0.40.0`](https://togithub.com/jesseduffield/lazygit/releases/tag/v0.40.0)

[Compare
Source](https://togithub.com/jesseduffield/lazygit/compare/v0.39.4...v0.40.0)

<!-- Release notes generated using configuration in .github/release.yml
at v0.40.0 -->

### 🎉  LAZYGIT FIVE YEAR ANNIVERSARY EDITION 🎉

Holy moly, has it really been 5 years since Lazygit's birth? Time flies
when you're having fun.

I've written a post celebrating the anniversary
[here](https://jesseduffield.com/Lazygit-5-Years-On).

As for this release, we've got some great features here.

##### Worktrees

We now have a worktrees view so you can easily create worktrees and
switch to them and so on. I'm not a big worktrees user myself so please
raise an issue if you can think of places to improve the UX.


![worktree_create_from_branches-compressed](https://togithub.com/jesseduffield/lazygit/assets/8456633/3ef0b085-e9d0-42de-af58-16cbae581d34)

##### Rebase --onto

Rebasing onto a marked base commit is a very useful feature that we've
been sorely lacking for a while
(demo coming soon)

##### Auto-refresh on window focus

Auto-refresh on window activation is a complete game-changer. No more
having to manually press shift+R when you come back from your editor.

##### Nuking the worktree

We also have a fun enhancement in this release: showing an explosion
animation when you nuke the working tree.


![nuke-gif](https://togithub.com/jesseduffield/lazygit/assets/8456633/32b3f91c-fea3-474d-8997-1de2f5e4f5d4)

You'll also notice in the readme we've got some updated demo gifs to
showoff Lazygit's features. More of those to come.

#### What's Changed

##### Features ✨

- Add worktrees view by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) (with help
from [@&#8203;kadaan](https://togithub.com/kadaan)) in
[https://github.com/jesseduffield/lazygit/pull/2147](https://togithub.com/jesseduffield/lazygit/pull/2147)
- Rebase onto branch from a marked base commit by
[@&#8203;stefanhaller](https://togithub.com/stefanhaller) in
[https://github.com/jesseduffield/lazygit/pull/2835](https://togithub.com/jesseduffield/lazygit/pull/2835)
- Auto-refresh on window activation by
[@&#8203;stefanhaller](https://togithub.com/stefanhaller) in
[https://github.com/jesseduffield/lazygit/pull/2854](https://togithub.com/jesseduffield/lazygit/pull/2854)

##### Enhancements 🔥

- Faster refresh by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2841](https://togithub.com/jesseduffield/lazygit/pull/2841)
- feat: add os.copyToClipboardCmd to allow for a custom command
[#&#8203;1055](https://togithub.com/jesseduffield/lazygit/issues/1055)
by [@&#8203;redstreet](https://togithub.com/redstreet) in
[https://github.com/jesseduffield/lazygit/pull/2784](https://togithub.com/jesseduffield/lazygit/pull/2784)
- Add bisect menu entry that lets you choose bisect terms by
[@&#8203;stefanhaller](https://togithub.com/stefanhaller) in
[https://github.com/jesseduffield/lazygit/pull/2838](https://togithub.com/jesseduffield/lazygit/pull/2838)
- When bisecting, always mark the current commit as good/bad, not the
selected by [@&#8203;stefanhaller](https://togithub.com/stefanhaller) in
[https://github.com/jesseduffield/lazygit/pull/2837](https://togithub.com/jesseduffield/lazygit/pull/2837)
- Visualize local branch heads in commits panel, 2nd approach by
[@&#8203;stefanhaller](https://togithub.com/stefanhaller) in
[https://github.com/jesseduffield/lazygit/pull/2775](https://togithub.com/jesseduffield/lazygit/pull/2775)
- Allow force-tagging if tag exists by
[@&#8203;stefanhaller](https://togithub.com/stefanhaller) in
[https://github.com/jesseduffield/lazygit/pull/2827](https://togithub.com/jesseduffield/lazygit/pull/2827)
- Save IgnoreWhitespaceInDiffView in state.yml by
[@&#8203;stefanhaller](https://togithub.com/stefanhaller) in
[https://github.com/jesseduffield/lazygit/pull/2830](https://togithub.com/jesseduffield/lazygit/pull/2830)
- Show loader when rebasing by
[@&#8203;KarlHeitmann](https://togithub.com/KarlHeitmann) in
[https://github.com/jesseduffield/lazygit/pull/2851](https://togithub.com/jesseduffield/lazygit/pull/2851)
- Internationalise logging of commands by
[@&#8203;KarlHeitmann](https://togithub.com/KarlHeitmann) in
[https://github.com/jesseduffield/lazygit/pull/2852](https://togithub.com/jesseduffield/lazygit/pull/2852)
- Show visual explosion effect when nuking worktree by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2861](https://togithub.com/jesseduffield/lazygit/pull/2861)

##### Fixes 🔧

- Fix issue where using `null` to un-map a keybinding was ignored by
[@&#8203;hatredholder](https://togithub.com/hatredholder) in
[https://github.com/jesseduffield/lazygit/pull/2832](https://togithub.com/jesseduffield/lazygit/pull/2832)
- Show error when trying to open patch menu with an empty patch by
[@&#8203;stefanhaller](https://togithub.com/stefanhaller) in
[https://github.com/jesseduffield/lazygit/pull/2829](https://togithub.com/jesseduffield/lazygit/pull/2829)
- Fix merge status for update-ref command by
[@&#8203;stefanhaller](https://togithub.com/stefanhaller) in
[https://github.com/jesseduffield/lazygit/pull/2845](https://togithub.com/jesseduffield/lazygit/pull/2845)
- Stop worktrees view from stealing the window by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2863](https://togithub.com/jesseduffield/lazygit/pull/2863)
- Fix confirmation view sizing by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2879](https://togithub.com/jesseduffield/lazygit/pull/2879)

##### Maintenance ⚙️

- Standardise on using lo for slice functions by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2846](https://togithub.com/jesseduffield/lazygit/pull/2846)
- Remove redundant secureexec package by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2847](https://togithub.com/jesseduffield/lazygit/pull/2847)
- Add automated demo recordings by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2853](https://togithub.com/jesseduffield/lazygit/pull/2853)
- Remove file watcher code by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2865](https://togithub.com/jesseduffield/lazygit/pull/2865)
- Add more demos to the README by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2866](https://togithub.com/jesseduffield/lazygit/pull/2866)
- Move features to top of readme by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2867](https://togithub.com/jesseduffield/lazygit/pull/2867)
- Add more demos by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2874](https://togithub.com/jesseduffield/lazygit/pull/2874)

##### Other Changes

- Create demo output dir if it doesn't already exist by
[@&#8203;jesseduffield](https://togithub.com/jesseduffield) in
[https://github.com/jesseduffield/lazygit/pull/2857](https://togithub.com/jesseduffield/lazygit/pull/2857)

#### New Contributors

- [@&#8203;hatredholder](https://togithub.com/hatredholder) made their
first contribution in
[https://github.com/jesseduffield/lazygit/pull/2832](https://togithub.com/jesseduffield/lazygit/pull/2832)
- [@&#8203;redstreet](https://togithub.com/redstreet) made their first
contribution in
[https://github.com/jesseduffield/lazygit/pull/2784](https://togithub.com/jesseduffield/lazygit/pull/2784)
- [@&#8203;kadaan](https://togithub.com/kadaan) made their first
contribution in
[https://github.com/jesseduffield/lazygit/pull/2147](https://togithub.com/jesseduffield/lazygit/pull/2147)
- [@&#8203;KarlHeitmann](https://togithub.com/KarlHeitmann) made their
first contribution in
[https://github.com/jesseduffield/lazygit/pull/2851](https://togithub.com/jesseduffield/lazygit/pull/2851)

**Full Changelog**:
jesseduffield/lazygit@v0.39.4...v0.40.0

</details>

<details>
<summary>weaveworks/eksctl (weaveworks/eksctl)</summary>

###
[`v0.151.0`](https://togithub.com/eksctl-io/eksctl/releases/tag/v0.151.0):
eksctl 0.151.0 (permalink)

[Compare
Source](https://togithub.com/weaveworks/eksctl/compare/0.150.0...0.151.0)

### Release v0.151.0

#### 🚀 Features

- Support custom AMIs for self-managed Windows nodegroups
([#&#8203;6804](https://togithub.com/weaveworks/eksctl/issues/6804))
- Support custom Ubuntu AMIs for EKS-managed nodegroups
([#&#8203;6850](https://togithub.com/weaveworks/eksctl/issues/6850))

#### 🎯 Improvements

- Remove support for EKS 1.22
([#&#8203;6704](https://togithub.com/weaveworks/eksctl/issues/6704))

#### 🐛 Bug Fixes

- Fix error with tar in `Post Cache go-build and mod` step
([#&#8203;6840](https://togithub.com/weaveworks/eksctl/issues/6840))
- Fix setting link-time variables for release version
([#&#8203;6841](https://togithub.com/weaveworks/eksctl/issues/6841))
- Select one subnet for AZs where multiple are present and no VPC config
provided
([#&#8203;6814](https://togithub.com/weaveworks/eksctl/issues/6814))
- Paginate instance type offerings response
([#&#8203;6832](https://togithub.com/weaveworks/eksctl/issues/6832))

#### 🧰 Maintenance

- Bump dependencies
([#&#8203;6852](https://togithub.com/weaveworks/eksctl/issues/6852),
[#&#8203;6859](https://togithub.com/weaveworks/eksctl/issues/6859))
- Cleanup Flux Integration
([#&#8203;6836](https://togithub.com/weaveworks/eksctl/issues/6836))

#### Acknowledgments

Weaveworks would like to sincerely thank:
[@&#8203;watany-dev](https://togithub.com/watany-dev)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 4pm on thursday" (UTC),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config help](https://togithub.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/scottames/dots).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNi4yNy4xIiwidXBkYXRlZEluVmVyIjoiMzYuMjcuMSIsInRhcmdldEJyYW5jaCI6Im1haW4ifQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant