test(core): benchmark layout, snap points and border resize motion
- commit
- 04334a42120ff5ea116f50b3ab0fbb44b15892db
- author
- brice · · 2026-10-21 08:04 UTC
- committer
- brice · · 2026-10-21 08:07 UTC
- parent
- fac869b
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +116 -0
A internal/core/layout_bench_test.go +116 -0
| @@ -0,0 +1,116 @@ | |||
| 1 | + | package core | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "context" | |
| 5 | + | "fmt" | |
| 6 | + | "testing" | |
| 7 | + | ||
| 8 | + | "github.com/bnema/neferwl/internal/ports" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | func benchLane(n int, stamped bool) *Workspace { | |
| 12 | + | w := spanWorkspace(OverflowScroll, n) | |
| 13 | + | if stamped { | |
| 14 | + | stampSpans(w, 0, n-1) | |
| 15 | + | } | |
| 16 | + | w.Focus = n / 2 | |
| 17 | + | return w | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | func benchLanes(b *testing.B, f func(b *testing.B, w *Workspace)) { | |
| 21 | + | for _, n := range []int{16, 64} { | |
| 22 | + | for _, stamped := range []bool{false, true} { | |
| 23 | + | b.Run(fmt.Sprintf("cols=%d/stamped=%t", n, stamped), func(b *testing.B) { | |
| 24 | + | w := benchLane(n, stamped) | |
| 25 | + | b.ReportAllocs() | |
| 26 | + | b.ResetTimer() | |
| 27 | + | f(b, w) | |
| 28 | + | }) | |
| 29 | + | } | |
| 30 | + | } | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | func BenchmarkSnapPoints(b *testing.B) { | |
| 34 | + | benchLanes(b, func(b *testing.B, w *Workspace) { | |
| 35 | + | for b.Loop() { | |
| 36 | + | _ = w.snapPoints() | |
| 37 | + | } | |
| 38 | + | }) | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | func BenchmarkLayout(b *testing.B) { | |
| 42 | + | benchLanes(b, func(b *testing.B, w *Workspace) { | |
| 43 | + | for b.Loop() { | |
| 44 | + | _ = w.Layout() | |
| 45 | + | } | |
| 46 | + | }) | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | func BenchmarkColumnRects(b *testing.B) { | |
| 50 | + | benchLanes(b, func(b *testing.B, w *Workspace) { | |
| 51 | + | var buf []Rect | |
| 52 | + | for b.Loop() { | |
| 53 | + | buf = w.columnRectsInto(buf, false) | |
| 54 | + | } | |
| 55 | + | }) | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | func BenchmarkScroll(b *testing.B) { | |
| 59 | + | benchLanes(b, func(b *testing.B, w *Workspace) { | |
| 60 | + | for b.Loop() { | |
| 61 | + | w.scroll() | |
| 62 | + | } | |
| 63 | + | }) | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | // BenchmarkBorderMotion is one pointer motion of a border resize with a | |
| 67 | + | // publish; "same" repeats a position, so nothing changes after the first. | |
| 68 | + | func BenchmarkBorderMotion(b *testing.B) { | |
| 69 | + | for _, mode := range []string{"moving", "same"} { | |
| 70 | + | b.Run(mode, func(b *testing.B) { | |
| 71 | + | var cfg ports.Config | |
| 72 | + | cfg.Keyboard.CmdKey = "super" | |
| 73 | + | cfg.Layout.MaxColumns = 3 | |
| 74 | + | scenes, cmds := make(chan []ports.Scene, 1), make(chan ports.ClientCommand, 1024) | |
| 75 | + | c, err := New(cfg, Channels{Scenes: scenes, Commands: cmds}, Options{}) | |
| 76 | + | if err != nil { | |
| 77 | + | b.Fatal(err) | |
| 78 | + | } | |
| 79 | + | c.addScreen(ports.OutputInfo{Name: "A", Width: 1920, Height: 1080}) | |
| 80 | + | sc := c.cur() | |
| 81 | + | w := sc.mon.Current() | |
| 82 | + | w.Overflow = OverflowScroll | |
| 83 | + | for id := WindowID(1); id <= 12; id++ { | |
| 84 | + | w.AddWindow(id) | |
| 85 | + | } | |
| 86 | + | sc.arrange() | |
| 87 | + | ctx := context.Background() | |
| 88 | + | if err := c.publish(ctx); err != nil { | |
| 89 | + | b.Fatal(err) | |
| 90 | + | } | |
| 91 | + | rs := w.columnRects() | |
| 92 | + | g := newBorderResize(c, sc, w, border{kind: borderColumn, col: 0}, btnRight) | |
| 93 | + | x := float64(rs[0].X + rs[0].W) | |
| 94 | + | c.cursorY = 100 | |
| 95 | + | n := 0 | |
| 96 | + | b.ReportAllocs() | |
| 97 | + | b.ResetTimer() | |
| 98 | + | for b.Loop() { | |
| 99 | + | c.cursorX = x | |
| 100 | + | if mode == "moving" { | |
| 101 | + | c.cursorX += float64(n%2*8) + 8 | |
| 102 | + | n++ | |
| 103 | + | } | |
| 104 | + | for len(scenes) > 0 { | |
| 105 | + | <-scenes | |
| 106 | + | } | |
| 107 | + | for len(cmds) > 0 { | |
| 108 | + | <-cmds | |
| 109 | + | } | |
| 110 | + | if err := g.motion(ctx, 0); err != nil { | |
| 111 | + | b.Fatal(err) | |
| 112 | + | } | |
| 113 | + | } | |
| 114 | + | }) | |
| 115 | + | } | |
| 116 | + | } | |
| @@ -0,0 +1,116 @@ | |||||
| 1 | + | package core | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "context" | |||
| 5 | + | "fmt" | |||
| 6 | + | "testing" | |||
| 7 | + | ||||
| 8 | + | "github.com/bnema/neferwl/internal/ports" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | func benchLane(n int, stamped bool) *Workspace { | |||
| 12 | + | w := spanWorkspace(OverflowScroll, n) | |||
| 13 | + | if stamped { | |||
| 14 | + | stampSpans(w, 0, n-1) | |||
| 15 | + | } | |||
| 16 | + | w.Focus = n / 2 | |||
| 17 | + | return w | |||
| 18 | + | } | |||
| 19 | + | ||||
| 20 | + | func benchLanes(b *testing.B, f func(b *testing.B, w *Workspace)) { | |||
| 21 | + | for _, n := range []int{16, 64} { | |||
| 22 | + | for _, stamped := range []bool{false, true} { | |||
| 23 | + | b.Run(fmt.Sprintf("cols=%d/stamped=%t", n, stamped), func(b *testing.B) { | |||
| 24 | + | w := benchLane(n, stamped) | |||
| 25 | + | b.ReportAllocs() | |||
| 26 | + | b.ResetTimer() | |||
| 27 | + | f(b, w) | |||
| 28 | + | }) | |||
| 29 | + | } | |||
| 30 | + | } | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | func BenchmarkSnapPoints(b *testing.B) { | |||
| 34 | + | benchLanes(b, func(b *testing.B, w *Workspace) { | |||
| 35 | + | for b.Loop() { | |||
| 36 | + | _ = w.snapPoints() | |||
| 37 | + | } | |||
| 38 | + | }) | |||
| 39 | + | } | |||
| 40 | + | ||||
| 41 | + | func BenchmarkLayout(b *testing.B) { | |||
| 42 | + | benchLanes(b, func(b *testing.B, w *Workspace) { | |||
| 43 | + | for b.Loop() { | |||
| 44 | + | _ = w.Layout() | |||
| 45 | + | } | |||
| 46 | + | }) | |||
| 47 | + | } | |||
| 48 | + | ||||
| 49 | + | func BenchmarkColumnRects(b *testing.B) { | |||
| 50 | + | benchLanes(b, func(b *testing.B, w *Workspace) { | |||
| 51 | + | var buf []Rect | |||
| 52 | + | for b.Loop() { | |||
| 53 | + | buf = w.columnRectsInto(buf, false) | |||
| 54 | + | } | |||
| 55 | + | }) | |||
| 56 | + | } | |||
| 57 | + | ||||
| 58 | + | func BenchmarkScroll(b *testing.B) { | |||
| 59 | + | benchLanes(b, func(b *testing.B, w *Workspace) { | |||
| 60 | + | for b.Loop() { | |||
| 61 | + | w.scroll() | |||
| 62 | + | } | |||
| 63 | + | }) | |||
| 64 | + | } | |||
| 65 | + | ||||
| 66 | + | // BenchmarkBorderMotion is one pointer motion of a border resize with a | |||
| 67 | + | // publish; "same" repeats a position, so nothing changes after the first. | |||
| 68 | + | func BenchmarkBorderMotion(b *testing.B) { | |||
| 69 | + | for _, mode := range []string{"moving", "same"} { | |||
| 70 | + | b.Run(mode, func(b *testing.B) { | |||
| 71 | + | var cfg ports.Config | |||
| 72 | + | cfg.Keyboard.CmdKey = "super" | |||
| 73 | + | cfg.Layout.MaxColumns = 3 | |||
| 74 | + | scenes, cmds := make(chan []ports.Scene, 1), make(chan ports.ClientCommand, 1024) | |||
| 75 | + | c, err := New(cfg, Channels{Scenes: scenes, Commands: cmds}, Options{}) | |||
| 76 | + | if err != nil { | |||
| 77 | + | b.Fatal(err) | |||
| 78 | + | } | |||
| 79 | + | c.addScreen(ports.OutputInfo{Name: "A", Width: 1920, Height: 1080}) | |||
| 80 | + | sc := c.cur() | |||
| 81 | + | w := sc.mon.Current() | |||
| 82 | + | w.Overflow = OverflowScroll | |||
| 83 | + | for id := WindowID(1); id <= 12; id++ { | |||
| 84 | + | w.AddWindow(id) | |||
| 85 | + | } | |||
| 86 | + | sc.arrange() | |||
| 87 | + | ctx := context.Background() | |||
| 88 | + | if err := c.publish(ctx); err != nil { | |||
| 89 | + | b.Fatal(err) | |||
| 90 | + | } | |||
| 91 | + | rs := w.columnRects() | |||
| 92 | + | g := newBorderResize(c, sc, w, border{kind: borderColumn, col: 0}, btnRight) | |||
| 93 | + | x := float64(rs[0].X + rs[0].W) | |||
| 94 | + | c.cursorY = 100 | |||
| 95 | + | n := 0 | |||
| 96 | + | b.ReportAllocs() | |||
| 97 | + | b.ResetTimer() | |||
| 98 | + | for b.Loop() { | |||
| 99 | + | c.cursorX = x | |||
| 100 | + | if mode == "moving" { | |||
| 101 | + | c.cursorX += float64(n%2*8) + 8 | |||
| 102 | + | n++ | |||
| 103 | + | } | |||
| 104 | + | for len(scenes) > 0 { | |||
| 105 | + | <-scenes | |||
| 106 | + | } | |||
| 107 | + | for len(cmds) > 0 { | |||
| 108 | + | <-cmds | |||
| 109 | + | } | |||
| 110 | + | if err := g.motion(ctx, 0); err != nil { | |||
| 111 | + | b.Fatal(err) | |||
| 112 | + | } | |||
| 113 | + | } | |||
| 114 | + | }) | |||
| 115 | + | } | |||
| 116 | + | } | |||