Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opt alloc #115

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
15 changes: 15 additions & 0 deletions bench/calls/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/
40 changes: 40 additions & 0 deletions bench/calls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Benchmarking calls to/from VM

# Run bencmarks

- go test -run none -benchmem -bench Benchmark_callSumAndAdd1

# Profile memory

- go test -run none -benchmem -memprofile=mem.out -bench Benchmark_callSumAndAdd1
- go tool pprof -http=:8088 mem.out

# Profile CPU

- go test -run none -benchmem -cpuprofile=cpu.out -bench Benchmark_callSumAndAdd1
- go tool pprof -http=:8088 cpu.out

# Links

- https://github.com/perlin-network/life/blob/69f41b0484c346e56a57921aef51f7aa4947f5b2/exec/vm_codegen.go
- GAS https://github.com/perlin-network/life/issues/86

# Misc

goos: linux
goarch: amd64
pkg: github.com/perlin-network/life/bench/calls
Benchmark_callSumAndAdd1_0_NoAOT-2 2394116 504 ns/op 0 B/op 0 allocs/op
Benchmark_callSumAndAdd1_1_NoAOT-2 974137 1245 ns/op 0 B/op 0 allocs/op
Benchmark_callSumAndAdd1_10_NoAOT-2 142293 8406 ns/op 0 B/op 0 allocs/op
Benchmark_callSumAndAdd1_0_AOT-2 1000000 1192 ns/op 24 B/op 2 allocs/op
Benchmark_callSumAndAdd1_1_AOT-2 606807 2096 ns/op 24 B/op 2 allocs/op
Benchmark_callSumAndAdd1_10_AOT-2 129069 9286 ns/op 24 B/op 2 allocs/op

Benchmark_life_callSumAndAdd1_0_NoAOT-2 1726413 696 ns/op 56 B/op 2 allocs/op
Benchmark_life_callSumAndAdd1_1_NoAOT-2 669055 1834 ns/op 144 B/op 5 allocs/op
Benchmark_life_callSumAndAdd1_10_NoAOT-2 93620 12866 ns/op 936 B/op 32 allocs/op
Benchmark_life_callSumAndAdd1_0_AOT-2 854827 1390 ns/op 80 B/op 4 allocs/op
Benchmark_life_callSumAndAdd1_1_AOT-2 463479 2645 ns/op 168 B/op 7 allocs/op
Benchmark_life_callSumAndAdd1_10_AOT-2 87625 13668 ns/op 960 B/op 34 allocs/op
Benchmark_life_callSumAndAdd1_100_AOT-2 9847 141453 ns/op 8880 B/op 304 allocs/op
95 changes: 95 additions & 0 deletions bench/calls/callSumAndAdd1_0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
https://wasdk.github.io/wasmcodeexplorer/

case opcodes.GetLocal:
//1, 149
id := int(LE.Uint32(frame.Code[frame.IP : frame.IP+4])) //2
val := frame.Locals[id]
frame.IP += 4
frame.Regs[valueID] = val

case opcodes.I32Const:
// 2, 3
val := LE.Uint32(frame.Code[frame.IP : frame.IP+4]) //1
frame.IP += 4
frame.Regs[valueID] = int64(val)

case opcodes.I32GeS:
// 1, 31
a := int32(frame.Regs[int(LE.Uint32(frame.Code[frame.IP:frame.IP+4]))])
b := int32(frame.Regs[int(LE.Uint32(frame.Code[frame.IP+4:frame.IP+8]))])
frame.IP += 8
if a >= b {
frame.Regs[valueID] = 1
} else {
frame.Regs[valueID] = 0
}

case opcodes.JmpIf:
target := int(LE.Uint32(frame.Code[frame.IP : frame.IP+4]))
cond := int(LE.Uint32(frame.Code[frame.IP+4 : frame.IP+8]))
yieldedReg := int(LE.Uint32(frame.Code[frame.IP+8 : frame.IP+12]))
frame.IP += 12
if frame.Regs[cond] != 0 {
vm.Yielded = frame.Regs[yieldedReg]
frame.IP = target
}

case opcodes.Jmp:
target := int(LE.Uint32(frame.Code[frame.IP : frame.IP+4]))
vm.Yielded = frame.Regs[int(LE.Uint32(frame.Code[frame.IP+4:frame.IP+8]))]
frame.IP = target

case opcodes.GetLocal:
id := int(LE.Uint32(frame.Code[frame.IP : frame.IP+4]))
val := frame.Locals[id]
frame.IP += 4
frame.Regs[valueID] = val

case opcodes.ReturnValue:
val := frame.Regs[int(LE.Uint32(frame.Code[frame.IP:frame.IP+4]))]
frame.Destroy(vm)
vm.CurrentFrame--
if vm.CurrentFrame == -1 {
vm.Exited = true
vm.ReturnValue = val
return
}

frame = vm.GetCurrentFrame()
frame.Regs[frame.ReturnReg] = val



