internal/ops: use uint32 for pc, version, macroID

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 <egonelbre@gmail.com>
This commit is contained in:
Egon Elbre
2023-10-23 19:51:10 +03:00
committed by Chris Waldon
parent d078bf0ed7
commit 49296bd0ca
6 changed files with 34 additions and 34 deletions
+17 -17
View File
@@ -14,7 +14,7 @@ import (
type Ops struct { type Ops struct {
// version is incremented at each Reset. // version is incremented at each Reset.
version int version uint32
// data contains the serialized operations. // data contains the serialized operations.
data []byte data []byte
// refs hold external references for operations. // refs hold external references for operations.
@@ -32,7 +32,7 @@ type Ops struct {
stringRefs []string stringRefs []string
// nextStateID is the id allocated for the next // nextStateID is the id allocated for the next
// StateOp. // StateOp.
nextStateID int nextStateID uint32
// multipOp indicates a multi-op such as clip.Path is being added. // multipOp indicates a multi-op such as clip.Path is being added.
multipOp bool multipOp bool
@@ -91,23 +91,23 @@ const (
) )
type StackID struct { type StackID struct {
id int id uint32
prev int prev uint32
} }
// StateOp represents a saved operation snapshot to be restored // StateOp represents a saved operation snapshot to be restored
// later. // later.
type StateOp struct { type StateOp struct {
id int id uint32
macroID int macroID uint32
ops *Ops ops *Ops
} }
// stack tracks the integer identities of stack operations to ensure correct // stack tracks the integer identities of stack operations to ensure correct
// pairing of their push and pop methods. // pairing of their push and pop methods.
type stack struct { type stack struct {
currentID int currentID uint32
nextID int nextID uint32
} }
type StackKind uint8 type StackKind uint8
@@ -266,11 +266,11 @@ func AddCall(o *Ops, callOps *Ops, pc PC, end PC) {
bo.PutUint32(data[13:], uint32(end.refs)) 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 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 { if o.macroStack.currentID != macroID {
panic("stack push and pop must not cross macro boundary") 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 { 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 { func (s *stack) push() StackID {
@@ -460,17 +460,17 @@ var opProps = [0x100]opProp{
TypeActionInput: {Size: TypeActionInputLen, NumRefs: 0}, TypeActionInput: {Size: TypeActionInputLen, NumRefs: 0},
} }
func (t OpType) props() (size, numRefs int) { func (t OpType) props() (size, numRefs uint32) {
v := opProps[t] v := opProps[t]
return int(v.Size), int(v.NumRefs) return uint32(v.Size), uint32(v.NumRefs)
} }
func (t OpType) Size() int { func (t OpType) Size() uint32 {
return int(opProps[t].Size) return uint32(opProps[t].Size)
} }
func (t OpType) NumRefs() int { func (t OpType) NumRefs() uint32 {
return int(opProps[t].NumRefs) return uint32(opProps[t].NumRefs)
} }
func (t OpType) String() string { func (t OpType) String() string {
+13 -13
View File
@@ -26,8 +26,8 @@ type EncodedOp struct {
// Key is a unique key for a given op. // Key is a unique key for a given op.
type Key struct { type Key struct {
ops *Ops ops *Ops
pc int pc uint32
version int version uint32
} }
// Shadow of op.MacroOp. // Shadow of op.MacroOp.
@@ -39,8 +39,8 @@ type macroOp struct {
// PC is an instruction counter for an operation list. // PC is an instruction counter for an operation list.
type PC struct { type PC struct {
data int data uint32
refs int refs uint32
} }
type macro struct { type macro struct {
@@ -128,7 +128,7 @@ func (r *Reader) Decode() (EncodedOp, bool) {
if nrefs != 1 { if nrefs != 1 {
panic("internal error: unexpected number of macro refs") 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) copy(deferData, data)
r.pc.data += n r.pc.data += n
r.pc.refs += nrefs r.pc.refs += nrefs
@@ -154,8 +154,8 @@ func (r *Reader) Decode() (EncodedOp, bool) {
r.pc = op.endpc r.pc = op.endpc
} else { } else {
// Treat an incomplete macro as containing all remaining ops. // Treat an incomplete macro as containing all remaining ops.
r.pc.data = len(r.ops.data) r.pc.data = uint32(len(r.ops.data))
r.pc.refs = len(r.ops.refs) r.pc.refs = uint32(len(r.ops.refs))
} }
continue continue
} }
@@ -171,8 +171,8 @@ func (op *opMacroDef) decode(data []byte) {
} }
bo := binary.LittleEndian bo := binary.LittleEndian
data = data[:TypeMacroLen] data = data[:TypeMacroLen]
op.endpc.data = int(int32(bo.Uint32(data[1:]))) op.endpc.data = bo.Uint32(data[1:])
op.endpc.refs = int(int32(bo.Uint32(data[5:]))) op.endpc.refs = bo.Uint32(data[5:])
} }
func (m *macroOp) decode(data []byte, refs []interface{}) { func (m *macroOp) decode(data []byte, refs []interface{}) {
@@ -183,8 +183,8 @@ func (m *macroOp) decode(data []byte, refs []interface{}) {
data = data[:TypeCallLen] data = data[:TypeCallLen]
m.ops = refs[0].(*Ops) m.ops = refs[0].(*Ops)
m.start.data = int(int32(bo.Uint32(data[1:]))) m.start.data = bo.Uint32(data[1:])
m.start.refs = int(int32(bo.Uint32(data[5:]))) m.start.refs = bo.Uint32(data[5:])
m.end.data = int(int32(bo.Uint32(data[9:]))) m.end.data = bo.Uint32(data[9:])
m.end.refs = int(int32(bo.Uint32(data[13:]))) m.end.refs = bo.Uint32(data[13:])
} }
+1 -1
View File
@@ -51,7 +51,7 @@ type PassOp struct {
type PassStack struct { type PassStack struct {
ops *ops.Ops ops *ops.Ops
id ops.StackID id ops.StackID
macroID int macroID uint32
} }
// InputOp declares an input handler ready for pointer // InputOp declares an input handler ready for pointer
+1 -1
View File
@@ -29,7 +29,7 @@ type Op struct {
type Stack struct { type Stack struct {
ops *ops.Ops ops *ops.Ops
id ops.StackID id ops.StackID
macroID int macroID uint32
} }
var pathSeed maphash.Seed var pathSeed maphash.Seed
+1 -1
View File
@@ -111,7 +111,7 @@ type TransformOp struct {
// TransformStack represents a TransformOp pushed on the transformation stack. // TransformStack represents a TransformOp pushed on the transformation stack.
type TransformStack struct { type TransformStack struct {
id ops.StackID id ops.StackID
macroID int macroID uint32
ops *ops.Ops ops *ops.Ops
} }
+1 -1
View File
@@ -48,7 +48,7 @@ type PaintOp struct {
// until Pop is called. // until Pop is called.
type OpacityStack struct { type OpacityStack struct {
id ops.StackID id ops.StackID
macroID int macroID uint32
ops *ops.Ops ops *ops.Ops
} }