3 ms·
> The problem with this is that once you get any deeper than the first call, that helpful "&arg" goes away and it's just "arg". There's a HUGE difference from
by dragontamer 4y ago
> The problem with this is that once you get any deeper than the first call, that helpful "&arg" goes away and it's just "arg".
There's a HUGE difference from a "int& foo" and a "int* foo". ESPECIALLY if I'm doing incredibly sophisticated atomic operations, memory-barrier, and atomic operations (IE: atomic<int>& vs atomic<int>*).
Take a guess which one I'm more comfortable passing around.
This isn't a case of "param->Operation1(1)" like you're talking about. This is a simple integer being used in some of the most complex, sophisticated, low-level optimizations known to the modern programmer. Knowing when, where, why, and how these things are dereferenced is the entire damn point of memory-barriers and atomics. I want _NO_ surprises, at all.
At least, within the context of "C++ Concurrency Models". To be honest, this is the stuff that causes me to run away screaming. But if I'm forced to write code involving the words "compare-and-swap", "atomic", or "barrier", you damn well bet that I'm going to be doing it with "int*" rather than "int&".
--------
Every style of code has its place. When dealing with atomics, I definitely prefer pointers over references. If you're running around in object-oriented land, you probably don't care and have modest benefits to the reference. But the minute you start studying "A-B-A" problems and the difference between "load-acquire" vs "load-consume" and the possible memory-orderings that can happen, you suddenly want to become extremely explicit over your dereferencing.
--------
"When" did the dereference occur? Is it compatible between Thread#1 and Thread#2? Are there any weird "gotchas" that could occur? Has the dependencies you need been ordered correctly in all threads that are active?
If you're unaware of what's going on here, give this a readover. https://en.cppreference.com/w/cpp/atomic/memory_order https://en.cppreference.com/w/cpp/atomic/memory_order
I promise you, you'll have some headaches. This is by far the hardest subject in all of low-level programming I've come across. And being more explicit about when the dereferences happen is very useful.
- cpgxiii 4y agoSo, to be clear, you appear to either (a) have a case where there are significant behavior differences between passing by mutable reference versus pointer (and thus _need_ to use a pointer), or (b) are relying on operations to certain types being "atomic" and on the compiler and processor not mangling the code you write without using any explicit concurrency-protecting constructs (e.g. std::atomic<T>) which you believe they would do to a reference argument.
- dragontamer 4y agoHave you read the post in question? Its a good introduction to C++ Concurrency / memory models. atomic<int>* blah; a = *blah; // This is relatively fast. *blah = b; // This is slow: store-release. blah = c; // This is fast, no store-release operation going on. Just a pointer-assignment Anyone dipping down to this level is interested in the performance characteristics of memory barriers. Indeed, the entire point of this exercise is to get faster than std::mutex lock / unlock after all, and squeezing the last ounce of performance out of your code. Indeed, one can argue that when using atomics, the more explicit "store" and "load" functions should be used instead. a = blah->load(); // These two examples are blah->store(b); // arguably the most explicit, preferred choice
- cpgxiii 4y ago> Have you read the post in question? Its a good introduction to C++ Concurrency / memory models. Yes, I have. And yes, I am familiar with the C++ concurrency/memory models discussed here. And, to be clear, nothing in the article is doing anything that couldn't be done just as clearly with std::atomic<bool>& as with std::atomic<bool>*. Obviously, if you actually want to be able to assign to the pointer, then you need the pointer form. > Indeed, one can argue that when using atomics, the more explicit "store" and "load" functions should be used instead. Yes, this is the style I strongly prefer. Of course, here it doesn't matter between a = blah->load(); blah->store(b); or a = blah.load(); blah.store(b); and choosing to use the pointer form is often just introducing nullability where you don't need it.
- kllrnohj 4y ago> choosing to use the pointer form is often just introducing nullability where you don't need it. _Nonnull int* whatever; Tada! Nullability avoided. https://releases.llvm.org/3.8.0/tools/clang/docs/AttributeReference.html#nonnull https://releases.llvm.org/3.8.0/tools/clang/docs/AttributeRe...
- bonzini 4y agoThat's not a store-release, it's a store-release followed by a full memory barrier. A store-release is fast but does not order the store against subsequent loads.