From 49296bd0ca9720cc2a408ac9d5f60e5ed7504700 Mon Sep 17 00:00:00 2001 From: Egon Elbre Date: Mon, 23 Oct 2023 19:51:10 +0300 Subject: [PATCH] internal/ops: use uint32 for pc, version, macroID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4GB of render data should be sufficient for anyone. By replacing an int with uint32, it allows for a smaller memory footprint and faster caching. Example impact on rendering static labels. │ before.txt │ after.txt │ │ sec/op │ sec/op vs base │ LabelStatic/1000runes-RTL-arabic-32 98.08µ ± 3% 88.17µ ± 1% -10.10% (p=0.002 n=6) LabelStatic/1000runes-RTL-complex-32 103.9µ ± 2% 101.9µ ± 1% -1.84% (p=0.009 n=6) LabelStatic/1000runes-RTL-emoji-32 113.3µ ± 2% 100.7µ ± 3% -11.11% (p=0.002 n=6) LabelStatic/1000runes-RTL-latin-32 100.01µ ± 1% 92.31µ ± 1% -7.69% (p=0.002 n=6) LabelStatic/1000runes-LTR-arabic-32 97.90µ ± 2% 87.92µ ± 2% -10.19% (p=0.002 n=6) LabelStatic/1000runes-LTR-complex-32 102.63µ ± 2% 99.81µ ± 1% -2.75% (p=0.002 n=6) LabelStatic/1000runes-LTR-emoji-32 106.56µ ± 2% 98.47µ ± 1% -7.59% (p=0.002 n=6) LabelStatic/1000runes-LTR-latin-32 97.51µ ± 1% 92.60µ ± 3% -5.03% (p=0.002 n=6) geomean 102.4µ 95.09µ -7.10% Signed-off-by: Egon Elbre --- internal/ops/ops.go | 34 +++++++++++++++++----------------- internal/ops/reader.go | 26 +++++++++++++------------- io/pointer/pointer.go | 2 +- op/clip/clip.go | 2 +- op/op.go | 2 +- op/paint/paint.go | 2 +- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/internal/ops/ops.go b/internal/ops/ops.go index 068c9ee0..0ba83a71 100644 --- a/internal/ops/ops.go +++ b/internal/ops/ops.go @@ -14,7 +14,7 @@ import ( type Ops struct { // version is incremented at each Reset. - version int + version uint32 // data contains the serialized operations. data []byte // refs hold external references for operations. @@ -32,7 +32,7 @@ type Ops struct { stringRefs []string // nextStateID is the id allocated for the next // StateOp. - nextStateID int + nextStateID uint32 // multipOp indicates a multi-op such as clip.Path is being added. multipOp bool @@ -91,23 +91,23 @@ const ( ) type StackID struct { - id int - prev int + id uint32 + prev uint32 } // StateOp represents a saved operation snapshot to be restored // later. type StateOp struct { - id int - macroID int + id uint32 + macroID uint32 ops *Ops } // stack tracks the integer identities of stack operations to ensure correct // pairing of their push and pop methods. type stack struct { - currentID int - nextID int + currentID uint32 + nextID uint32 } type StackKind uint8 @@ -266,11 +266,11 @@ func AddCall(o *Ops, callOps *Ops, pc PC, end PC) { bo.PutUint32(data[13:], uint32(end.refs)) } -func PushOp(o *Ops, kind StackKind) (StackID, int) { +func PushOp(o *Ops, kind StackKind) (StackID, uint32) { return o.stacks[kind].push(), o.macroStack.currentID } -func PopOp(o *Ops, kind StackKind, sid StackID, macroID int) { +func PopOp(o *Ops, kind StackKind, sid StackID, macroID uint32) { if o.macroStack.currentID != macroID { panic("stack push and pop must not cross macro boundary") } @@ -310,7 +310,7 @@ func Write3(o *Ops, n int, ref1, ref2, ref3 interface{}) []byte { } func PCFor(o *Ops) PC { - return PC{data: len(o.data), refs: len(o.refs)} + return PC{data: uint32(len(o.data)), refs: uint32(len(o.refs))} } func (s *stack) push() StackID { @@ -460,17 +460,17 @@ var opProps = [0x100]opProp{ TypeActionInput: {Size: TypeActionInputLen, NumRefs: 0}, } -func (t OpType) props() (size, numRefs int) { +func (t OpType) props() (size, numRefs uint32) { v := opProps[t] - return int(v.Size), int(v.NumRefs) + return uint32(v.Size), uint32(v.NumRefs) } -func (t OpType) Size() int { - return int(opProps[t].Size) +func (t OpType) Size() uint32 { + return uint32(opProps[t].Size) } -func (t OpType) NumRefs() int { - return int(opProps[t].NumRefs) +func (t OpType) NumRefs() uint32 { + return uint32(opProps[t].NumRefs) } func (t OpType) String() string { diff --git a/internal/ops/reader.go b/internal/ops/reader.go index 394bfcc9..5ed78ff7 100644 --- a/internal/ops/reader.go +++ b/internal/ops/reader.go @@ -26,8 +26,8 @@ type EncodedOp struct { // Key is a unique key for a given op. type Key struct { ops *Ops - pc int - version int + pc uint32 + version uint32 } // Shadow of op.MacroOp. @@ -39,8 +39,8 @@ type macroOp struct { // PC is an instruction counter for an operation list. type PC struct { - data int - refs int + data uint32 + refs uint32 } type macro struct { @@ -128,7 +128,7 @@ func (r *Reader) Decode() (EncodedOp, bool) { if nrefs != 1 { panic("internal error: unexpected number of macro refs") } - deferData := Write1(&r.deferOps, n, refs[0]) + deferData := Write1(&r.deferOps, int(n), refs[0]) copy(deferData, data) r.pc.data += n r.pc.refs += nrefs @@ -154,8 +154,8 @@ func (r *Reader) Decode() (EncodedOp, bool) { r.pc = op.endpc } else { // Treat an incomplete macro as containing all remaining ops. - r.pc.data = len(r.ops.data) - r.pc.refs = len(r.ops.refs) + r.pc.data = uint32(len(r.ops.data)) + r.pc.refs = uint32(len(r.ops.refs)) } continue } @@ -171,8 +171,8 @@ func (op *opMacroDef) decode(data []byte) { } bo := binary.LittleEndian data = data[:TypeMacroLen] - op.endpc.data = int(int32(bo.Uint32(data[1:]))) - op.endpc.refs = int(int32(bo.Uint32(data[5:]))) + op.endpc.data = bo.Uint32(data[1:]) + op.endpc.refs = bo.Uint32(data[5:]) } func (m *macroOp) decode(data []byte, refs []interface{}) { @@ -183,8 +183,8 @@ func (m *macroOp) decode(data []byte, refs []interface{}) { data = data[:TypeCallLen] m.ops = refs[0].(*Ops) - m.start.data = int(int32(bo.Uint32(data[1:]))) - m.start.refs = int(int32(bo.Uint32(data[5:]))) - m.end.data = int(int32(bo.Uint32(data[9:]))) - m.end.refs = int(int32(bo.Uint32(data[13:]))) + m.start.data = bo.Uint32(data[1:]) + m.start.refs = bo.Uint32(data[5:]) + m.end.data = bo.Uint32(data[9:]) + m.end.refs = bo.Uint32(data[13:]) } diff --git a/io/pointer/pointer.go b/io/pointer/pointer.go index 60619389..ae734eed 100644 --- a/io/pointer/pointer.go +++ b/io/pointer/pointer.go @@ -51,7 +51,7 @@ type PassOp struct { type PassStack struct { ops *ops.Ops id ops.StackID - macroID int + macroID uint32 } // InputOp declares an input handler ready for pointer diff --git a/op/clip/clip.go b/op/clip/clip.go index 279077ea..98024ab9 100644 --- a/op/clip/clip.go +++ b/op/clip/clip.go @@ -29,7 +29,7 @@ type Op struct { type Stack struct { ops *ops.Ops id ops.StackID - macroID int + macroID uint32 } var pathSeed maphash.Seed diff --git a/op/op.go b/op/op.go index 07947c59..e183d271 100644 --- a/op/op.go +++ b/op/op.go @@ -111,7 +111,7 @@ type TransformOp struct { // TransformStack represents a TransformOp pushed on the transformation stack. type TransformStack struct { id ops.StackID - macroID int + macroID uint32 ops *ops.Ops } diff --git a/op/paint/paint.go b/op/paint/paint.go index 2a4e05d6..59583ed7 100644 --- a/op/paint/paint.go +++ b/op/paint/paint.go @@ -48,7 +48,7 @@ type PaintOp struct { // until Pop is called. type OpacityStack struct { id ops.StackID - macroID int + macroID uint32 ops *ops.Ops }