-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Add LLVM's unordered intrinsic to Rust. #19835
Conversation
The more I read about this the less useful it seems... if it's a version of Monotonic that can be freely reordered, I doubt it's useful for much if you already know a variable is shared. |
@thestinger The problem is that I'm not even sure it's useful for that (or at least, notably more useful than Monotonic). From the LLVM atomics documentation on Monotonic:
Similar documentation elsewhere in LLVM suggests that unordered loads can also probably be reordered around everything else, with the possible exception of explicit fences (and it's not even completely clear to me that unordered loads and stores respect those). So I am not sure whether it really buys us anything over |
@pythonesque Do you still think this has value? Do you need a reviewer? |
@gankro I think it still has value (for the same reason it has value in Java) and I don't think it would be harmful to add to Rust. I just have fewer immediate usecases for it. |
@thestinger are you willing to review? |
I think it needs a feasible use case before it should be merged. |
@thestinger I think certain operations are safe with it where the value can't ultimately affect correctness (e.g. while writing a spinlock, doing an unordered load while spinning to see if it's worth doing the CAS, or the aforementioned racy counters). In practice I suspect a monotonic load will have exactly the same codegen there (up to operation reordering that you likely don't want), plus saner behavior overall (and if you want more specific fast behavior on particular architectures you probably have to drop into assembly anyway). So really the only usecase I see is implementing something like a JVM bytecode interpreter, where you'll have non-Rust code running in a language that can't enforce Rust's no data race guarantee. TBH, I don't know if that's a useful enough case. If you don't think so, feel free to close this. |
@pythonesque: I think it's enough that the code generator can generate better code in practice, considering that it's a low-level primitive. It can be implemented in terms of |
…estinger This corresponds to the JMM memory model's non-volatile reads and writes to shared variables. It provides fairly weak guarantees, but prevents UB (specifically, you will never see a value that was not written _at some point_ to the provided location). It is not part of the C++ memory model and is only legal to provide to LLVM for loads and stores (not fences, atomicrmw, etc.). Valid uses of this ordering are things like racy counters where you don't care about the operation actually being atomic, just want to avoid UB. It cannot be used for synchronization without additional memory barriers since unordered loads and stores may be reordered freely by the optimizer (this is the main way it differs from relaxed). Because it is new to Rust and it provides so few guarantees, for now only the intrinsic is provided--this patch doesn't add it to any of the higher-level atomic wrappers.
@thestinger what ended up being the concrete usecase for this? just as an optimization for "speculative" locking? |
@cmr: It's theoretically useful for speeding up approximate statistics via counters which is good enough for me. |
i.e. LLVM is a lot more free to move around the counter increments or do something like merging 3 writes into one |
This corresponds to the JMM memory model's non-volatile reads and writes to shared variables. It provides fairly weak guarantees, but prevents UB (specifically, you will never see a value that was not written at some point to the provided location). It is not part of the C++ memory model and is only legal to provide to LLVM for loads and stores (not fences, atomicrmw, etc.).
Valid uses of this ordering are things like racy counters where you don't care about the operation actually being atomic, just want to avoid UB. It cannot be used for synchronization without additional memory barriers since unordered loads and stores may be reordered freely by the optimizer (this is the main way it differs from relaxed).
Because it is new to Rust and it provides so few guarantees, for now only the intrinsic is provided--this patch doesn't add it to any of the higher-level atomic wrappers.