1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package ermitegit
4
5// DefaultRoutes returns the routes of production.
6//
7// The rule: a method is routed to ermite-git when it passes the shared
8// conformance table (TestConformance, TestCompareWithCLI) and its benchmark
9// beats the CLI adapter WARM in time AND in bytes per call, on the generated
10// fixture AND on lazygit (BenchmarkRepoReader, ERMITE_GIT_BENCH_REPO), and its
11// COLD run does not lose in time by more than the open itself (about 65 µs,
12// paid once per open, see below). Allocation counts are reported but do not
13// decide: the repository cache (cache.go) keeps up to 64 repositories open for
14// ten minutes, so nearly every request is warm, and the CLI's own cost (a
15// process per call, absent from the Go counters) is not in its numbers. Where
16// a method has several shapes that behave differently, each has its own
17// constant: GetTree, GetTreeLastCommits (root), GetTreeSubdirLastCommits,
18// ResolveRevisionAbbrev and ListCommitsPath. A method that does not win stays
19// on the CLI. GetCommit is not routable: the diff stays on git. ListPaths has
20// one shape: the fixture lists 131 files and lazygit 2,180.
21//
22// Warm is a repository that is open; cold closes it before every call, the
23// price of the first read after the idle TTL, an eviction or a restart (a
24// cold ermite-git call opens the packs and the commit-graph: about 65 µs and
25// 130 allocations before the call itself). The CLI keeps nothing open, so its
26// cold run is its warm run. Only the allocations of the Go process are
27// counted, which favours the CLI (its work happens in git processes). The
28// parallel runs (b.RunParallel) were also made: they agree with the warm runs
29// and widen the time gap.
30//
31// The numbers are the median of six runs of `go test -bench RepoReader
32// -benchmem -benchtime 500ms` on an AMD Ryzen 9 7900X3D, git 2.56, ermite-git
33// 5228465, as "time bytes allocations per call", CLI against ermite-git (K and
34// M are KiB and MiB). The fixture has 220 commits, 8 directories of 40 files
35// and a 10 MiB blob; lazygit has 7,550 commits. "loses" names what the
36// library does not win: t time, b bytes, a allocations.
37//
38// shape repo mode cli ermite-git verdict
39// ListRefs fixture warm 905µs 116.3K 125 24µs 19.0K 73 win
40// ListRefs fixture cold 904µs 116.3K 125 88µs 35.2K 203 loses a
41// ListRefs lazygit warm 901µs 105.2K 88 15µs 2.2K 27 win
42// ListRefs lazygit cold 908µs 105.4K 88 88µs 14.2K 163 loses a
43// ResolveRevision/branch fixture warm 22µs 0.9K 19 4µs 0.4K 8 win
44// ResolveRevision/branch fixture cold 22µs 0.9K 19 68µs 16.6K 138 loses tba
45// ResolveRevision/branch lazygit warm 22µs 1.0K 19 4µs 0.5K 8 win
46// ResolveRevision/branch lazygit cold 23µs 1.0K 19 72µs 12.4K 144 loses tba
47// ResolveRevision/fullid fixture warm 9µs 1.3K 22 2µs 0.4K 6 win
48// ResolveRevision/fullid fixture cold 9µs 1.3K 22 65µs 16.6K 136 loses tba
49// ResolveRevision/fullid lazygit warm 9µs 1.3K 22 2µs 0.4K 6 win
50// ResolveRevision/fullid lazygit cold 9µs 1.3K 22 70µs 12.3K 142 loses tba
51// ResolveRevision/abbrev fixture warm 63µs 1.5K 31 9µs 0.7K 11 win
52// ResolveRevision/abbrev fixture cold 64µs 1.5K 31 71µs 16.8K 141 loses tba
53// ResolveRevision/abbrev lazygit warm 63µs 1.5K 31 9µs 0.6K 11 win
54// ResolveRevision/abbrev lazygit cold 63µs 1.5K 31 76µs 12.6K 147 loses tba
55// ResolveRefPath fixture warm 104µs 2.4K 59 15µs 0.8K 22 win
56// ResolveRefPath fixture cold 105µs 2.4K 59 78µs 17.0K 152 loses ba
57// ResolveRefPath lazygit warm 150µs 4.1K 79 22µs 1.4K 29 win
58// ResolveRefPath lazygit cold 148µs 4.1K 79 89µs 13.4K 165 loses ba
59// GetTree/root fixture warm 717µs 108.4K 114 13µs 5.8K 24 win
60// GetTree/root fixture cold 725µs 108.4K 114 78µs 22.4K 154 loses a
61// GetTree/root lazygit warm 827µs 121.4K 167 29µs 18.2K 56 win
62// GetTree/root lazygit cold 831µs 121.5K 167 376µs 1.6M 200 loses ba
63// GetTree/dir fixture warm 742µs 109.5K 141 16µs 9.2K 26 win
64// GetTree/dir fixture cold 745µs 109.5K 141 109µs 75.8K 158 loses a
65// GetTree/dir lazygit warm 1.4ms 267.9K 1058 193µs 318.4K 73 loses b
66// GetTree/dir lazygit cold 1.4ms 268.0K 1058 4.2ms 2.0M 2490 loses tba
67// GetTree/root+last fixture warm 4.0ms 1.2M 1023 70µs 108.3K 107 win
68// GetTree/root+last fixture cold 4.0ms 1.2M 1024 165µs 127.8K 249 win
69// GetTree/root+last lazygit warm 20.2ms 3.8M 3072 6.1ms 367.0K 510 win
70// GetTree/root+last lazygit cold 20.3ms 3.8M 3073 8.5ms 3.4M 4204 loses a
71// GetTree/dir+last fixture warm 5.3ms 1.8M 1454 190µs 166.1K 142 win
72// GetTree/dir+last fixture cold 5.4ms 1.8M 1455 316µs 247.6K 323 win
73// GetTree/dir+last lazygit warm 195.2ms 20.5M 17116 7.9ms 1.5M 855 win
74// GetTree/dir+last lazygit cold 195.9ms 20.5M 17107 14.8ms 5.1M 6989 win
75// ListPaths fixture warm 760µs 118.4K 225 41µs 37.9K 30 win
76// ListPaths fixture cold 771µs 118.4K 225 107µs 54.7K 160 win
77// ListPaths lazygit warm 1.8ms 432.9K 2280 842µs 250.3K 477 win
78// ListPaths lazygit cold 1.8ms 432.9K 2280 1.2ms 298.7K 749 win
79// OpenBlob/small fixture warm 46µs 6.1K 25 17µs 5.8K 14 win
80// OpenBlob/small fixture cold 47µs 6.1K 25 87µs 27.7K 147 loses tba
81// OpenBlob/small lazygit warm 51µs 5.4K 25 29µs 7.0K 42 loses ba
82// OpenBlob/small lazygit cold 50µs 5.4K 25 131µs 25.1K 184 loses tba
83// OpenBlob/largest fixture warm 21.4ms 69.7K 64 52.5ms 9.0K 13 loses t
84// OpenBlob/largest fixture cold 21.6ms 70.0K 64 51.4ms 26.2K 143 loses ta
85// OpenBlob/largest lazygit warm 3.3ms 69.9K 64 5.7ms 16.3K 123 loses ta
86// OpenBlob/largest lazygit cold 3.3ms 69.9K 64 5.8ms 31.6K 283 loses ta
87// ListCommits/page1 fixture warm 1.0ms 122.5K 404 61µs 19.1K 108 win
88// ListCommits/page1 fixture cold 1.0ms 122.5K 404 134µs 36.9K 244 win
89// ListCommits/page1 lazygit warm 1.3ms 157.2K 415 361µs 120.1K 224 win
90// ListCommits/page1 lazygit cold 1.3ms 157.2K 415 503µs 136.2K 377 win
91// ListCommits/deep fixture warm 1.0ms 122.1K 404 71µs 19.3K 108 win
92// ListCommits/deep fixture cold 1.0ms 122.1K 404 136µs 36.1K 238 win
93// ListCommits/deep lazygit warm 2.0ms 123.2K 404 424µs 23.9K 112 win
94// ListCommits/deep lazygit cold 2.0ms 123.2K 404 544µs 36.3K 248 win
95// ListCommits/path fixture warm 1.6ms 108.8K 138 443µs 25.4K 42 win
96// ListCommits/path fixture cold 1.6ms 108.9K 138 594µs 93.6K 360 loses a
97// ListCommits/path lazygit warm 12.9ms 108.7K 118 6.0ms 29.2K 58 win
98// ListCommits/path lazygit cold 12.8ms 107.7K 118 9.5ms 2.4M 6498 loses ba
99// ListCommits/dirpath fixture warm 1.3ms 123.0K 403 199µs 33.8K 113 win
100// ListCommits/dirpath fixture cold 1.3ms 123.0K 403 277µs 55.4K 268 win
101// ListCommits/dirpath lazygit warm 6.7ms 127.8K 403 2.0ms 50.2K 181 win
102// ListCommits/dirpath lazygit cold 6.7ms 127.8K 403 3.3ms 887.1K 2019 loses ba
103//
104// The "verdict" column is the strict reading, every line of the table won;
105// the routes follow the rule above. What decides them, with ermite-git 5228465
106// (zlib reader with a reused adler32, header-size cache, Refresh and
107// LookupRefID without allocations):
108//
109// - ListRefs (every repository page, through loadRepo): 905 µs against 24 µs
110// warm, 120 KiB against 19 KiB (fixture), 960 against 16 µs and 108 KiB
111// against 2 KiB (lazygit). Cold it is still 4 to 10 times faster
112// (93 µs); only its allocation count loses. Routed.
113// - ResolveRevision (name, full id, abbreviation) and ResolveRefPath: 4 to 8
114// times faster warm, with fewer bytes. Cold they lose 45 µs of time to the
115// open, but the ListRefs of the same page has opened the repository
116// already, so the open is paid by a call that wins it back. Routed.
117// - GetTree with last commits: root listing 4.1 ms against 68 µs on the
118// fixture, 20.5 ms against 6.2 ms on lazygit (cold 8.7 ms; its 4,206
119// allocations against 3,082 are the only line lost); a subdirectory of
120// lazygit 195-199 ms against 8-9 ms warm, 15-17 ms cold, 20 MiB against
121// 1.5 MiB. They are two constants so that either can be flipped back on
122// its own. Both routed.
123// - GetTree without last commits wins warm in time everywhere (13 µs against
124// 0.7 ms at the root), but a large directory of lazygit loses warm bytes
125// (325 KiB against 275 KiB) and, cold, 4.0 ms against 1.4 ms in time. It
126// stays on the CLI.
127// - ListCommits of a revision wins every line and stays routed.
128// ListCommitsPath wins warm and cold in time (lazygit file: 12.9 ms against
129// 6.0 ms warm), only its cold bytes lose (2.4 MiB): it is a candidate, not
130// flipped here, to be decided with its own conformance review.
131// - OpenBlob by size: below 1 MiB the small blob loses warm bytes and
132// allocations on lazygit; above, it loses time (inflate speed: 52 ms
133// against 21 ms). No size makes it win, so there is no size-based route.
134// - ListPaths (the recursive path list of the finder) wins every line, even
135// cold, because one walk replaces a process and the parsing of its output
136// (the bytes of the paths are kept in 32 KiB blocks, which is what brought
137// the lazygit bytes under the CLI's: a single growing buffer lost them,
138// 699K against 433K). Routed.
139func DefaultRoutes() Routes {
140 return Routes{
141 ListRefs: ErmiteGit,
142 ResolveRevision: ErmiteGit,
143 ResolveRevisionAbbrev: ErmiteGit,
144 ResolveRefPath: ErmiteGit,
145 GetTree: CLI,
146 GetTreeLastCommits: ErmiteGit,
147 GetTreeSubdirLastCommits: ErmiteGit,
148 OpenBlob: CLI,
149 ListCommits: ErmiteGit,
150 ListCommitsPath: CLI,
151 ListPaths: ErmiteGit,
152 }
153}