1.0:{valueID: 1, opcode: opcodes.GetLocal, v1: 2, v2: 2}
1.9:{valueID: 2, opcode: opcodes.I32Const, v1: 1, v2: 1}
1.18:{valueID: 1, opcode: opcodes.I32GeS, v1: 1, v2: 2}
1.31:{valueID: 0, opcode: opcodes.JmpIf, v1: 61, v2: 1}
1.61:{valueID: 1, opcode: opcodes.GetLocal, v1: 0, v2: 2}
1.70:{valueID: 2, opcode: opcodes.GetLocal, v1: 1, v2: 1}
1.79:{valueID: 1, opcode: opcodes.Call, v1: 0, v2: 2}
0.0:{valueID: 1, opcode: opcodes.InvokeImport, v1: 0, v2: 0}
2020/08/04 15:23:01 Resolver called
0.9:{valueID: 0, opcode: opcodes.ReturnValue, v1: 1, v2: 0}
1.100:{valueID: 2, opcode: opcodes.I32Const, v1: 1, v2: 1}
1.109:{valueID: 1, opcode: opcodes.I32Add, v1: 1, v2: 2}
1.122:{valueID: 0, opcode: opcodes.SetLocal, v1: 0, v2: 1}
1.135:{valueID: 1, opcode: opcodes.GetLocal, v1: 2, v2: 2}
1.144:{valueID: 2, opcode: opcodes.I32Const, v1: 4294967295, v2: 1}
1.153:{valueID: 1, opcode: opcodes.I32Add, v1: 1, v2: 2}
1.166:{valueID: 0, opcode: opcodes.SetLocal, v1: 2, v2: 1}
1.179:{valueID: 0, opcode: opcodes.JmpIf, v1: 61, v2: 1}
1.61:{valueID: 1, opcode: opcodes.GetLocal, v1: 0, v2: 2}
1.70:{valueID: 2, opcode: opcodes.GetLocal, v1: 1, v2: 1}
1.79:{valueID: 1, opcode: opcodes.Call, v1: 0, v2: 2}
0.0:{valueID: 1, opcode: opcodes.InvokeImport, v1: 0, v2: 0}
0.9:{valueID: 0, opcode: opcodes.ReturnValue, v1: 1, v2: 0}
1.100:{valueID: 2, opcode: opcodes.I32Const, v1: 1, v2: 1}
1.109:{valueID: 1, opcode: opcodes.I32Add, v1: 1, v2: 2}
1.122:{valueID: 0, opcode: opcodes.SetLocal, v1: 0, v2: 1}
1.135:{valueID: 1, opcode: opcodes.GetLocal, v1: 2, v2: 2}
1.144:{valueID: 2, opcode: opcodes.I32Const, v1: 4294967295, v2: 1}
1.153:{valueID: 1, opcode: opcodes.I32Add, v1: 1, v2: 2}
1.166:{valueID: 0, opcode: opcodes.SetLocal, v1: 2, v2: 1}
1.179:{valueID: 0, opcode: opcodes.JmpIf, v1: 61, v2: 1}
1.196:{valueID: 1, opcode: opcodes.GetLocal, v1: 0, v2: 0}
1.205:{valueID: 0, opcode: opcodes.ReturnValue, v1: 1, v2: 0}
147 changes: 147 additions & 0 deletions bench/calls/calls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package calls

import (
"fmt"
"io/ioutil"
"log"
"testing"

"github.com/perlin-network/life/exec"
"github.com/stretchr/testify/require"
)

func Test_callSumAndAdd1(t *testing.T) {

input, err := ioutil.ReadFile("sum-add.wasm")
require.Nil(t, err)

vm := newVM(t, input, &lifeResolver{}, false)
require.Nil(t, err)

entryID, ok := vm.GetFunctionExport("callSumAndAdd1")
require.True(t, ok)

ret, err := vm.Run(entryID, 3, 4, 0)
require.Equal(t, int64(3), ret)

ret, err = vm.Run(entryID, 3, 4, 1)
require.Nil(t, err)
require.Equal(t, int64(8), ret)

ret, err = vm.Run(entryID, 3, 4, 10)
require.Nil(t, err)
require.Equal(t, int64(53), ret)
}

func Test_callSumAndAdd1_0(t *testing.T) {
input, err := ioutil.ReadFile("sum-add.wasm")
require.Nil(t, err)

vm := newVM(t, input, &lifeResolver{}, false)
require.Nil(t, err)

entryID, ok := vm.GetFunctionExport("callSumAndAdd1")
require.True(t, ok)

ret, err := vm.Run(entryID, 3, 4, 0)
require.Nil(t, err)
require.Equal(t, int64(8), ret)
}

func Test_callSumAndAdd1_2(t *testing.T) {
input, err := ioutil.ReadFile("sum-add.wasm")
require.Nil(t, err)

vm := newVM(t, input, &lifeResolver{}, false)
require.Nil(t, err)

entryID, ok := vm.GetFunctionExport("callSumAndAdd1")
require.True(t, ok)

ret, err := vm.Run(entryID, 3, 4, 2)
require.Nil(t, err)
require.Equal(t, int64(8), ret)
}

