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 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
15// diff stays on git.
16//
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.
25//
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.
32//
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.
118func DefaultRoutes() Routes {
119 return Routes{
120 ListRefs: CLI,
121 ResolveRevision: CLI,
122 ResolveRevisionAbbrev: CLI,
123 ResolveRefPath: CLI,
124 GetTree: CLI,
125 GetTreeLastCommits: CLI,
126 OpenBlob: CLI,
127 ListCommits: ErmiteGit,
128 ListCommitsPath: CLI,
129 }
130}