Skip to content

Commit

Permalink
purego: decrease number of allocs in RegisterFunc (#128)
Browse files Browse the repository at this point in the history
This rewrites part of the RegisterFunc to have less code escape to the heap. Running the benchmark inside #121 shows a speed increase and alloc decrease.

goos: darwin
goarch: arm64
pkg: github.com/ebitengine/purego
BenchmarkCStringC
BenchmarkCStringC-10    	 2294838	       500.3 ns/op	     472 B/op	       8 allocs/op
PASS
  • Loading branch information
TotallyGamerJet authored Apr 10, 2023
1 parent c3a82d7 commit ec16513
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 4 deletions.
11 changes: 7 additions & 4 deletions func.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
}
}
var sysargs [maxArgs]uintptr
var stack = sysargs[numOfIntegerRegisters():]
stack := sysargs[numOfIntegerRegisters():]
var floats [numOfFloats]uintptr
var numInts int
var numFloats int
Expand All @@ -147,6 +147,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
var keepAlive []interface{}
defer func() {
runtime.KeepAlive(keepAlive)
runtime.KeepAlive(args)
}()
for _, v := range args {
switch v.Kind() {
Expand All @@ -159,7 +160,7 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
addInt(uintptr(v.Int()))
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
keepAlive = append(keepAlive, v.Pointer())
// There is no need to keepAlive this pointer separately because it is kept alive in the args variable
addInt(v.Pointer())
case reflect.Func:
addInt(NewCallback(v.Interface()))
Expand Down Expand Up @@ -192,7 +193,8 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
cfn,
sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5], sysargs[6], sysargs[7], sysargs[8],
floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7],
0, 0, 0}
0, 0, 0,
}
runtime_cgocall(syscall9XABI0, unsafe.Pointer(&syscall))
r1, r2 := syscall.r1, syscall.r2

Expand All @@ -212,7 +214,8 @@ func RegisterFunc(fptr interface{}, cfn uintptr) {
// We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer
v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&r1)))
case reflect.Ptr:
v = reflect.NewAt(outType, unsafe.Pointer(&r1)).Elem()
// It is safe to have the address of r1 not escape because it is immediately dereferenced with .Elem()
v = reflect.NewAt(outType, runtime_noescape(unsafe.Pointer(&r1))).Elem()
case reflect.Func:
// wrap this C function in a nicely typed Go function
v = reflect.New(outType)
Expand Down
4 changes: 4 additions & 0 deletions go_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ import (

//go:linkname runtime_cgocall runtime.cgocall
func runtime_cgocall(fn uintptr, arg unsafe.Pointer) int32 // from runtime/sys_libc.go

//go:linkname runtime_noescape runtime.noescape
//go:noescape
func runtime_noescape(p unsafe.Pointer) unsafe.Pointer // from runtime/stubs.go

0 comments on commit ec16513

Please sign in to comment.