func Benchmark_Ignite(t *testing.B) {

input, err := ioutil.ReadFile("sum-add.wasm")
require.Nil(t, err)

vm := newVM(t, input, &lifeResolver{}, false)
require.Nil(t, err)

entryID, ok := vm.GetFunctionExport("callSumAndAdd1")
require.True(t, ok)

t.ResetTimer()
for i := 0; i < t.N; i++ {
vm.Ignite(entryID, 3, 4, 10)
vm.CurrentFrame--
vm.Exited = true
}
}

func Benchmark_callSumAndAdd1_0_NoAOT(b *testing.B) {
callSumAndAdd1(b, 0, false)
}

func Benchmark_callSumAndAdd1_1_NoAOT(b *testing.B) {
callSumAndAdd1(b, 1, false)
}
func Benchmark_callSumAndAdd1_10_NoAOT(b *testing.B) {
callSumAndAdd1(b, 10, false)
}

func Benchmark_callSumAndAdd1_0_AOT(b *testing.B) {
callSumAndAdd1(b, 0, true)
}
func Benchmark_callSumAndAdd1_1_AOT(b *testing.B) {
callSumAndAdd1(b, 1, true)
}
func Benchmark_callSumAndAdd1_10_AOT(b *testing.B) {
callSumAndAdd1(b, 10, true)
}

func callSumAndAdd1(t *testing.B, cnt int, aot bool) {
input, err := ioutil.ReadFile("sum-add.wasm")
require.Nil(t, err)

vm := newVM(t, input, &lifeResolver{}, aot)

entryID, ok := vm.GetFunctionExport("callSumAndAdd1")
require.True(t, ok)

t.ResetTimer()
for i := 0; i < t.N; i++ {
_, err := vm.Run(entryID, 3, 4, int64(cnt))
if nil != err {
panic(err)
}
}
}

type lifeResolver struct{}

func (r *lifeResolver) ResolveFunc(module, field string) exec.FunctionImport {
switch module {
case "env":
switch field {
case "sum":
log.Println("Resolver called")
return func(vm *exec.VirtualMachine) int64 {
v1 := int32(vm.GetCurrentFrame().Locals[0])
v2 := int32(vm.GetCurrentFrame().Locals[1])
return int64(v1 + v2)
}
default:
panic(fmt.Errorf("unknown import resolved: %s", field))
}
default:
panic(fmt.Errorf("unknown module: %s", module))
}
}

func (r *lifeResolver) ResolveGlobal(module, field string) int64 {
panic("we're not resolving global variables for now")
}
58 changes: 58 additions & 0 deletions bench/calls/fast_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package calls

import (
"testing"

"github.com/perlin-network/life/compiler/opcodes"
"github.com/stretchr/testify/require"
)

func Test_fast_callSumAndAdd1(t *testing.T) {
vm := newFastVM()
fn := newCallSumAndAdd1_0()
res, _ := vm.exec(fn, 3, 4, 0)
require.Equal(t, int64(3), res)

}

func Benchmark_fast_callSumAndAdd1(b *testing.B) {
vm := newFastVM()
fn := newCallSumAndAdd1_0()
for i := 0; i < b.N; i++ {
vm.exec(fn, 3, 4, 0)
}
}

func Benchmark_fast_justCall(b *testing.B) {
vm := newFastVM()
fn := newJustCall()
for i := 0; i < b.N; i++ {
vm.exec(fn)
}
}

func newCallSumAndAdd1_0() (res *function) {
fn := function{}
fn.NumParams = 3
fn.NumRegs = 3
fn.NumLocals = 0

fn.inss = append(fn.inss, ins{valueID: 1, opcode: opcodes.GetLocal, v1: 2, v2: 2})
fn.inss = append(fn.inss, ins{valueID: 2, opcode: opcodes.I32Const, v1: 1, v2: 1})
fn.inss = append(fn.inss, ins{valueID: 1, opcode: opcodes.I32GeS, v1: 1, v2: 2})
fn.inss = append(fn.inss, ins{valueID: 0, opcode: opcodes.JmpIf, v1: 61, v2: 1})
fn.inss = append(fn.inss, ins{valueID: 0, opcode: opcodes.Jmp, v1: 5, v2: 0})
fn.inss = append(fn.inss, ins{valueID: 1, opcode: opcodes.GetLocal, v1: 0, v2: 0})
fn.inss = append(fn.inss, ins{valueID: 0, opcode: opcodes.ReturnValue, v1: 1, v2: 0})
return &fn
}

func newJustCall() (res *function) {
fn := function{}
fn.NumParams = 0
fn.NumRegs = 1
fn.NumLocals = 0

fn.inss = append(fn.inss, ins{valueID: 0, opcode: opcodes.ReturnValue, v1: 0, v2: 0})
return &fn
}
Loading