detached 7b4c456

perf(ermitegit): re-route the readers with the numbers of ermite-git 5228465

commit
7b4c45664e9be3bb8985fcd5fe272d59e4f0e8a0
author
brice · · 2026-10-11 01:51 UTC
parent
127d960
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +112 -38
M internal/adapters/ermitegit/ermitegit.go +3 -3
@@ -23,9 +23,9 @@
2323 // commit-graph and notice a repository that stopped being supported: at once
2424 // when ermite itself wrote (the push and the maintenance tell it through
2525 // Refresh), and as a safety net at most once per Config.RefreshEvery for each
26-// repository while it is read, by one reader at a time. A stat-based Refresh
27-// is 20 µs and 34 allocations, too much to pay on every read of a method that
28-// itself costs 20 µs.
26+// repository while it is read, by one reader at a time. A Refresh
27+// stats the files of the repository, too much to pay on every read of a
28+// method that itself costs 2 µs.
2929 //
3030 // Memory: the cache of open repositories is bounded (LRU) and drops the ones
3131 // idle longer than Config.IdleTTL; each repository holds at most
@@ -23,9 +23,9 @@
23 // commit-graph and notice a repository that stopped being supported: at once23 // commit-graph and notice a repository that stopped being supported: at once
24 // when ermite itself wrote (the push and the maintenance tell it through24 // when ermite itself wrote (the push and the maintenance tell it through
25 // Refresh), and as a safety net at most once per Config.RefreshEvery for each25 // Refresh), and as a safety net at most once per Config.RefreshEvery for each
26-// repository while it is read, by one reader at a time. A stat-based Refresh26+// repository while it is read, by one reader at a time. A Refresh
27-// is 20 µs and 34 allocations, too much to pay on every read of a method that27+// stats the files of the repository, too much to pay on every read of a
28-// itself costs 20 µs.28+// method that itself costs 2 µs.
29 //29 //
30 // Memory: the cache of open repositories is bounded (LRU) and drops the ones30 // Memory: the cache of open repositories is bounded (LRU) and drops the ones
31 // idle longer than Config.IdleTTL; each repository holds at most31 // idle longer than Config.IdleTTL; each repository holds at most
M internal/adapters/ermitegit/routes.go +109 -35
@@ -4,47 +4,121 @@ package ermitegit
44
55 // DefaultRoutes returns the routes of production.
66 //
7-// A method is routed to ermite-git only when it passes the shared conformance
8-// table (TestConformance, TestCompareWithCLI) and its benchmark beats the CLI
9-// adapter in time AND in allocations, on the generated fixture and on lazygit
10-// (BenchmarkRepoReader, ERMITE_GIT_BENCH_REPO). A method that does not stays
11-// on the CLI, with the numbers that decided it. GetCommit is not routable: the
7+// The rule: a method is routed to ermite-git only when it passes the shared
8+// conformance table (TestConformance, TestCompareWithCLI) and its benchmark
9+// beats the CLI adapter in time, in bytes AND in allocations per call, warm
10+// AND cold, on the generated fixture AND on lazygit (BenchmarkRepoReader,
11+// ERMITE_GIT_BENCH_REPO). Where a method has several shapes, every shape must
12+// win: the constants GetTree, GetTreeLastCommits, ResolveRevisionAbbrev and
13+// ListCommitsPath cover directories of any size and files of any history. A
14+// method that does not win stays on the CLI. GetCommit is not routable: the
1215 // diff stays on git.
1316 //
14-// The numbers are the median of three runs of `go test -bench RepoReader
15-// -benchmem` on an AMD Ryzen 9 7900X3D, git 2.56, ermite-git ea9bcf1, as
16-// "time / allocations per call", CLI against ermite-git. The fixture has 220
17-// commits, 8 directories of 40 files and a 10 MiB blob; lazygit has 7,550
18-// commits. Only the allocations of the Go process are counted, which favours
19-// the CLI (its work happens in git processes).
17+// Warm is a repository that is open; cold closes it before every call, the
18+// price of the first read after the idle TTL, an eviction or a restart (a
19+// cold ermite-git call opens the packs and the commit-graph: about 65 µs and
20+// 130 allocations before the call itself). The CLI keeps nothing open, so its
21+// cold run is its warm run. Only the allocations of the Go process are
22+// counted, which favours the CLI (its work happens in git processes). The
23+// parallel runs (b.RunParallel) were also made: they agree with the warm runs
24+// and widen the time gap.
2025 //
21-// fixture lazygit
22-// ListRefs 900 µs/126 → 29 µs/89 900 µs/89 → 20 µs/43 win
23-// ResolveRevision branch 24 µs/19 → 5 µs/14 26 µs/19 → 5 µs/14 win
24-// ResolveRevision full id 10 µs/22 → 2 µs/6 9 µs/22 → 2 µs/6 win
25-// ResolveRevision abbrev 65 µs/31 → 12 µs/33 65 µs/31 → 13 µs/33 allocs +2
26-// ResolveRefPath 108 µs/59 → 20 µs/72 159 µs/79 → 28 µs/101 allocs +13
27-// GetTree dir 736 µs/141 → 26 µs/51 1.3 ms/1059 → 3.5 ms/2634 loses on lazygit
28-// GetTree root 717 µs/114 → 14 µs/36 812 µs/167 → 50 µs/78 win
29-// GetTree root+last 4.0 ms/1022 → 73 µs/139 20 ms/3084 → 6.2 ms/4470 allocs +45%
30-// GetTree dir+last 5.3 ms/1452 → 176 µs/247 196 ms/17143 → 11 ms/8058 win
31-// OpenBlob small 46 µs/25 → 17 µs/28 53 µs/25 → 29 µs/57 allocs +2.3x
32-// OpenBlob 10 MiB 21 ms/64 → 49 ms/28 3.4 ms/64 → 5.6 ms/140 loses (inflate speed)
33-// ListCommits page 1 1.0 ms/404 → 59 µs/149 1.3 ms/415 → 368 µs/265 win
34-// ListCommits skip 5000 1.0 ms/404 → 74 µs/149 2.0 ms/404 → 433 µs/153 win
35-// ListCommits -- file 1.6 ms/138 → 453 µs/355 12.7 ms/117 → 6.1 ms/6870 allocs +58x
36-// ListCommits -- dir 1.3 ms/403 → 202 µs/239 6.6 ms/403 → 2.0 ms/1988 allocs +5x
26+// The numbers are the median of six runs of `go test -bench RepoReader
27+// -benchmem -benchtime 500ms` on an AMD Ryzen 9 7900X3D, git 2.56, ermite-git
28+// 5228465, as "time bytes allocations per call", CLI against ermite-git (K and
29+// M are KiB and MiB). The fixture has 220 commits, 8 directories of 40 files
30+// and a 10 MiB blob; lazygit has 7,550 commits. "loses" names what the
31+// library does not win: t time, b bytes, a allocations.
3732 //
38-// Where a method has several shapes, every shape must win to be routed: the
39-// constants GetTree, GetTreeLastCommits and ListCommitsPath cover directories
40-// of any size and files of any history. The losses in allocations come from
41-// the inflate of tree and commit objects (compress/zlib allocates an adler32
42-// digest and the flate tables on every Reset): once the library avoids that,
43-// re-run the benchmark and flip the constants it wins.
33+// shape repo mode cli ermite-git verdict
34+// ListRefs fixture warm 905µs 116.3K 125 24µs 19.0K 73 win
35+// ListRefs fixture cold 904µs 116.3K 125 88µs 35.2K 203 loses a
36+// ListRefs lazygit warm 901µs 105.2K 88 15µs 2.2K 27 win
37+// ListRefs lazygit cold 908µs 105.4K 88 88µs 14.2K 163 loses a
38+// ResolveRevision/branch fixture warm 22µs 0.9K 19 4µs 0.4K 8 win
39+// ResolveRevision/branch fixture cold 22µs 0.9K 19 68µs 16.6K 138 loses tba
40+// ResolveRevision/branch lazygit warm 22µs 1.0K 19 4µs 0.5K 8 win
41+// ResolveRevision/branch lazygit cold 23µs 1.0K 19 72µs 12.4K 144 loses tba
42+// ResolveRevision/fullid fixture warm 9µs 1.3K 22 2µs 0.4K 6 win
43+// ResolveRevision/fullid fixture cold 9µs 1.3K 22 65µs 16.6K 136 loses tba
44+// ResolveRevision/fullid lazygit warm 9µs 1.3K 22 2µs 0.4K 6 win
45+// ResolveRevision/fullid lazygit cold 9µs 1.3K 22 70µs 12.3K 142 loses tba
46+// ResolveRevision/abbrev fixture warm 63µs 1.5K 31 9µs 0.7K 11 win
47+// ResolveRevision/abbrev fixture cold 64µs 1.5K 31 71µs 16.8K 141 loses tba
48+// ResolveRevision/abbrev lazygit warm 63µs 1.5K 31 9µs 0.6K 11 win
49+// ResolveRevision/abbrev lazygit cold 63µs 1.5K 31 76µs 12.6K 147 loses tba
50+// ResolveRefPath fixture warm 104µs 2.4K 59 15µs 0.8K 22 win
51+// ResolveRefPath fixture cold 105µs 2.4K 59 78µs 17.0K 152 loses ba
52+// ResolveRefPath lazygit warm 150µs 4.1K 79 22µs 1.4K 29 win
53+// ResolveRefPath lazygit cold 148µs 4.1K 79 89µs 13.4K 165 loses ba
54+// GetTree/root fixture warm 717µs 108.4K 114 13µs 5.8K 24 win
55+// GetTree/root fixture cold 725µs 108.4K 114 78µs 22.4K 154 loses a
56+// GetTree/root lazygit warm 827µs 121.4K 167 29µs 18.2K 56 win
57+// GetTree/root lazygit cold 831µs 121.5K 167 376µs 1.6M 200 loses ba
58+// GetTree/dir fixture warm 742µs 109.5K 141 16µs 9.2K 26 win
59+// GetTree/dir fixture cold 745µs 109.5K 141 109µs 75.8K 158 loses a
60+// GetTree/dir lazygit warm 1.4ms 267.9K 1058 193µs 318.4K 73 loses b
61+// GetTree/dir lazygit cold 1.4ms 268.0K 1058 4.2ms 2.0M 2490 loses tba
62+// GetTree/root+last fixture warm 4.0ms 1.2M 1023 70µs 108.3K 107 win
63+// GetTree/root+last fixture cold 4.0ms 1.2M 1024 165µs 127.8K 249 win
64+// GetTree/root+last lazygit warm 20.2ms 3.8M 3072 6.1ms 367.0K 510 win
65+// GetTree/root+last lazygit cold 20.3ms 3.8M 3073 8.5ms 3.4M 4204 loses a
66+// GetTree/dir+last fixture warm 5.3ms 1.8M 1454 190µs 166.1K 142 win
67+// GetTree/dir+last fixture cold 5.4ms 1.8M 1455 316µs 247.6K 323 win
68+// GetTree/dir+last lazygit warm 195.2ms 20.5M 17116 7.9ms 1.5M 855 win
69+// GetTree/dir+last lazygit cold 195.9ms 20.5M 17107 14.8ms 5.1M 6989 win
70+// OpenBlob/small fixture warm 46µs 6.1K 25 17µs 5.8K 14 win
71+// OpenBlob/small fixture cold 47µs 6.1K 25 87µs 27.7K 147 loses tba
72+// OpenBlob/small lazygit warm 51µs 5.4K 25 29µs 7.0K 42 loses ba
73+// OpenBlob/small lazygit cold 50µs 5.4K 25 131µs 25.1K 184 loses tba
74+// OpenBlob/largest fixture warm 21.4ms 69.7K 64 52.5ms 9.0K 13 loses t
75+// OpenBlob/largest fixture cold 21.6ms 70.0K 64 51.4ms 26.2K 143 loses ta
76+// OpenBlob/largest lazygit warm 3.3ms 69.9K 64 5.7ms 16.3K 123 loses ta
77+// OpenBlob/largest lazygit cold 3.3ms 69.9K 64 5.8ms 31.6K 283 loses ta
78+// ListCommits/page1 fixture warm 1.0ms 122.5K 404 61µs 19.1K 108 win
79+// ListCommits/page1 fixture cold 1.0ms 122.5K 404 134µs 36.9K 244 win
80+// ListCommits/page1 lazygit warm 1.3ms 157.2K 415 361µs 120.1K 224 win
81+// ListCommits/page1 lazygit cold 1.3ms 157.2K 415 503µs 136.2K 377 win
82+// ListCommits/deep fixture warm 1.0ms 122.1K 404 71µs 19.3K 108 win
83+// ListCommits/deep fixture cold 1.0ms 122.1K 404 136µs 36.1K 238 win
84+// ListCommits/deep lazygit warm 2.0ms 123.2K 404 424µs 23.9K 112 win
85+// ListCommits/deep lazygit cold 2.0ms 123.2K 404 544µs 36.3K 248 win
86+// ListCommits/path fixture warm 1.6ms 108.8K 138 443µs 25.4K 42 win
87+// ListCommits/path fixture cold 1.6ms 108.9K 138 594µs 93.6K 360 loses a
88+// ListCommits/path lazygit warm 12.9ms 108.7K 118 6.0ms 29.2K 58 win
89+// ListCommits/path lazygit cold 12.8ms 107.7K 118 9.5ms 2.4M 6498 loses ba
90+// ListCommits/dirpath fixture warm 1.3ms 123.0K 403 199µs 33.8K 113 win
91+// ListCommits/dirpath fixture cold 1.3ms 123.0K 403 277µs 55.4K 268 win
92+// ListCommits/dirpath lazygit warm 6.7ms 127.8K 403 2.0ms 50.2K 181 win
93+// ListCommits/dirpath lazygit cold 6.7ms 127.8K 403 3.3ms 887.1K 2019 loses ba
94+//
95+// What decides the routes, with ermite-git 5228465 (zlib reader with a reused
96+// adler32, header-size cache, Refresh and LookupRefID without allocations):
97+//
98+// - The warm runs all win but GetTree of a large directory on lazygit (time
99+// is won since the zlib change, bytes are not) and OpenBlob, whose
100+// allocations come from the inflate of a small blob and whose time is
101+// lost for a 10 MiB blob (inflate speed: 52 ms against 21 ms).
102+// - The cold runs lose in allocations for every method that does little
103+// work after the open (ListRefs, GetTree, ResolveRefPath: 150 to 200
104+// against 60 to 140), and in time too for the ones that cost a few
105+// microseconds warm (ResolveRevision: 65 µs against 9 to 63 µs).
106+// - Only the history of a revision (ListCommits page 1 and deep) wins every
107+// line, so it is the only method routed. GetTree dir+last also wins every
108+// line, but GetTreeLastCommits covers GetTree root+last too, which loses
109+// cold allocations on lazygit (4204 against 3073): it is the nearest
110+// miss, and the first to flip once opening a repository allocates less.
111+// - OpenBlob by size: below 1 MiB the small blob loses cold and, on lazygit,
112+// warm bytes and allocations; above, it loses time. No size makes it win,
113+// so there is no size-based route.
114+//
115+// Once opening a repository allocates less, re-run the benchmark and flip the
116+// constants it wins: ListRefs, ResolveRefPath and ResolveRevision win warm by 4
117+// to 60 times in time.
44118 func DefaultRoutes() Routes {
45119 return Routes{
46- ListRefs: ErmiteGit,
47- ResolveRevision: ErmiteGit,
120+ ListRefs: CLI,
121+ ResolveRevision: CLI,
48122 ResolveRevisionAbbrev: CLI,
49123 ResolveRefPath: CLI,
50124 GetTree: CLI,
@@ -4,47 +4,121 @@ package ermitegit
4 4
5 // DefaultRoutes returns the routes of production.5 // DefaultRoutes returns the routes of production.
6 //6 //
7-// A method is routed to ermite-git only when it passes the shared conformance7+// The rule: a method is routed to ermite-git only when it passes the shared
8-// table (TestConformance, TestCompareWithCLI) and its benchmark beats the CLI8+// conformance table (TestConformance, TestCompareWithCLI) and its benchmark
9-// adapter in time AND in allocations, on the generated fixture and on lazygit9+// beats the CLI adapter in time, in bytes AND in allocations per call, warm
10-// (BenchmarkRepoReader, ERMITE_GIT_BENCH_REPO). A method that does not stays10+// AND cold, on the generated fixture AND on lazygit (BenchmarkRepoReader,
11-// on the CLI, with the numbers that decided it. GetCommit is not routable: the11+// ERMITE_GIT_BENCH_REPO). Where a method has several shapes, every shape must
12+// win: the constants GetTree, GetTreeLastCommits, ResolveRevisionAbbrev and
13+// ListCommitsPath cover directories of any size and files of any history. A
14+// method that does not win stays on the CLI. GetCommit is not routable: the
12 // diff stays on git.15 // diff stays on git.
13 //16 //
14-// The numbers are the median of three runs of `go test -bench RepoReader17+// Warm is a repository that is open; cold closes it before every call, the
15-// -benchmem` on an AMD Ryzen 9 7900X3D, git 2.56, ermite-git ea9bcf1, as18+// price of the first read after the idle TTL, an eviction or a restart (a
16-// "time / allocations per call", CLI against ermite-git. The fixture has 22019+// cold ermite-git call opens the packs and the commit-graph: about 65 µs and
17-// commits, 8 directories of 40 files and a 10 MiB blob; lazygit has 7,55020+// 130 allocations before the call itself). The CLI keeps nothing open, so its
18-// commits. Only the allocations of the Go process are counted, which favours21+// cold run is its warm run. Only the allocations of the Go process are
19-// the CLI (its work happens in git processes).22+// counted, which favours the CLI (its work happens in git processes). The
23+// parallel runs (b.RunParallel) were also made: they agree with the warm runs
24+// and widen the time gap.
20 //25 //
21-// fixture lazygit26+// The numbers are the median of six runs of `go test -bench RepoReader
22-// ListRefs 900 µs/126 → 29 µs/89 900 µs/89 → 20 µs/43 win27+// -benchmem -benchtime 500ms` on an AMD Ryzen 9 7900X3D, git 2.56, ermite-git
23-// ResolveRevision branch 24 µs/19 → 5 µs/14 26 µs/19 → 5 µs/14 win28+// 5228465, as "time bytes allocations per call", CLI against ermite-git (K and
24-// ResolveRevision full id 10 µs/22 → 2 µs/6 9 µs/22 → 2 µs/6 win29+// M are KiB and MiB). The fixture has 220 commits, 8 directories of 40 files
25-// ResolveRevision abbrev 65 µs/31 → 12 µs/33 65 µs/31 → 13 µs/33 allocs +230+// and a 10 MiB blob; lazygit has 7,550 commits. "loses" names what the
26-// ResolveRefPath 108 µs/59 → 20 µs/72 159 µs/79 → 28 µs/101 allocs +1331+// library does not win: t time, b bytes, a allocations.
27-// GetTree dir 736 µs/141 → 26 µs/51 1.3 ms/1059 → 3.5 ms/2634 loses on lazygit
28-// GetTree root 717 µs/114 → 14 µs/36 812 µs/167 → 50 µs/78 win
29-// GetTree root+last 4.0 ms/1022 → 73 µs/139 20 ms/3084 → 6.2 ms/4470 allocs +45%
30-// GetTree dir+last 5.3 ms/1452 → 176 µs/247 196 ms/17143 → 11 ms/8058 win
31-// OpenBlob small 46 µs/25 → 17 µs/28 53 µs/25 → 29 µs/57 allocs +2.3x
32-// OpenBlob 10 MiB 21 ms/64 → 49 ms/28 3.4 ms/64 → 5.6 ms/140 loses (inflate speed)
33-// ListCommits page 1 1.0 ms/404 → 59 µs/149 1.3 ms/415 → 368 µs/265 win
34-// ListCommits skip 5000 1.0 ms/404 → 74 µs/149 2.0 ms/404 → 433 µs/153 win
35-// ListCommits -- file 1.6 ms/138 → 453 µs/355 12.7 ms/117 → 6.1 ms/6870 allocs +58x
36-// ListCommits -- dir 1.3 ms/403 → 202 µs/239 6.6 ms/403 → 2.0 ms/1988 allocs +5x
37 //32 //
38-// Where a method has several shapes, every shape must win to be routed: the33+// shape repo mode cli ermite-git verdict
39-// constants GetTree, GetTreeLastCommits and ListCommitsPath cover directories34+// ListRefs fixture warm 905µs 116.3K 125 24µs 19.0K 73 win
40-// of any size and files of any history. The losses in allocations come from35+// ListRefs fixture cold 904µs 116.3K 125 88µs 35.2K 203 loses a
41-// the inflate of tree and commit objects (compress/zlib allocates an adler3236+// ListRefs lazygit warm 901µs 105.2K 88 15µs 2.2K 27 win
42-// digest and the flate tables on every Reset): once the library avoids that,37+// ListRefs lazygit cold 908µs 105.4K 88 88µs 14.2K 163 loses a
43-// re-run the benchmark and flip the constants it wins.38+// ResolveRevision/branch fixture warm 22µs 0.9K 19 4µs 0.4K 8 win
39+// ResolveRevision/branch fixture cold 22µs 0.9K 19 68µs 16.6K 138 loses tba
40+// ResolveRevision/branch lazygit warm 22µs 1.0K 19 4µs 0.5K 8 win
41+// ResolveRevision/branch lazygit cold 23µs 1.0K 19 72µs 12.4K 144 loses tba
42+// ResolveRevision/fullid fixture warm 9µs 1.3K 22 2µs 0.4K 6 win
43+// ResolveRevision/fullid fixture cold 9µs 1.3K 22 65µs 16.6K 136 loses tba
44+// ResolveRevision/fullid lazygit warm 9µs 1.3K 22 2µs 0.4K 6 win
45+// ResolveRevision/fullid lazygit cold 9µs 1.3K 22 70µs 12.3K 142 loses tba
46+// ResolveRevision/abbrev fixture warm 63µs 1.5K 31 9µs 0.7K 11 win
47+// ResolveRevision/abbrev fixture cold 64µs 1.5K 31 71µs 16.8K 141 loses tba
48+// ResolveRevision/abbrev lazygit warm 63µs 1.5K 31 9µs 0.6K 11 win
49+// ResolveRevision/abbrev lazygit cold 63µs 1.5K 31 76µs 12.6K 147 loses tba
50+// ResolveRefPath fixture warm 104µs 2.4K 59 15µs 0.8K 22 win
51+// ResolveRefPath fixture cold 105µs 2.4K 59 78µs 17.0K 152 loses ba
52+// ResolveRefPath lazygit warm 150µs 4.1K 79 22µs 1.4K 29 win
53+// ResolveRefPath lazygit cold 148µs 4.1K 79 89µs 13.4K 165 loses ba
54+// GetTree/root fixture warm 717µs 108.4K 114 13µs 5.8K 24 win
55+// GetTree/root fixture cold 725µs 108.4K 114 78µs 22.4K 154 loses a
56+// GetTree/root lazygit warm 827µs 121.4K 167 29µs 18.2K 56 win
57+// GetTree/root lazygit cold 831µs 121.5K 167 376µs 1.6M 200 loses ba
58+// GetTree/dir fixture warm 742µs 109.5K 141 16µs 9.2K 26 win
59+// GetTree/dir fixture cold 745µs 109.5K 141 109µs 75.8K 158 loses a
60+// GetTree/dir lazygit warm 1.4ms 267.9K 1058 193µs 318.4K 73 loses b
61+// GetTree/dir lazygit cold 1.4ms 268.0K 1058 4.2ms 2.0M 2490 loses tba
62+// GetTree/root+last fixture warm 4.0ms 1.2M 1023 70µs 108.3K 107 win
63+// GetTree/root+last fixture cold 4.0ms 1.2M 1024 165µs 127.8K 249 win
64+// GetTree/root+last lazygit warm 20.2ms 3.8M 3072 6.1ms 367.0K 510 win
65+// GetTree/root+last lazygit cold 20.3ms 3.8M 3073 8.5ms 3.4M 4204 loses a
66+// GetTree/dir+last fixture warm 5.3ms 1.8M 1454 190µs 166.1K 142 win
67+// GetTree/dir+last fixture cold 5.4ms 1.8M 1455 316µs 247.6K 323 win
68+// GetTree/dir+last lazygit warm 195.2ms 20.5M 17116 7.9ms 1.5M 855 win
69+// GetTree/dir+last lazygit cold 195.9ms 20.5M 17107 14.8ms 5.1M 6989 win
70+// OpenBlob/small fixture warm 46µs 6.1K 25 17µs 5.8K 14 win
71+// OpenBlob/small fixture cold 47µs 6.1K 25 87µs 27.7K 147 loses tba
72+// OpenBlob/small lazygit warm 51µs 5.4K 25 29µs 7.0K 42 loses ba
73+// OpenBlob/small lazygit cold 50µs 5.4K 25 131µs 25.1K 184 loses tba
74+// OpenBlob/largest fixture warm 21.4ms 69.7K 64 52.5ms 9.0K 13 loses t
75+// OpenBlob/largest fixture cold 21.6ms 70.0K 64 51.4ms 26.2K 143 loses ta
76+// OpenBlob/largest lazygit warm 3.3ms 69.9K 64 5.7ms 16.3K 123 loses ta
77+// OpenBlob/largest lazygit cold 3.3ms 69.9K 64 5.8ms 31.6K 283 loses ta
78+// ListCommits/page1 fixture warm 1.0ms 122.5K 404 61µs 19.1K 108 win
79+// ListCommits/page1 fixture cold 1.0ms 122.5K 404 134µs 36.9K 244 win
80+// ListCommits/page1 lazygit warm 1.3ms 157.2K 415 361µs 120.1K 224 win
81+// ListCommits/page1 lazygit cold 1.3ms 157.2K 415 503µs 136.2K 377 win
82+// ListCommits/deep fixture warm 1.0ms 122.1K 404 71µs 19.3K 108 win
83+// ListCommits/deep fixture cold 1.0ms 122.1K 404 136µs 36.1K 238 win
84+// ListCommits/deep lazygit warm 2.0ms 123.2K 404 424µs 23.9K 112 win
85+// ListCommits/deep lazygit cold 2.0ms 123.2K 404 544µs 36.3K 248 win
86+// ListCommits/path fixture warm 1.6ms 108.8K 138 443µs 25.4K 42 win
87+// ListCommits/path fixture cold 1.6ms 108.9K 138 594µs 93.6K 360 loses a
88+// ListCommits/path lazygit warm 12.9ms 108.7K 118 6.0ms 29.2K 58 win
89+// ListCommits/path lazygit cold 12.8ms 107.7K 118 9.5ms 2.4M 6498 loses ba
90+// ListCommits/dirpath fixture warm 1.3ms 123.0K 403 199µs 33.8K 113 win
91+// ListCommits/dirpath fixture cold 1.3ms 123.0K 403 277µs 55.4K 268 win
92+// ListCommits/dirpath lazygit warm 6.7ms 127.8K 403 2.0ms 50.2K 181 win
93+// ListCommits/dirpath lazygit cold 6.7ms 127.8K 403 3.3ms 887.1K 2019 loses ba
94+//
95+// What decides the routes, with ermite-git 5228465 (zlib reader with a reused
96+// adler32, header-size cache, Refresh and LookupRefID without allocations):
97+//
98+// - The warm runs all win but GetTree of a large directory on lazygit (time
99+// is won since the zlib change, bytes are not) and OpenBlob, whose
100+// allocations come from the inflate of a small blob and whose time is
101+// lost for a 10 MiB blob (inflate speed: 52 ms against 21 ms).
102+// - The cold runs lose in allocations for every method that does little
103+// work after the open (ListRefs, GetTree, ResolveRefPath: 150 to 200
104+// against 60 to 140), and in time too for the ones that cost a few
105+// microseconds warm (ResolveRevision: 65 µs against 9 to 63 µs).
106+// - Only the history of a revision (ListCommits page 1 and deep) wins every
107+// line, so it is the only method routed. GetTree dir+last also wins every
108+// line, but GetTreeLastCommits covers GetTree root+last too, which loses
109+// cold allocations on lazygit (4204 against 3073): it is the nearest
110+// miss, and the first to flip once opening a repository allocates less.
111+// - OpenBlob by size: below 1 MiB the small blob loses cold and, on lazygit,
112+// warm bytes and allocations; above, it loses time. No size makes it win,
113+// so there is no size-based route.
114+//
115+// Once opening a repository allocates less, re-run the benchmark and flip the
116+// constants it wins: ListRefs, ResolveRefPath and ResolveRevision win warm by 4
117+// to 60 times in time.
44 func DefaultRoutes() Routes {118 func DefaultRoutes() Routes {
45 return Routes{119 return Routes{
46- ListRefs: ErmiteGit,120+ ListRefs: CLI,
47- ResolveRevision: ErmiteGit,121+ ResolveRevision: CLI,
48 ResolveRevisionAbbrev: CLI,122 ResolveRevisionAbbrev: CLI,
49 ResolveRefPath: CLI,123 ResolveRefPath: CLI,
50 GetTree: CLI,124 GetTree: CLI,

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 45

↑↓ moveenter openesc close