Skip to content

Commit

Permalink
perf: machine string (#1994)
Browse files Browse the repository at this point in the history
Make the `String` method on the `Machine` struct faster by preallocating
a string builder.

[issue](#1981)
  • Loading branch information
petar-dambovaliev authored May 3, 2024
1 parent 56b5bc0 commit 0fc011a
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 70 deletions.
25 changes: 25 additions & 0 deletions gnovm/pkg/gnolang/gno_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,31 @@ import (
"github.com/stretchr/testify/require"
)

func setupMachine(b *testing.B, numValues, numStmts, numExprs, numBlocks, numFrames, numExceptions int) *Machine {
b.Helper()

m := &Machine{
Ops: make([]Op, 100),
NumOps: 100,
Values: make([]TypedValue, numValues),
NumValues: numValues,
Exprs: make([]Expr, numExprs),
Stmts: make([]Stmt, numStmts),
Blocks: make([]*Block, numBlocks),
Frames: make([]*Frame, numFrames),
Exceptions: make([]Exception, numExceptions),
}
return m
}

func BenchmarkStringLargeData(b *testing.B) {
m := setupMachine(b, 10000, 5000, 5000, 2000, 3000, 1000)

for i := 0; i < b.N; i++ {
_ = m.String()
}
}

func TestRunInvalidLabels(t *testing.T) {
tests := []struct {
code string
Expand Down
126 changes: 57 additions & 69 deletions gnovm/pkg/gnolang/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -2024,117 +2024,105 @@ func (m *Machine) Printf(format string, args ...interface{}) {
}

func (m *Machine) String() string {
vs := []string{}
// Calculate some reasonable total length to avoid reallocation
// Assuming an average length of 32 characters per string
var (
vsLength = m.NumValues * 32
ssLength = len(m.Stmts) * 32
xsLength = len(m.Exprs) * 32
bsLength = 1024
obsLength = len(m.Blocks) * 32
fsLength = len(m.Frames) * 32
exceptionsLength = len(m.Exceptions)

totalLength = vsLength + ssLength + xsLength + bsLength + obsLength + fsLength + exceptionsLength
)

var builder strings.Builder
builder.Grow(totalLength)

builder.WriteString(fmt.Sprintf("Machine:\n CheckTypes: %v\n Op: %v\n Values: (len: %d)\n", m.CheckTypes, m.Ops[:m.NumOps], m.NumValues))

for i := m.NumValues - 1; i >= 0; i-- {
v := m.Values[i]
vs = append(vs, fmt.Sprintf(" #%d %v", i, v))
builder.WriteString(fmt.Sprintf(" #%d %v\n", i, m.Values[i]))
}
ss := []string{}
for i := len(m.Stmts) - 1; i >= 0; i-- {
s := m.Stmts[i]
ss = append(ss, fmt.Sprintf(" #%d %v", i, s))
}
xs := []string{}

for i := len(m.Exprs) - 1; i >= 0; i-- {
x := m.Exprs[i]
xs = append(xs, fmt.Sprintf(" #%d %v", i, x))
builder.WriteString(fmt.Sprintf(" #%d %v\n", i, m.Exprs[i]))
}
bs := []string{}

for i := len(m.Stmts) - 1; i >= 0; i-- {
builder.WriteString(fmt.Sprintf(" #%d %v\n", i, m.Stmts[i]))
}

for b := m.LastBlock(); b != nil; {
gen := len(bs)/3 + 1
gen := builder.Len()/3 + 1
gens := "@" // strings.Repeat("@", gen)

if pv, ok := b.Source.(*PackageNode); ok {
// package blocks have too much, so just
// print the pkgpath.
bs = append(bs, fmt.Sprintf(" %s(%d) %s", gens, gen, pv.PkgPath))
builder.WriteString(fmt.Sprintf(" %s(%d) %s\n", gens, gen, pv.PkgPath))
} else {
bsi := b.StringIndented(" ")
bs = append(bs, fmt.Sprintf(" %s(%d) %s", gens, gen, bsi))
builder.WriteString(fmt.Sprintf(" %s(%d) %s\n", gens, gen, bsi))

if b.Source != nil {
sb := b.GetSource(m.Store).GetStaticBlock().GetBlock()
bs = append(bs, fmt.Sprintf(" (s vals) %s(%d) %s", gens, gen,
sb.StringIndented(" ")))
builder.WriteString(fmt.Sprintf(" (s vals) %s(%d) %s\n", gens, gen, sb.StringIndented(" ")))

sts := b.GetSource(m.Store).GetStaticBlock().Types
bs = append(bs, fmt.Sprintf(" (s typs) %s(%d) %s", gens, gen,
sts))
builder.WriteString(fmt.Sprintf(" (s typs) %s(%d) %s\n", gens, gen, sts))
}
}
// b = b.Parent.(*Block|RefValue)

// Update b
switch bp := b.Parent.(type) {
case nil:
b = nil
break
case *Block:
b = bp
case RefValue:
bs = append(bs, fmt.Sprintf(" (block ref %v)", bp.ObjectID))
builder.WriteString(fmt.Sprintf(" (block ref %v)\n", bp.ObjectID))
b = nil
break
default:
panic("should not happen")
}
}
obs := []string{}

for i := len(m.Blocks) - 2; i >= 0; i-- {
b := m.Blocks[i]

if b == nil || b.Source == nil {
continue
}

if _, ok := b.Source.(*PackageNode); ok {
break // done, skip *PackageNode.
} else {
obs = append(obs, fmt.Sprintf(" #%d %s", i,
builder.WriteString(fmt.Sprintf(" #%d %s", i,
b.StringIndented(" ")))
if b.Source != nil {
sb := b.GetSource(m.Store).GetStaticBlock().GetBlock()
obs = append(obs, fmt.Sprintf(" (static) #%d %s", i,
builder.WriteString(fmt.Sprintf(" (static) #%d %s", i,
sb.StringIndented(" ")))
}
}
}
fs := []string{}

for i := len(m.Frames) - 1; i >= 0; i-- {
fr := m.Frames[i]
fs = append(fs, fmt.Sprintf(" #%d %s", i, fr.String()))
builder.WriteString(fmt.Sprintf(" #%d %s\n", i, m.Frames[i]))
}
rlmpath := ""

if m.Realm != nil {
rlmpath = m.Realm.Path
}
exceptions := make([]string, len(m.Exceptions))
for i, ex := range m.Exceptions {
exceptions[i] = ex.Sprint(m)
}
return fmt.Sprintf(`Machine:
CheckTypes: %v
Op: %v
Values: (len: %d)
%s
Exprs:
%s
Stmts:
%s
Blocks:
%s
Blocks (other):
%s
Frames:
%s
Realm:
%s
Exceptions:
%s
%s`,
m.CheckTypes,
m.Ops[:m.NumOps],
m.NumValues,
strings.Join(vs, "\n"),
strings.Join(xs, "\n"),
strings.Join(ss, "\n"),
strings.Join(bs, "\n"),
strings.Join(obs, "\n"),
strings.Join(fs, "\n"),
rlmpath,
m.Exceptions,
strings.Join(exceptions, "\n"),
)
builder.WriteString(fmt.Sprintf(" Realm:\n %s\n", m.Realm.Path))
}

for _, ex := range m.Exceptions {
builder.WriteString(fmt.Sprintf(" %s\n", ex.Sprint(m)))
}

return builder.String()
}

//----------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion gnovm/pkg/gnolang/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -2284,7 +2284,7 @@ func (b *Block) StringIndented(indent string) string {
if len(source) > 32 {
source = source[:32] + "..."
}
lines := []string{}
lines := make([]string, 0, 3)
lines = append(lines,
fmt.Sprintf("Block(ID:%v,Addr:%p,Source:%s,Parent:%p)",
b.ObjectInfo.ID, b, source, b.Parent)) // XXX Parent may be RefValue{}.
Expand Down

0 comments on commit 0fc011a

Please sign in to comment.