diff --git a/docs/decisions.md b/docs/decisions.md index 4abc5f2..721f41f 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -249,7 +249,7 @@ set is declared keeps them on that frame's stack. A `Fake` often reads no refere at all, and making them anyway cost about a fifth of the cheapest render, so it makes them on the first read instead, at two heap allocations for a render that does share a draw. The allocation gate over a repeat of a reference path and over a named draw group -prices that, and pins the two measures that keep a hold set off the heap. +prices that, and pins what keeps a hold set off the heap. ## A category never references itself, and a record's fences run at load diff --git a/drawfence_test.go b/drawfence_test.go index 91e6bfb..6d28bf7 100644 --- a/drawfence_test.go +++ b/drawfence_test.go @@ -73,9 +73,6 @@ func TestEveryReadARenderMakesIsGathered(t *testing.T) { } } -// wantGathered renders a root five times over the seeded stream and fails where a reference the -// render read is not among gathered: same draw group and path, and where the render stood in a -// table's row, gathered from that row or from no row of the table. func wantGathered(t *testing.T, s *session, tables map[string]*table, label string, gathered []pathRead, renderOnce func()) { t.Helper() var reads []pathRead diff --git a/holdset.go b/holdset.go index 4cdc4fe..ab91287 100644 --- a/holdset.go +++ b/holdset.go @@ -11,7 +11,9 @@ type holdSet struct { } // renderScope is where a render reads its reference paths: a hold set, in the draw group of the -// template rendering, and the row a table's format is rendering, which its columns read. +// template rendering, and the row a table's format is rendering, which its columns read. It passes +// by value, and what is read from it reaches a map key, an interface or a func value only as a +// copy; else sc, and with it every render's hold set, moves to the heap. type renderScope struct { set *holdSet group string @@ -60,7 +62,7 @@ func (sc renderScope) hold() *hold { sc.set.named = map[string]*hold{} } d = &hold{variant: map[string]node{}, value: map[string]draw{}} - sc.set.named[strings.Clone(sc.group)] = d // a key from sc would leak sc, and with it every render's hold set, to the heap + sc.set.named[strings.Clone(sc.group)] = d } return d } diff --git a/perf_test.go b/perf_test.go index 4491566..7336cf6 100644 --- a/perf_test.go +++ b/perf_test.go @@ -55,7 +55,7 @@ func TestNoRenderAllocRegression(t *testing.T) { } } -// The repeat shape prices both escape measures: dropping either costs an alloc an iteration. +// The repeat shape prices what keeps a hold set off the heap: a copy dropped costs an alloc an iteration. func TestNoReferenceAllocRegression(t *testing.T) { word := `{"format":"{w}","w":["alpha","beta","gamma","delta"]}` for _, s := range []struct {