Start every render from the zero hold set
This commit is contained in:
@@ -55,7 +55,7 @@ In [docs/decisions.md](docs/decisions.md):
|
|||||||
- A render shares one reference draw per category, per group
|
- A render shares one reference draw per category, per group
|
||||||
- A draw group name is local to its category
|
- A draw group name is local to its category
|
||||||
- The expansion hold and the render's draws are two fences
|
- The expansion hold and the render's draws are two fences
|
||||||
- A record makes its draw maps up front, a `Fake` on its first read
|
- A hold set makes its draw maps on the first read
|
||||||
- A category never references itself, and a record's fences run at load
|
- A category never references itself, and a record's fences run at load
|
||||||
- A record's column set is fixed before the first draw
|
- A record's column set is fixed before the first draw
|
||||||
- Null is a `null` item, not a rate
|
- Null is a `null` item, not a rate
|
||||||
|
|||||||
+8
-7
@@ -242,14 +242,15 @@ groups. They pin different things — an operand pins the value its own render p
|
|||||||
and stops at a reference, a path pins every level it passes through — so one walk would
|
and stops at a reference, a path pins every level it passes through — so one walk would
|
||||||
carry both rules and both scopes anyway, and tell them apart at every step.
|
carry both rules and both scopes anyway, and tell them apart at every step.
|
||||||
|
|
||||||
## A record makes its draw maps up front, a `Fake` on its first read
|
## A hold set makes its draw maps on the first read
|
||||||
|
|
||||||
A record's columns always read through the render's draws, so making the maps where the
|
Every render starts from the zero `holdSet`, per goal 2's one spelling. Making the maps
|
||||||
set is declared keeps them on that frame's stack. A `Fake` often reads no reference path
|
where the set is declared kept a record's on its frame's stack, but a `Fake`'s set lives
|
||||||
at all, and making them anyway cost about a fifth of the cheapest render, so it makes
|
on the `Generator`, so there it cost every render two heap allocations and about half the
|
||||||
them on the first read instead, at two heap allocations for a render that does share a
|
cheapest one's time, measured 2026-09-28. Lazily, a render reading a reference path pays
|
||||||
draw. The allocation gate over a repeat of a reference path and over a named draw group
|
those two allocations, a record or struct included, and one reading none pays nothing;
|
||||||
prices that, and pins what keeps a hold set off the heap.
|
goal 11 holds either way. The allocation gate over a repeat of a reference path and over
|
||||||
|
a named draw group 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
|
## A category never references itself, and a record's fences run at load
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ type renderScope struct {
|
|||||||
pins *pinSet
|
pins *pinSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func eagerHoldSet() holdSet {
|
|
||||||
return holdSet{unnamed: hold{variant: map[string]node{}, value: map[string]readValue{}}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// renderOnce renders n as one render, over a hold set of its own.
|
// renderOnce renders n as one render, over a hold set of its own.
|
||||||
func renderOnce(s *session, n node) string {
|
func renderOnce(s *session, n node) string {
|
||||||
var set holdSet
|
var set holdSet
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func (f *Generator) FakeRecord(path string) (*Record, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("fejkdata: %s: %w", path, err)
|
return nil, fmt.Errorf("fejkdata: %s: %w", path, err)
|
||||||
}
|
}
|
||||||
set := eagerHoldSet()
|
var set holdSet
|
||||||
sc := renderScope{set: &set}
|
sc := renderScope{set: &set}
|
||||||
if t, isTable := n.(*table); isTable {
|
if t, isTable := n.(*table); isTable {
|
||||||
if n, err = tableRecord(f.rand, t, tail, sc); err != nil {
|
if n, err = tableRecord(f.rand, t, tail, sc); err != nil {
|
||||||
@@ -204,7 +204,7 @@ type RecordTemplate struct {
|
|||||||
func (t *RecordTemplate) Fake() *Record {
|
func (t *RecordTemplate) Fake() *Record {
|
||||||
t.g.mu.Lock()
|
t.g.mu.Lock()
|
||||||
defer t.g.mu.Unlock()
|
defer t.g.mu.Unlock()
|
||||||
set := eagerHoldSet()
|
var set holdSet
|
||||||
return renderRecord(t.g.rand, t.template, t.columns, renderScope{set: &set})
|
return renderRecord(t.g.rand, t.template, t.columns, renderScope{set: &set})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ func (p *valueProof) checkField(label string, ft reflect.Type, column node) erro
|
|||||||
// fill draws the record into v's tagged fields, then each nested struct as a record of its own.
|
// fill draws the record into v's tagged fields, then each nested struct as a record of its own.
|
||||||
func (s *structShape) fill(sess *session, v reflect.Value) {
|
func (s *structShape) fill(sess *session, v reflect.Value) {
|
||||||
if s.record != nil {
|
if s.record != nil {
|
||||||
set := eagerHoldSet()
|
var set holdSet
|
||||||
for i, c := range renderRecord(sess, s.record, s.columns, renderScope{set: &set}).columns {
|
for i, c := range renderRecord(sess, s.record, s.columns, renderScope{set: &set}).columns {
|
||||||
setColumn(fieldAt(v, s.fieldIndexes[i]), c)
|
setColumn(fieldAt(v, s.fieldIndexes[i]), c)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ Self-sufficiency 5.6), every seat capped by Locality, and all nine naming
|
|||||||
|
|
||||||
This round, in order:
|
This round, in order:
|
||||||
|
|
||||||
- Start every render from one spelling of an empty hold set: `Fake` uses
|
|
||||||
`holdSet{}`, the record and struct paths `eagerHoldSet()`.
|
|
||||||
- Correct the false comments: `funcCall` names `checkFunc` where `parseFormat`
|
- Correct the false comments: `funcCall` names `checkFunc` where `parseFormat`
|
||||||
reports, `struct.go`'s "t sits at index within c.t" names no field, `recordFormats`
|
reports, `struct.go`'s "t sits at index within c.t" names no field, `recordFormats`
|
||||||
calls ndjson "the same column", `Generator` says every other entry point takes `mu`
|
calls ndjson "the same column", `Generator` says every other entry point takes `mu`
|
||||||
|
|||||||
Reference in New Issue
Block a user