tinygo
tinygo copied to clipboard
`bytes.Equal` shouldn't allocate
TinyGo uses the bytes package from upstream Go.
// Equal reports whether a and b
// are the same length and contain the same bytes.
// A nil argument is equivalent to an empty slice.
func Equal(a, b []byte) bool {
// Neither cmd/compile nor gccgo allocates for these string conversions.
return string(a) == string(b)
}
However, the []byte->string conversion does allocate on TinyGo. We should replace calls to bytes.Equal with a call to our own function that doesn't allocate.
I'd like to avoid an optimization like that because it just seems a bit hacky to me. (We have a number of those optimizations in TinyGo, but generally they're to replace a call to a LLVM intrinsic). Instead, I'd propose we add a new transform just like our current OptimizeStringToBytes but the other way round.
Here is the transform:
https://github.com/tinygo-org/tinygo/blob/731532cd2b6353b60b443343b51296ec0fafae09/transform/rtcalls.go#L12-L18
The benefit of doing it like that is that it also fixes every other cast that looks like that, instead of just the bytes.Equal case.
shouldn't that do something equivalent like memcmp?
@assyrianic Yes. You can't just immediately defer to memcmp though because you need to check for nil slices, check the lengths, and dereference the data pointers.
We should replace calls to
bytes.Equalwith a call to our own function that doesn't allocate.
Maybe you're right. I looked at #4289 but realized that optimization is a lot less trivial than the existing string-to-[]bytes optimization. I think it can be replaced with a call to runtime.stringEqual (using the len part of the byte slice for the string length).
@lpereira
There are quite a bit of string(b) conversions in the standard library, too, that would benefit from #4289. I can add a transform that makes bytes.Equal() better by itself, but I'm not sure how beneficial it would be, to be honest.