v0.5.0 31affde

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				runtime.GC()
82				runtime.ReadMemStats(&after)
83				if after.HeapAlloc >= before.HeapAlloc {
84					retained += after.HeapAlloc - before.HeapAlloc
85				}
86			}
87			stats := benchmarkStorageHistorySink.CompressionStats()
88			b.ReportMetric(float64(stats.CompressedBytes), "compressed-B")
89			b.ReportMetric(float64(stats.ColdPages), "cold-pages")
90			b.ReportMetric(float64(retained)/float64(b.N), "retained-B/op")
91			benchmarkStorageHistorySink = nil
92		})
93	}
94}
95
96func historyStorageScenarios() []storageBenchmarkScenario {
97	defaultStyle := core.DefaultStyle()
98	repeatedIndexed := core.Style{
99		Bold:           true,
100		Foreground:     196,
101		Background:     17,
102		UnderlineStyle: core.UnderlineSingle,
103	}
104	repeatedRGB := core.Style{
105		Foreground:       -1,
106		Background:       -1,
107		HasForegroundRGB: true,
108		ForegroundRGB:    core.RGB{R: 224, G: 108, B: 117},
109		HasBackgroundRGB: true,
110		BackgroundRGB:    core.RGB{R: 40, G: 44, B: 52},
111	}
112	return []storageBenchmarkScenario{
113		{
114			name: "plain-ascii",
115			fill: func(row []core.Cell, y int) {
116				for x := range row {
117					row[x] = core.Cell{Rune: rune('a' + (x+y)%26), Style: defaultStyle}
118				}
119			},
120		},
121		{
122			name: "repeated-indexed-style",
123			fill: func(row []core.Cell, y int) {
124				for x := range row {
125					row[x] = core.Cell{Rune: rune('a' + (x+y)%26), Style: repeatedIndexed}
126				}
127			},
128		},
129		{
130			name: "repeated-rgb-style",
131			fill: func(row []core.Cell, y int) {
132				for x := range row {
133					row[x] = core.Cell{Rune: rune('a' + (x+y)%26), Style: repeatedRGB}
134				}
135			},
136		},
137		{
138			name: "high-cardinality-rgb",
139			fill: func(row []core.Cell, y int) {
140				for x := range row {
141					value := uint32(y*len(row) + x)
142					style := core.Style{
143						Foreground:       -1,
144						Background:       -1,
145						HasForegroundRGB: true,
146						ForegroundRGB: core.RGB{
147							R: uint8(value >> 16),
148							G: uint8(value >> 8),
149							B: uint8(value),
150						},
151					}
152					row[x] = core.Cell{Rune: 'x', Style: style}
153				}
154			},
155		},
156		{
157			name: "wide-unicode",
158			fill: func(row []core.Cell, _ int) {
159				for x := 0; x < len(row); x += 2 {
160					row[x] = core.Cell{Rune: '界', Style: defaultStyle}
161					row[x+1] = core.Cell{Style: defaultStyle, Continuation: true}
162				}
163			},
164		},
165		{
166			name: "styled-blanks",
167			fill: func(row []core.Cell, y int) {
168				style := core.Style{Foreground: -1, Background: (y % 16) + 232}
169				for x := range row {
170					row[x] = core.Cell{Rune: ' ', Style: style}
171				}
172			},
173		},
174	}
175}
176
177func buildStorageBenchmarkHistory(b *testing.B, fill func([]core.Cell, int)) *History {
178	b.Helper()
179	history := NewHistory(HistoryConfig{
180		MaxRows:   storageBenchmarkRows,
181		MaxBytes:  1 << 30,
182		ChunkRows: 256,
183	})
184	row := make([]core.Cell, storageBenchmarkWidth)
185	for y := range storageBenchmarkRows {
186		fill(row, y)
187		if err := history.Append(row, LineBound{End: len(row)}); err != nil {
188			b.Fatal(err)
189		}
190	}
191	return history
192}

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