detached ab6686c

storage_benchmark_test.go RawHistoryFolder

1package vt
2
3import (
4	"runtime"
5	"testing"
6
7	"github.com/bnema/vev-vt/core"
8)
9
10const (
11	storageBenchmarkRows  = 10_000
12	storageBenchmarkWidth = 120
13)
14
15var benchmarkStorageHistorySink *History
16
17type storageBenchmarkScenario struct {
18	name string
19	fill func([]core.Cell, int)
20}
21
22func BenchmarkHistoryBuild10Kx120(b *testing.B) {
23	for _, scenario := range historyStorageScenarios() {
24		b.Run(scenario.name, func(b *testing.B) {
25			b.ReportAllocs()
26			for b.Loop() {
27				benchmarkStorageHistorySink = buildStorageBenchmarkHistory(b, scenario.fill)
28			}
29			b.ReportMetric(storageBenchmarkRows*storageBenchmarkWidth, "cells/op")
30		})
31	}
32}
33
34// BenchmarkHistoryRetained10Kx120 isolates each sample with garbage collection
35// and reports the live heap retained by one history. Use -benchtime=1x when
36// comparing representations; elapsed time includes the measurement GCs.
37func BenchmarkHistoryRetained10Kx120(b *testing.B) {
38	for _, scenario := range historyStorageScenarios() {
39		b.Run(scenario.name, func(b *testing.B) {
40			var retained uint64
41			for b.Loop() {
42				benchmarkStorageHistorySink = nil
43				runtime.GC()
44				var before runtime.MemStats
45				runtime.ReadMemStats(&before)
46
47				benchmarkStorageHistorySink = buildStorageBenchmarkHistory(b, scenario.fill)
48				runtime.GC()
49				var after runtime.MemStats
50				runtime.ReadMemStats(&after)
51				if after.HeapAlloc >= before.HeapAlloc {
52					retained += after.HeapAlloc - before.HeapAlloc
53				}
54			}
55			benchmarkStorageHistorySink = nil
56			if b.N > 0 {
57				b.ReportMetric(float64(retained)/float64(b.N), "retained-B/op")
58			}
59		})
60	}
61}
62
63// BenchmarkColdHistoryRetained10Kx120 measures the same scenarios after two
64// bounded idle passes. The newest sealed page and mutable tail remain hot.
65func BenchmarkColdHistoryRetained10Kx120(b *testing.B) {
66	for _, scenario := range historyStorageScenarios() {
67		b.Run(scenario.name, func(b *testing.B) {
68			var retained uint64
69			for b.Loop() {
70				benchmarkStorageHistorySink = nil
71				runtime.GC()
72				var before, after runtime.MemStats
73				runtime.ReadMemStats(&before)
74				h := buildStorageBenchmarkHistory(b, scenario.fill)
75				for range 2 {
76					if _, err := h.CompressIdle(len(h.chunks)); err != nil {
77						b.Fatal(err)
78					}
79				}
80				benchmarkStorageHistorySink = h
81				// Two cycles drain the compressor sync.Pool, so only the
82				// history itself is measured.
83				runtime.GC()
84				runtime.GC()
85				runtime.ReadMemStats(&after)
86				if after.HeapAlloc >= before.HeapAlloc {
87					retained += after.HeapAlloc - before.HeapAlloc
88				}
89			}
90			stats := benchmarkStorageHistorySink.CompressionStats()
91			b.ReportMetric(float64(stats.CompressedBytes), "compressed-B")
92			b.ReportMetric(float64(stats.ColdPages), "cold-pages")
93			b.ReportMetric(float64(retained)/float64(b.N), "retained-B/op")
94			benchmarkStorageHistorySink = nil
95		})
96	}
97}
98
99func historyStorageScenarios() []storageBenchmarkScenario {
100	defaultStyle := core.DefaultStyle()
101	repeatedIndexed := core.Style{
102		Bold:           true,
103		Foreground:     196,
104		Background:     17,
105		UnderlineStyle: core.UnderlineSingle,
106	}
107	repeatedRGB := core.Style{
108		Foreground:       -1,
109		Background:       -1,
110		HasForegroundRGB: true,
111		ForegroundRGB:    core.RGB{R: 224, G: 108, B: 117},
112		HasBackgroundRGB: true,
113		BackgroundRGB:    core.RGB{R: 40, G: 44, B: 52},
114	}
115	return []storageBenchmarkScenario{
116		{
117			name: "plain-ascii",
118			fill: func(row []core.Cell, y int) {
119				for x := range row {
120					row[x] = core.Cell{Rune: rune('a' + (x+y)%26), Style: defaultStyle}
121				}
122			},
123		},
124		{
125			name: "repeated-indexed-style",
126			fill: func(row []core.Cell, y int) {
127				for x := range row {
128					row[x] = core.Cell{Rune: rune('a' + (x+y)%26), Style: repeatedIndexed}
129				}
130			},
131		},
132		{
133			name: "repeated-rgb-style",
134			fill: func(row []core.Cell, y int) {
135				for x := range row {
136					row[x] = core.Cell{Rune: rune('a' + (x+y)%26), Style: repeatedRGB}
137				}
138			},
139		},
140		{
141			name: "high-cardinality-rgb",
142			fill: func(row []core.Cell, y int) {
143				for x := range row {
144					value := uint32(y*len(row) + x)
145					style := core.Style{
146						Foreground:       -1,
147						Background:       -1,
148						HasForegroundRGB: true,
149						ForegroundRGB: core.RGB{
150							R: uint8(value >> 16),
151							G: uint8(value >> 8),
152							B: uint8(value),
153						},
154					}
155					row[x] = core.Cell{Rune: 'x', Style: style}
156				}
157			},
158		},
159		{
160			name: "wide-unicode",
161			fill: func(row []core.Cell, _ int) {
162				for x := 0; x < len(row); x += 2 {
163					row[x] = core.Cell{Rune: '界', Style: defaultStyle}
164					row[x+1] = core.Cell{Style: defaultStyle, Continuation: true}
165				}
166			},
167		},
168		{
169			name: "styled-blanks",
170			fill: func(row []core.Cell, y int) {
171				style := core.Style{Foreground: -1, Background: (y % 16) + 232}
172				for x := range row {
173					row[x] = core.Cell{Rune: ' ', Style: style}
174				}
175			},
176		},
177	}
178}
179
180func buildStorageBenchmarkHistory(b *testing.B, fill func([]core.Cell, int)) *History {
181	b.Helper()
182	history := NewHistory(HistoryConfig{
183		MaxRows:   storageBenchmarkRows,
184		MaxBytes:  1 << 30,
185		ChunkRows: 256,
186	})
187	row := make([]core.Cell, storageBenchmarkWidth)
188	for y := range storageBenchmarkRows {
189		fill(row, y)
190		if err := history.Append(row, LineBound{End: len(row)}); err != nil {
191			b.Fatal(err)
192		}
193	}
194	return history
195}

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 13

↑↓ moveenter openesc close