Improves instantiation performance by removing ProxyFunc (#942)

This removes ProxyFunc, which was an internal experiment to make
functions that use memory to store parameters or results easier to see.
The main issue with the approach was instantiation performance, as it
needs to dynamically generate functions. Another approach to visibility
can happen later, for example via internal logging hooks.

Notably, this fixed the performance regression after switching WASI to ProxyFunc:
```
name                       old time/op    new time/op    delta
Allocation/Compile-16        39.8ms ± 0%    39.5ms ± 0%   -0.62%  (p=0.016 n=4+5)
Allocation/Instantiate-16    1.74ms ± 4%    0.86ms ± 1%  -50.19%  (p=0.008 n=5+5)
Allocation/Call-16           4.25µs ± 1%    4.21µs ± 2%     ~     (p=0.151 n=5+5)

name                       old alloc/op   new alloc/op   delta
Allocation/Compile-16        20.3MB ± 0%    20.3MB ± 0%     ~     (p=0.841 n=5+5)
Allocation/Instantiate-16    1.04MB ± 0%    0.56MB ± 0%  -45.88%  (p=0.008 n=5+5)
Allocation/Call-16            48.0B ± 0%     48.0B ± 0%     ~     (all equal)

name                       old allocs/op  new allocs/op  delta
Allocation/Compile-16          432k ± 0%      432k ± 0%     ~     (p=0.833 n=5+5)
Allocation/Instantiate-16     16.6k ± 0%      6.7k ± 0%  -59.34%  (p=0.008 n=5+5)
Allocation/Call-16             5.00 ± 0%      5.00 ± 0%     ~     (all equal)
```

Since we also removed it from `GOARCH=wasm GOOS=js`, we experienced a performance
benefit there as well:
```
name               old time/op    new time/op    delta
_main/gojs.Run-16    13.7ms ± 1%    12.2ms ± 3%  -10.76%  (p=0.008 n=5+5)

name               old alloc/op   new alloc/op   delta
_main/gojs.Run-16    25.4MB ± 0%    25.0MB ± 0%   -1.66%  (p=0.008 n=5+5)

name               old allocs/op  new allocs/op  delta
_main/gojs.Run-16      166k ± 0%      158k ± 0%   -4.79%  (p=0.016 n=4+5)

```

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Crypt Keeper
2022-12-19 18:18:19 +09:00
committed by GitHub
parent b90e9f394c
commit b1cb9140dd
25 changed files with 620 additions and 1763 deletions

View File

@@ -5,13 +5,10 @@ import (
"fmt"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/gojs/spfunc"
"github.com/tetratelabs/wazero/internal/wasm"
)
const (
i32, i64 = api.ValueTypeI32, api.ValueTypeI64
wasmExitName = "runtime.wasmExit"
wasmWriteName = "runtime.wasmWrite"
resetMemoryDataViewName = "runtime.resetMemoryDataView"
@@ -25,19 +22,13 @@ const (
// WasmExit implements runtime.wasmExit which supports runtime.exit.
//
// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.go#L28
var WasmExit = spfunc.MustCallFromSP(false, &wasm.HostFunc{
ExportNames: []string{wasmExitName},
Name: wasmExitName,
ParamTypes: []api.ValueType{i32},
ParamNames: []string{"code"},
Code: &wasm.Code{
IsHostFunction: true,
GoFunc: api.GoModuleFunc(wasmExit),
},
})
var WasmExit = newSPFunc(wasmExitName, wasmExit)
func wasmExit(ctx context.Context, mod api.Module, stack []uint64) {
code := uint32(stack[0])
func wasmExit(ctx context.Context, mod api.Module, sp []uint64) {
mem := mod.Memory()
// Read the code from offset SP+8
code := mustReadUint32Le(mem, "code", uint32(sp[0]+8))
getState(ctx).clear()
_ = mod.CloseWithExitCode(ctx, code) // TODO: should ours be signed bit (like -1 == 255)?
@@ -47,21 +38,18 @@ func wasmExit(ctx context.Context, mod api.Module, stack []uint64) {
// runtime.writeErr. This implements `println`.
//
// See https://github.com/golang/go/blob/go1.19/src/runtime/os_js.go#L29
var WasmWrite = spfunc.MustCallFromSP(false, &wasm.HostFunc{
ExportNames: []string{wasmWriteName},
Name: wasmWriteName,
ParamTypes: []api.ValueType{i32, i32, i32},
ParamNames: []string{"fd", "p", "n"},
Code: &wasm.Code{
IsHostFunction: true,
GoFunc: api.GoModuleFunc(wasmWrite),
},
})
var WasmWrite = newSPFunc(wasmWriteName, wasmWrite)
func wasmWrite(_ context.Context, mod api.Module, stack []uint64) {
func wasmWrite(_ context.Context, mod api.Module, sp []uint64) {
fsc := mod.(*wasm.CallContext).Sys.FS()
mem := mod.Memory()
fd, p, n := uint32(stack[0]), uint32(stack[1]), uint32(stack[2])
// Read (param + result count) * 8 memory starting at SP+8
stack := mustRead(mem, "sp", uint32(sp[0]+8), 24)
fd := le.Uint32(stack)
p := le.Uint32(stack[8:])
n := le.Uint32(stack[16:])
writer := fsc.FdWriter(fd)
if writer == nil {
@@ -89,39 +77,30 @@ var ResetMemoryDataView = &wasm.HostFunc{
// Nanotime1 implements runtime.nanotime which supports time.Since.
//
// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L184
var Nanotime1 = spfunc.MustCallFromSP(false, &wasm.HostFunc{
ExportNames: []string{nanotime1Name},
Name: nanotime1Name,
ResultTypes: []api.ValueType{i64},
Code: &wasm.Code{
IsHostFunction: true,
GoFunc: api.GoModuleFunc(nanotime1),
},
})
var Nanotime1 = newSPFunc(nanotime1Name, nanotime1)
func nanotime1(_ context.Context, mod api.Module, stack []uint64) {
func nanotime1(_ context.Context, mod api.Module, sp []uint64) {
time := mod.(*wasm.CallContext).Sys.Nanotime()
stack[0] = api.EncodeI64(time)
mem := mod.Memory()
// Write the result to offset SP+8
mustWriteUint64Le(mem, "time", uint32(sp[0]+8), api.EncodeI64(time))
}
// Walltime implements runtime.walltime which supports time.Now.
//
// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L188
var Walltime = spfunc.MustCallFromSP(false, &wasm.HostFunc{
ExportNames: []string{walltimeName},
Name: walltimeName,
ResultTypes: []api.ValueType{i64, i32},
ResultNames: []string{"sec", "nsec"},
Code: &wasm.Code{
IsHostFunction: true,
GoFunc: api.GoModuleFunc(walltime),
},
})
var Walltime = newSPFunc(walltimeName, walltime)
func walltime(_ context.Context, mod api.Module, stack []uint64) {
func walltime(_ context.Context, mod api.Module, sp []uint64) {
sec, nsec := mod.(*wasm.CallContext).Sys.Walltime()
stack[0] = api.EncodeI64(sec)
stack[1] = api.EncodeI32(nsec)
mem := mod.Memory()
// Write results starting at SP+8
results := mustRead(mem, "sp", uint32(sp[0]+8), 16)
le.PutUint64(results, uint64(sec))
le.PutUint32(results[8:], uint32(nsec))
}
// ScheduleTimeoutEvent implements runtime.scheduleTimeoutEvent which supports
@@ -147,20 +126,17 @@ var ClearTimeoutEvent = stubFunction(clearTimeoutEventName)
// for runtime.fastrand.
//
// See https://github.com/golang/go/blob/go1.19/src/runtime/sys_wasm.s#L200
var GetRandomData = spfunc.MustCallFromSP(false, &wasm.HostFunc{
ExportNames: []string{getRandomDataName},
Name: getRandomDataName,
ParamTypes: []api.ValueType{i32, i32},
ParamNames: []string{"buf", "bufLen"},
Code: &wasm.Code{
IsHostFunction: true,
GoFunc: api.GoModuleFunc(getRandomData),
},
})
var GetRandomData = newSPFunc(getRandomDataName, getRandomData)
func getRandomData(_ context.Context, mod api.Module, stack []uint64) {
func getRandomData(_ context.Context, mod api.Module, sp []uint64) {
randSource := mod.(*wasm.CallContext).Sys.RandSource()
buf, bufLen := uint32(stack[0]), uint32(stack[1])
mem := mod.Memory()
// Read (param + result count) * 8 memory starting at SP+8
stack := mustRead(mem, "sp", uint32(sp[0]+8), 16)
buf := le.Uint32(stack)
bufLen := le.Uint32(stack[8:])
r := mustRead(mod.Memory(), "r", buf, bufLen)