-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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: add #cgo noescape/nocallback annotations #56378
Comments
Oh, sorry. I made a mistake here. The stack won't move while in syscall. Then, seems this can be easier, just need to skip |
Alternatively, we could have an annotation that says a C call does not call back into Go. That captures a user intent rather than a language implementation detail, and we can easily check that the call obeys this at run-time. |
|
This proposal has been added to the active column of the proposals project |
I don't think it's sufficient just to check that the same C thread doesn't call back into Go. What if the C thread passes the pointer to a different C thread, when then passes that pointer back to a Go function? |
That's an interesting point. That's probably something we'd want to account for in the documentation of any such directive (and maybe in its name), but I'm not sure that having a check that doesn't trip in a tiny fraction of cases is a deal-breaker. |
How much of a performance improvement might we expect from using this directive? And in what situations? I’m trying to understand where this proposal is coming from. |
Thanks all, happy to see this proposal is active.
yep, I think it's an unsafe annotation - for better performance, since we can not protect it at run-time. For its name, I think the |
Hi @hherman1 That depends, it could be very significant - when it's heavily using cgo and escaping Go objects frequency - with large GC overhead. We are implementing the Envoy Golang filter extension, https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/golang_filter |
Note that I believe that it is possible today for a goroutine that is suspended in a call to C to have its stack shrunk. That is OK because the current escaping implementation mean that no stack allocated address can be passed to C. If we implement this new pragma, then I think that in order to make it useful it will have to somehow disable stack shrinking for any call to a C function using this pragma. |
Upon further thought I don't fully understand where this pragma should be placed. Currently the cgo tool makes no attempt to parse any portion of the cgo comment. It just passes everything to the C compiler. So I don't see how the pragma can appear in the cgo comment. But I also don't understand where else it could appear. |
Thanks @ianlancetaylor
Oh, that was my idea, a line before the C function. So, that sounds like a big change. |
It would be valuable to quantify this more, if possible. This is tricky to do at scale, though I think not impossible. For your particular application, are you positive that cgo is the only reason these objects are escaping? We've always assumed that, in practice, objects being passed to cgo are typically going to be on the heap for other reasons anyway. Strong evidence that this isn't true, at least in some range of applications, would help motivate this. Note also that we might improve escape analysis in the future, so having more information about C calls may have more impact in the future.
I believe you're correct that this prevents us from shrinking stacks while in C. If it doesn't, that's certainly something we could do. |
The annotations in the C comment today begin with
for the annotation? The important part is that the arguments do not escape from the C function back into Go. Unfortunately to put something on the stack in the main Go toolchain we also need to know that there is no call back into Go, because that might grow the stack. So we probably also need
Implementations that use a segmented stack would be able to make the optimization of keeping values on the stack with only "noescape". The current toolchain would need both for the same optimization. So maybe we should have both, to allow a C implementation to declare what it does and a Go implementation to make use of what it needs. Thoughts? |
Yep, it's possible. Now, the total GC CPU overhead is about 10% of the total CPU usage, in our application(gateway).
Yes, I can confirm it, here is an example in the Envoy Golang extension:
Cool, sounds great. |
Great, I think it's very good.
Sorry, I'm confused about this, the current Go implement is not using a segmented stack, right?
Sorry, I think I haven't got the meaning to have them both. Thanks. |
Note that I don't think your example code is good programming style. You are passing a pointer to a Go string to C. The documented API permits C code to accept that string as It would be cleaner to stick to the documented and supported APIs by having the C code return a length and a C pointer, one way or another, and have the Go code call |
The current Go implementation does not use a segmented stack, but it's something we've used in the past and that we've considered using again in the future. Technically, a C function that is marked nocallback does not guarantee that any pointer passed to that function does not escape. For example, the C function could pass the pointer to a different thread that could call back into Go. That is why we need both nocallback and noescape. Of course, we could say that nocallback implies noescape, but that is a subtle point and it seems at least possible that we would regret that in the future. |
Okay, understand. Thanks for your clarification.
Yep, I knew it is an unsafe usage, using this way, just to make the code simpler. i.e. we preallocate memory Go side and pass pointer to C, then fill memory(write) on C side. |
Updated title. Sounds like #cgo noescape and #cgo nocallback are okay. Have all concerns about this proposal been addressed? |
Thanks, yep, it's good from my side. |
Also any rollback of my rollback for Go 1.23 should chase down why using #cgo noescape causes crashes, as in #63739. |
Change https://go.dev/cl/539235 mentions this issue: |
Go 1.21 and earlier do not understand this line, causing "go mod vendor" of //go:build go1.22-tagged code that uses this feature to fail. The solution is to include the go/build change to skip over the line in Go 1.22 (making "go mod vendor" from Go 1.22 onward work with this change) and then wait to deploy the cgo change until Go 1.23, at which point Go 1.21 and earlier will be unsupported. For #56378. Fixes #63293. Change-Id: Ifa08b134eac5a6aa15d67dad0851f00e15e1e58b Reviewed-on: https://go-review.googlesource.com/c/go/+/539235 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Bryan Mills <bcmills@google.com>
Change https://go.dev/cl/548876 mentions this issue: |
The only issue in this section, #56378, does not need a release note for Go 1.22 because its feature was disabled for this release. For #61422. Updates #56378. Change-Id: Ia4e090994cd9ac04e855f8b3a2c6ca0cde4485d2 Reviewed-on: https://go-review.googlesource.com/c/go/+/548876 Auto-Submit: Bryan Mills <bcmills@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
The only issue in this section, golang#56378, does not need a release note for Go 1.22 because its feature was disabled for this release. For golang#61422. Updates golang#56378. Change-Id: Ia4e090994cd9ac04e855f8b3a2c6ca0cde4485d2 Reviewed-on: https://go-review.googlesource.com/c/go/+/548876 Auto-Submit: Bryan Mills <bcmills@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Here is the proposal: golang/go#56378 They are documented here: https://pkg.go.dev/cmd/cgo@master#hdr-Optimizing_calls_of_C_code This would have been very useful to fix tinygo-org/bluetooth#176 in a nice way. That bug is now fixed in a different way using a wrapper function, but once this new noescape pragma gets included in TinyGo we could remove the workaround and use `#cgo noescape` instead.
This reverts commit 607e020. Reason for revert: Go1.22 is released. It's aggressive to introdcue #cgo noescape/nocallback in Go1.22, as in golang#63739 And it won't be a problem again while upgrading from Go1.22 to Go1.23. fix golang#56378 Signed-off-by: doujiang24 <doujiang24@gmail.com>
Change https://go.dev/cl/579955 mentions this issue: |
Here is the proposal: golang/go#56378 They are documented here: https://pkg.go.dev/cmd/cgo@master#hdr-Optimizing_calls_of_C_code This would have been very useful to fix tinygo-org/bluetooth#176 in a nice way. That bug is now fixed in a different way using a wrapper function, but once this new noescape pragma gets included in TinyGo we could remove the workaround and use `#cgo noescape` instead.
Would For example, the WebGPU C API has functions like these:
where
In C, all of the pointers would either be on the stack, or read-only globals. |
I think that we could change the rules to permit that if we wanted to. However, it gets really subtle: C code would not be permitted to change the values of any Go pointers stored in Go memory. And of course C code could not retain Go pointers to unpinned Go memory. |
This reverts commit 607e020. Reason for revert: Go1.22 is released. It's aggressive to introdcue #cgo noescape/nocallback in Go1.22, as in golang#63739 And it won't be a problem again while upgrading from Go1.22 to Go1.23. fix golang#56378 Signed-off-by: doujiang24 <doujiang24@gmail.com>
Here is the proposal: golang/go#56378 They are documented here: https://pkg.go.dev/cmd/cgo@master#hdr-Optimizing_calls_of_C_code This would have been very useful to fix tinygo-org/bluetooth#176 in a nice way. That bug is now fixed in a different way using a wrapper function, but once this new noescape pragma gets included in TinyGo we could remove the workaround and use `#cgo noescape` instead.
Here is the proposal: golang/go#56378 They are documented here: https://pkg.go.dev/cmd/cgo@master#hdr-Optimizing_calls_of_C_code This would have been very useful to fix tinygo-org/bluetooth#176 in a nice way. That bug is now fixed in a different way using a wrapper function, but once this new noescape pragma gets included in TinyGo we could remove the workaround and use `#cgo noescape` instead.
This is for better performance, which will avoid escaping.
example:
Now, the
str
pointer must be allocated on the heap, for safety.AFAIK, the unsafe cases:
str
pointer might move. from: cmd/cgo: replace _Cgo_use with runtime.KeepAlive? #20281 (comment)shrinkstack
, thestr
pointer might move, before C returns.So, when people use
//go:cgo_unsafe_stack_pointer
,they must make sure the C code will not call back to go, it's an unsafe usage.
And, the go compiler needs these changes:
_Cgo_use
forstr
pointer.shrinkstack
when the goroutine is invoking such an unsafe C function (we could set a flag underg
, before invoking the C function).The text was updated successfully, but these errors were encountered: