#[Bug] HashCode.Combine returns different values for Burst and non-Burst code
1 messages · Page 1 of 1 (latest)
I'm not the original bug catcher so I didn't. However I've already posted a reproducible code in that thread by the name Laicasaane. Do you want a bug report for formal bug fixing process?
If you don't mind
Here you go IN-115333
it was reported back in July IN-106860
I was under the assumption that it was (weirdly) intended considering the burst documentation states
Do note that Burst does not follow .NET's implementation
Which tbh I feel like means it should have never been implemented in the first place
that's a huge footgun since it's not really possible to know in a large codebase whether a function gets called from burst, mono, or both, and it can completely silently break things like passing a NativeHashMap from managed code to a job and back
if it can't be implemented the same then imo it should just fail to compile with burst entirely
Followed up in thread, it is intended, it's unlikely to change and the fix is going to be a documentation fix
if you need a deterministic hash then it needs to be explicit
welp time to ban usage project-wide via CI then
is there no hope for just turning it into a hard error on burst compile?
it doesn't seem like it'd be even possible to use it "correctly" because something as innocent as setting a breakpoint while attached to the editor will seemingly change behavior, as well as burst hot loading after async compilation (besides the already mentioned common usecase of passing a hashmap between managed and burst code)
is there no hope for just turning it into a hard error on burst compile?
Maybe? Sounds like a breaking API change. Maybe we can roll out a way to taint thinks via a configuration file or something
it was a hard error (though a very obscure one that made no sense) then they implemented it so it's no longer an error but should probably never be used ^_^'
i should add, the main reason i think this is terrible behaviour is even if you have all your code burst compiled, if you hook up a debugger to a job suddenly that one job is no longer burst compiled
and it breaks everything
hooking up a debugger should never just break your app, especially without any notice
this is a mistake on the level of, overriding null comparison for gameobjects
It is very unfortunate to have that sort of hidden behavior although it occurs in more than a couple of places with burst v non-burst code. I do agree it should be clearer that this is an API doesn't have identical semantics
to me that's irrelevant
it's not acceptable to allow a piece of safe code to break the ability to use a debugger
i've been thinking since the time this post came up for a case where a user would ever use HashCode.Combine and i can not think of one - it is never safe to use, therefore it should not be usable
When CoreCLR is finally available to us this sort of hidden behaviour can be harder to fix. System.HashCode has been the recommended way to implement GetHashCode for quite a while within the .NET ecosystem, there are multitude of types now use it.
Oh wow I wonder how many bugs we have that we haven't been able to explain that's caused by this. It's even more awkward since Burst is background compiled. For our game it takes about 2-3mins to compile our biggest assembly. So I suppose after burst is enabled for that assembly it's likely a lot of stuff that relies on the hashcode being stable for the session just breaks for "no reason"
Is there any immediate replacement we can use? I suppose we just have to do manual hashing?
actually this is an excellent point, i had only considered debugger turning it off
but async compilation does the opposite, randomly turns it on at different times
imagine using the same hashmap in 2 different jobs that finish compiling at different times
Even a simple use case such as:
- populating a hashmap from a burst job
- reading from the hashmap outside of burst
seems very reasonable to me, yet it will fail in very unexpected ways if hashes are different
+1 This is really bad, this makes hashcode.combine unusable as other people have said
I'm curious what are the other places that act differently for burst vs non-bursted code?
there's also potential for floating point inconsistencies due first to mono idiosyncracies and autovectorization, but I don't have an enumerated list there
Wow, really? Glad that I avoid that then. I usually just generate GetHashCode() using Rider and ticking off "Use HashCode.Combine()". I'm always under the assumption that anything non blittable API under System is not Burst compatible.
Also, when we will get the new features of C#, I hope the record structs auto-generated GetHashCode will not suffer from the same problem 👀
It's worst since it uses managed object, ie. EqualityComparer<T>.Default. 🥲
https://sharplab.io/#v2:EYLgZgpghgLgrgJwgZwLQAUoKgW2QYQHsAbYiAYxgEtCA7ZAGgBMQBqAHwAEAmARgFgAUEM4BmAARJyhBE3HIYCOJXEBBYOQAUVWjHEBJXQ3FhihWOIBiZ2AEoA3EA==
The bright side is that you can always provide your own GetHashCode for records.
Or any generated operation.
I was looking forward to having them auto-generated and not clutter the code with boilerplate to be fair 😅
You can source-generate them instead of letting the compiler do that.
yeah, that could definitely be a nice solution
For me, I hope Unity Burst can provide these features out of box, just like the generator for Unity.Properties.
necroing this thread because i just ran into this again, and what's even worse is that fixing the GetHashCode() implementation to remove HashCode.Combine failed to trigger a burst compile and left me with a poisoned Burst Cache that still has the broken version
if i didn't recall this being a problem it would be absolute hell to debug
i really really really really want a burst compiler error for HashCode.Combine
there's no reasonable way for a code analyzer to detect what code burst is going to compile, so the diagnostic really has to be in the burst compiler itself
this would be fantastic to have
Since my last comment I've spent time migrating all types I authored away from HashCode. Since I envisioned that the need for dictionary would only increase.
yea this is something that slipped through in autogenerated boilerplate