-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
util/must: consider performance implications #108169
Comments
cc @cockroachdb/test-eng |
Clarification–above concerns the case of not compiling away |
I'll point out that we only incur the boxing allocation for objects or large values, so this is still fine as long as we don't pass those. But it's easy to get wrong. We could consider adding a linter or runtime check, and a wrapper function to override the check. But it's not exactly ergonomic. We could consider a secondary API to decorate the error, but this won't work with fatal assertions since they'll fail before: if err := must.Equal(ctx, a, b, "format"); err != nil {
return must.Decorate(err, "%v", arg)
} Generics won't help us here, since variadic arguments would require the same type for all arguments in that case, and we can't be generic over func Equal[T comparable, A fmt.Stringer](ctx context.Context, a, b T, format string, args ...A) error // no bueno Another option would be to use code generation to generate code which only incurs the boxing cost on failures. Again, not very ergonomic. We could also pass a closure to generate the format args, but that's not ergonomic either, and likely comes with its own performance penalty. |
Well yes, but we generally want most assertions enabled in release builds too, if we can -- we need to guard against correctness violations, we want to maximize coverage across environments, and we want to be notified about violations. This allocation would likely be enough by itself to prevent their use in certain core code paths. |
Then again, I suppose one always has the option of manually switching to |
[Guy with no knowledge of Go generics voice] Can you define your own interface which is "either |
Maybe the interface boxing still kicks in in that case. The |
No, unions currently can't contain interfaces.
I don't think it does, because we instantiate a generic function for each concrete type. However, we can't mix different concrete types, they must all have the same type because the variadic parameters have type
Yeah, this would still incur the boxing, but it would also incur the substantial formatting cost even when we don't need it (the common case). |
108272: util/must: revert API r=erikgrinaker a=erikgrinaker This patch reverts #106508, since `@RaduBerinde` [pointed out](#107790 (review)) a performance flaw where it will often incur an allocation on the happy path due to interface boxing of the format args. Other options are considered in #108169. We'll revisit runtime assertions with a different API that avoids this cost on the happy path. Co-authored-by: Erik Grinaker <grinaker@cockroachlabs.com>
Might be best to wait for golang/go#37739 |
Closing, we'll need a different API, deferring to #94986. |
The
must
package isn't suitable for use in hot paths. The main problem is that it takes printf-style format args as[]interface{}
:cockroach/pkg/util/must/must.go
Lines 265 to 275 in 3250477
This has a couple of problems: the interface boxing incurs an allocation for certain types, and arguments can escape to the heap. One might hope that the compiler deferred the boxing until after the
a == b
condition when inlining, but no such luck.Most likely, the better option is to significantly simplify the API. Something basically similar to:
This only incurs the cost of boxing on the unhappy path, and the heap escape can similarly be deferred by copying the format args value in the branch.
However, let's consider some other options here, and see if we can come up with something suitable.
Jira issue: CRDB-30342
The text was updated successfully, but these errors were encountered: