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

cmd/cgo: unexpected allocations when passing void pointers #15048

Open
bmatsuo opened this Issue Mar 31, 2016 · 3 comments

Comments

Projects
None yet
2 participants
@bmatsuo

bmatsuo commented Mar 31, 2016

  1. What version of Go are you using (go version)? go1.6
  2. What operating system and processor architecture are you using (go env)? linux/amd64
  3. What did you do?

I was helping to debug several unaccounted for allocations and we came across what we thought was very peculiar behavior. Consider the following two implementations of the same package

http://play.golang.org/p/hC-b5VSG58
http://play.golang.org/p/soCMGF1WjG

And the following benchmark

http://play.golang.org/p/e37ObBxwJP

  1. What did you expect to see?

Neither implementation appears to allocate anything in the Foo function. So I expect go test -bench to report zero allocations for both implementations of the package.

  1. What did you see instead?

For the first example (hC-b5VSG58) the command go test -bench=. -benchmem reports two allocations (48 bytes total) when the C function is passed a non-nil argument.

$ go test -bench=. -benchmem
testing: warning: no tests to run
PASS
BenchmarkFoo-4           5000000               317 ns/op              48 B/op          2 allocs/op
BenchmarkFoo_nil-4      10000000               164 ns/op               0 B/op          0 allocs/op
ok      github.com/bmatsuo/lmdb-go/tmp/cgo-allocs       3.718s

The second example avoids any allocation.

$ go test -bench=. -benchmem
testing: warning: no tests to run
PASS
BenchmarkFoo-4          10000000               151 ns/op               0 B/op          0 allocs/op
BenchmarkFoo_nil-4      10000000               151 ns/op               0 B/op          0 allocs/op
ok      github.com/bmatsuo/lmdb-go/tmp/cgo-allocs       3.345s

The first example does see a performance impact, I assume because of the allocations.

@ianlancetaylor ianlancetaylor changed the title from cgo: unexpected allocations when passing void pointers to cmd/cgo: unexpected allocations when passing void pointers Mar 31, 2016

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Mar 31, 2016

The difference is that in the void* case, cgo doesn't figre out that the value does not point to a value that contains a Go pointer, and therefore inserts a check call. In the char* case, cgo can see that the type C.char can not contain a pointer, and therefore there is no need to use a check call. The check call causes the pointer to appear to escape, forcing buf to be allocated on the heap.

@ianlancetaylor ianlancetaylor added this to the Unplanned milestone Mar 31, 2016

@bmatsuo

This comment has been minimized.

bmatsuo commented Mar 31, 2016

That mostly makes sense. But you are saying it is allocating space on the heap for the 24-byte slice header (pointer + 2 ints)? It seems strange that a slice header can "escape".

@ianlancetaylor

This comment has been minimized.

Contributor

ianlancetaylor commented Mar 31, 2016

The slice header is being passed to a function via .... I think the compiler doesn't see that the ... slice does not escape.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment