6 ms·
> Weak atomics give you the same kind of performance as benign data races but they have well defined semantics. That is a great argument. Self-explanatory code
by Hokusai 6y ago
> Weak atomics give you the same kind of performance as benign data races but they have well defined semantics.
That is a great argument. Self-explanatory code make it easier to understand what are the design goals and usage of that code.
> Traditional “benign” races are likely to be broken by optimizing compilers or on tricky architectures.
This is even better. One of the reasons is so hard to move code from one architecture to another one is because not only word sizes but small non-defined behaviors.
I had the pleasure to make C++ code compile for WebAssembly. It is challenging and interesting the unexpected ways that it breaks. And how many times the mental model that we have as developer and the reality of the C++ specification differ in small ways.
My most absurd example was a test application that had to crashi on the push of a button, to verify that all logs and recovery code was working. To crash the application the code was accessing a NULL pointer. Alas, WebAssembly allowed read access to address Zero without problem. So, the button did nothing when compiled in WebAssembly.
- rezonant 6y agoHuh, I didn't know this was the case for WebAssembly. I found a discussion about it amongst the WebAssembly designers. https://github.com/WebAssembly/design/issues/204 https://github.com/WebAssembly/design/issues/204 -- Looks like efficient implementation of mprotect() was a sticking point, and its been relegated to a Future Feature https://github.com/WebAssembly/design/blob/master/FutureFeatures.md#finer-grained-control-over-memory https://github.com/WebAssembly/design/blob/master/FutureFeat...
- msla 6y ago> To crash the application the code was accessing a NULL pointer. Alas, WebAssembly allowed read access to address Zero without problem. Good thing nothing in the C or C++ standards mandates NULL be address all-bits-zero regardless of how it's sometimes spelled. Any compiler which assumes that memory access to address all-bits-zero is access to NULL is not compliant.
- PaulDavisThe1st 6y agoSince C++11, the definition of NULL is: an integer literal with value zero, or a prvalue of type std::nullptr_t See https://wg21.cmeerw.net/cwg/issue903 https://wg21.cmeerw.net/cwg/issue903 for a case where NULL was did not evaluate to zero.
- AlotOfReading 6y agoThe best part of C++ is how even the caveats get caveats. The fun never ends for rules lawyers.
- wolfgang42 6y agoI thought “integer literal zero (in pointer contexts)” was the definition of NULL in all versions of C. As I understand it the resulting pointer doesn’t necessarily have to have a value of 0 (even though that’s what it says in the source code) so long as it behaves appropriately nullish—i.e. the compiler can translate “int *foo = 0;” as “MOV foo 0xDEADCODE” so long as that value won’t be used for any legitimate pointer.
- msla 6y agoThe above-linked site about POSIX has this paragraph: https://www.austingroupbugs.net/view.php?id=940 https://www.austingroupbugs.net/view.php?id=940 > The C standard goes to great length to ensure that NULL (and for that matter, any representation of the null pointer, which is written in source code by converting a constant value 0 to a pointer) need not be represented in hardware by all 0 bits (that is, 'int p = 0;' or 'static int p;' is allowed to compile to assembly instructions that assign a non-zero bit pattern to the location named p). And in fact, there are historical machines where the most efficient null pointer in hardware was indeed a non-zero bit pattern: http://c-faq.com/null/machexamp.html http://c-faq.com/null/machexamp.html ... and the site it links to has this: > Q: Seriously, have any actual machines really used nonzero null pointers, or different representations for pointers to different types? > A: The Prime 50 series used segment 07777, offset 0 for the null pointer, at least for PL/I. Later models used segment 0, offset 0 for null pointers in C, necessitating new instructions such as TCNP (Test C Null Pointer), evidently as a sop to [footnote] all the extant poorly-written C code which made incorrect assumptions. Older, word-addressed Prime machines were also notorious for requiring larger byte pointers (char 's) than word pointers (int 's). [snip] > The Symbolics Lisp Machine, a tagged architecture, does not even have conventional numeric pointers; it uses the pair <NIL, 0> (basically a nonexistent <object, offset> handle) as a C null pointer.
- deleted 6y ago[deleted]
- cesarb 6y agoAFAIK, the POSIX standard mandates that all-bits-zero be treated as NULL (https://www.austingroupbugs.net/view.php?id=940 https://www.austingroupbugs.net/view.php?id=940).
- mehrdadn 6y agoDespite the quote, it's just not true that relaxed atomics will give the same kind of performance. They may, but they may not, depending on the situation. The issue with them is that they're still atomic throughout the entire program, even in the situations where you can guarantee there are no other threads running and hence don't need atomicity. (Perhaps in particular scenarios, like with small inputs, you never even spawn additional threads to begin with.) For native-sized integers, ensuring atomicity might or might have overhead depending on the implementation. But it will at least have overhead for larger objects.
- PaulDavisThe1st 6y ago> But it will at least have overhead for larger objects. Given that there's essentially no way to do atomic operations on larger objects (than machine-level types), all std::atomic<> for such types must involve a mutex, and are therefore guaranteed to have performance implications. std::atomic<T> provides syntax that treats std::atomic<int> and std::atomic<MyHugeObject> in precisely the same way. This is useful, but entirely misleading if you care about performance.
- mehrdadn 6y agoYou're arguing for my point, not against it. (Though your basis for it isn't even true; HTM could avoid mutexes.)
- PaulDavisThe1st 6y agoHTM?
- mehrdadn 6y agoHardware transactional memory. TSX-NI in Intel's case.
- pkolaczk 6y agoThey don't have to involve a mutex but some kind of synchronisation. A sequential lock is another way of efficiently implementing that, without blocking the happy read path. Instead of locking the large object, it detects if a read was atomic and retries if it was not.
- ganafagol 6y ago> I had the pleasure to make C++ code compile for WebAssembly. It is challenging and interesting the unexpected ways that it breaks. And how many times the mental model that we have as developer and the reality of the C++ specification differ in small ways. And that's because the way most programmers write C/C++ programs is with the mental model of the actual machine they are writing the code for. That's not so strange, it's being taught that way. It's even praised as a skill to look at the generated assembly if you want to understand how something is compiled. But that approach is fundamentally broken. One needs to program against the abstract machine which defines the semantics of your programming language. Everything else leads to unportable and subtly broken code with hidden assumptions of how undefined behavior is resolved. Sometimes you need to do this for performance reasons? Well, then you pay the price. It's expensive.