diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 2b5bb2e..7d9d1c9 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -25,10 +25,6 @@ jobs: run: | # Update actively-maintained direct dependencies individually so a # failure on any one package is logged but does not block the rest. - # pkujhd/goloader is excluded: it references cmd/objfile/* internal - # packages that cannot be resolved outside the Docker build environment, - # so `go get -u` would fail. The Dockerfile's `cp -r cmd/internal …` - # workaround handles it at build time instead. PKGS=( github.com/GoCodeAlone/go-plugin github.com/GoCodeAlone/yaegi diff --git a/Dockerfile b/Dockerfile index bf5a40b..d6aa2e2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ WORKDIR /app ENV GO111MODULE=on ENV GOARCH=amd64 +ENV GOLANG_PROTOBUF_REGISTRATION_CONFLICT=warn RUN apt-get update RUN apt-get install -y gcc @@ -26,14 +27,11 @@ COPY . ./ COPY --from=tinygo-builder /app/wazero.wasm ./ RUN go get -RUN cp -r /usr/local/go/src/cmd/internal /usr/local/go/src/cmd/objfile RUN go build -buildmode=plugin -o plugin.so golangplugin/main.go RUN go build -o ./hashicorpgoplugin ./hashicorp-go-plugin/main.go RUN go build -o ./pieplugin ./pie/main.go RUN go build -o ./pingoplugin ./pingo/main.go RUN go build -o ./plugplugin ./plug/plugin/main.go RUN go build -o ./gocodalonegoplugin ./gocodalone-go-plugin/main.go -RUN go list -export -f '{{if .Export}}packagefile {{.ImportPath}}={{.Export}}{{end}}' std `go list -f {{.Imports}} ./goloader/main.go | awk '{sub(/^\[/, ""); print }' | awk '{sub(/\]$/, ""); print }'` > importcfg -RUN CGO_ENABLED=0 go tool compile -importcfg importcfg -o ./goloader.o ./goloader/main.go CMD ["go", "test", "-bench=."] diff --git a/README.md b/README.md index e43076b..5434ef0 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,17 @@ A comparison of the [go plugin package](https://golang.org/pkg/plugin/) and othe | Name | Operations (higher is better) | ns/op (lower is better) | type | |-----------------------------------------------------------------------------------|:-----------------------------:|------------------------:|:-----------:| -| [go plugin package](https://golang.org/pkg/plugin/) | 44219324 | 30.35 ns/op | native | -| [hashicorp/go-plugin](https://github.com/hashicorp/go-plugin) | 3682 | 413257 ns/op | rpc | -| [GoCodeAlone/go-plugin](https://github.com/GoCodeAlone/go-plugin) | TBD | TBD | grpc | -| [natefinch/pie](https://github.com/natefinch/pie) | 3933 | 328025 ns/op | rpc | -| [dullgiulio/pingo](https://github.com/dullgiulio/pingo) | 4197 | 329354 ns/op | tcp | -| [dullgiulio/pingo](https://github.com/dullgiulio/pingo) | 3110 | 465628 ns/op | unix | -| [elliotmr/plug](https://github.com/elliotmr/plug) | 7998 | 162677 ns/op | ipc | -| [traefik/yaegi](https://github.com/traefik/yaegi) | 1000000 | 1184 ns/op | interpreter | -| [GoCodeAlone/yaegi](https://github.com/GoCodeAlone/yaegi) | TBD | TBD | interpreter | -| [pkujhd/goloader](https://github.com/pkujhd/goloader) | 68201743 | 19.11 ns/op | native | -| [tetratelabs/wazero](https://github.com/tetratelabs/wazero) | 11401358 | 105.0 ns/op | native | +| [go plugin package](https://golang.org/pkg/plugin/) | 466388373 | 12.90 ns/op | native | +| [hashicorp/go-plugin](https://github.com/hashicorp/go-plugin) | 42607 | 137687 ns/op | rpc | +| [GoCodeAlone/go-plugin](https://github.com/GoCodeAlone/go-plugin) | 36901 | 162668 ns/op | grpc | +| [natefinch/pie](https://github.com/natefinch/pie) | 66715 | 89390 ns/op | rpc | +| [dullgiulio/pingo](https://github.com/dullgiulio/pingo) | 51844 | 114880 ns/op | tcp | +| [dullgiulio/pingo](https://github.com/dullgiulio/pingo) | 55180 | 108367 ns/op | unix | +| [elliotmr/plug](https://github.com/elliotmr/plug) | 180102 | 32750 ns/op | ipc | +| [traefik/yaegi](https://github.com/traefik/yaegi) | 6725710 | 895.6 ns/op | interpreter | +| [GoCodeAlone/yaegi](https://github.com/GoCodeAlone/yaegi) | 6666477 | 901.1 ns/op | interpreter | +| [pkujhd/goloader](https://github.com/pkujhd/goloader) [¹](#goloader-note) | N/A | N/A | native | +| [tetratelabs/wazero](https://github.com/tetratelabs/wazero) | 32659566 | 184.4 ns/op | native | Several of the other packages use RPC or similar methods instead of the go plugin package which gets around issues such as, but not limited to, [not being compatible with Windows](https://github.com/golang/go/issues/19282) and [package paths and GOPATH needing to be the same between apps and plugins](https://github.com/golang/go/issues/20481). @@ -46,7 +46,7 @@ docker run go-plugin-benchmark:local Most plugins tested are using RPC which adds about 30 - 50 microseconds to plugin calls (or 0.03 - 0.05 milliseconds) over the golang plugin package. -The [goloader](https://github.com/pkujhd/goloader) package is interesting and may provide a good alternative to the go plugin package. One drawback is that it uses internal packages which requires renaming the internal folder locally and I have not tested compatibility to see if it solves the problems with the go plugin package. +¹ The [goloader](https://github.com/pkujhd/goloader) package is interesting and may provide a good alternative to the go plugin package. One drawback is that it uses internal packages which requires renaming the internal folder locally and I have not tested compatibility to see if it solves the problems with the go plugin package. **Note: goloader is currently excluded from benchmarks because it relies on `//go:linkname` access to unexported `runtime` symbols that are no longer accessible in Go 1.26.1.** ## Contributing diff --git a/go.mod b/go.mod index 1cfa2e6..8c35638 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-plugin v1.7.0 github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007 - github.com/pkujhd/goloader v0.0.24-0.20260211091157-860e2b19f73f github.com/tetratelabs/wazero v1.11.0 github.com/traefik/yaegi v0.16.1 google.golang.org/grpc v1.79.1 diff --git a/go.sum b/go.sum index 0bdfcfd..f2a2b1d 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,6 @@ github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007 h1:Ohgj9L0EYOgXxkDp+ github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007/go.mod h1:wKCOWMb6iNlvKiOToY2cNuaovSXvIiv1zDi9QDR7aGQ= github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E= github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= -github.com/pkujhd/goloader v0.0.24-0.20260211091157-860e2b19f73f h1:sNbr3bp/1tWirr4DBzWhtDsdSqtEpKbeytPrD6aWs7Y= -github.com/pkujhd/goloader v0.0.24-0.20260211091157-860e2b19f73f/go.mod h1:NBZlcY477N1nyopY6p3YcoiL5dtXHzj/F12F8b3ui/o= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= diff --git a/plugin_test.go b/plugin_test.go index 0d76e9a..b964a8a 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -9,7 +9,6 @@ import ( "plugin" "runtime" "testing" - "unsafe" gocplugin "github.com/GoCodeAlone/go-plugin" gcyaegiinterp "github.com/GoCodeAlone/yaegi/interp" @@ -18,7 +17,6 @@ import ( "github.com/hashicorp/go-hclog" hashicorpplugin "github.com/hashicorp/go-plugin" "github.com/natefinch/pie" - "github.com/pkujhd/goloader" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1" "github.com/traefik/yaegi/interp" @@ -182,44 +180,6 @@ func RandInt(i int) int { return rand.Int() }` }) } -func BenchmarkGoloaderRandInt(b *testing.B) { - linker, err := goloader.ReadObjs([]string{"goloader.o"}, []string{"main"}) - if err != nil { - b.Error(err) - return - } - - run := "main.RandInt" - - symPtr := make(map[string]uintptr) - err = goloader.RegSymbol(symPtr) - if err != nil { - b.Error(err) - return - } - - b.Run("goloader", func(b *testing.B) { - codeModule, err := goloader.Load(linker, symPtr) - if err != nil { - fmt.Println("Load error:", err) - return - } - runFuncPtr, ok := codeModule.Syms[run] - if !ok || runFuncPtr == 0 { - fmt.Println("Load error! not find function:", run) - return - } - funcPtrContainer := (uintptr)(unsafe.Pointer(&runFuncPtr)) - runFunc := *(*func() int)(unsafe.Pointer(&funcPtrContainer)) - for i := 0; i < b.N; i++ { - _ = runFunc() - } - - os.Stdout.Sync() - codeModule.Unload() - }) -} - func BenchmarkWazeroRandInt(b *testing.B) { wasmFile, err := os.ReadFile("wazero.wasm") if err != nil { @@ -233,11 +193,23 @@ func BenchmarkWazeroRandInt(b *testing.B) { wasi_snapshot_preview1.MustInstantiate(ctx, runtime) - module, err := runtime.Instantiate(ctx, wasmFile) + compiledModule, err := runtime.CompileModule(ctx, wasmFile) + if err != nil { + fmt.Println("failed to compile module:", err) + return + } + + // WithStartFunctions() with no arguments skips the auto-start (_start) + // entry point so the WASI module stays alive for repeated RandInt calls. + // Without this, TinyGo's _start calls main() and then proc_exit(0), + // closing the module before any benchmark iterations can complete. + cfg := wazero.NewModuleConfig().WithStartFunctions() + module, err := runtime.InstantiateModule(ctx, compiledModule, cfg) if err != nil { fmt.Println("failed to instantiate module:", err) return } + defer module.Close(ctx) b.Run("wazero", func(b *testing.B) { randIntFunction := module.ExportedFunction("RandInt") diff --git a/scripts/update_readme.py b/scripts/update_readme.py index a08607b..a34940f 100644 --- a/scripts/update_readme.py +++ b/scripts/update_readme.py @@ -17,7 +17,6 @@ "plug": r"github\.com/elliotmr/plug", "yaegi": r"github\.com/traefik/yaegi", "gocodalone-yaegi": r"github\.com/GoCodeAlone/yaegi", - "goloader": r"github\.com/pkujhd/goloader", "wazero": r"github\.com/tetratelabs/wazero", }