- DCAS-atomic - 5 Updates
- having the same method but with a different argument data type - 3 Updates
- Is this undefined behavior? - 15 Updates
- Fake switch or fake loop only to break - 1 Update
- std::map rebalancing - 1 Update
Bonita Montero <Bonita.Montero@gmail.com>: Jun 17 07:11AM +0200 The cmpxchg has a membar itself. |
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Jun 17 01:12PM -0700 On 6/16/2020 10:11 PM, Bonita Montero wrote: > The cmpxchg has a membar itself. you mean LOCK cmpxchg on x86, yes you are correct. Have you ever programmed for the SPARC in RMO mode or PPC? What about a DEC Alpha? Please read all of this: https://en.wikipedia.org/wiki/Memory_ordering Can you name some algorithms that require explicit membars on an x86 when using atomic loads and stores? I know of several. |
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Jun 17 01:34PM -0700 On 6/17/2020 1:12 PM, Chris M. Thomasson wrote: > https://en.wikipedia.org/wiki/Memory_ordering > Can you name some algorithms that require explicit membars on an x86 > when using atomic loads and stores? I know of several. On x86 wrt atomic loads and stores... Atomic Store to A release Atomic Load from B acquire Well.... The load from B can be hoisted up above the store to A on an x86. This can occur because A and B are different locations. The MFENCE instruction can take care of this, to ensure that the load from B is performed _after_ the store to A. Store to A release MFENCE Load from B acquire Okay, now, it works. Btw, there are several algorithms that depend on this. Using a LOCK RMW on x86 can do this as well. It has the right membar. Iirc, there was a problem with a bugged processor. It was a long time ago. Iirc it was part of the Plan9 problem. Iirc, it had something to do with using an atomic store to release a spinlock. I first learned about it way back on comp.programming.threads. |
Bonita Montero <Bonita.Montero@gmail.com>: Jun 18 12:02AM +0200 > you mean LOCK cmpxchg on x86, yes you are correct. I'm talking about the used intinsics of MSVC and gcc. They have full barriers on all CPUs. > Have you ever programmed for the SPARC in RMO mode or PPC? > What about a DEC Alpha? That are all dead CPUs. |
Bonita Montero <Bonita.Montero@gmail.com>: Jun 18 12:03AM +0200 Sorry, but youre talkin stupid stuff. I'm using the MSVC / gcc intinsics, and both have full barriers. Look at this code: long xchg; void f( int &i, int &j, int &k ) { k = i + j; __sync_bool_compare_and_swap( &xchg, 0, 1 ); k = i + j; } This compiles to this with g++: movl (%rsi), %eax addl (%rdi), %eax movl $1, %ecx movl %eax, (%rdx) xorl %eax, %eax lock cmpxchgq %rcx, xchg(%rip) movl (%rsi), %eax addl (%rdi), %eax movl %eax, (%rdx) ret So the compiler doesn't optimize away the duplicate calculation because __sync_bool_compare_and_swap has acquire- as well as release- behaviour - logically from the view of the instruction -stream as well as related to the internal order of the loads and stores of the CPU. |
Lynn McGuire <lynnmcguire5@gmail.com>: Jun 17 01:52PM -0500 On 6/16/2020 2:21 PM, Bonita Montero wrote: > } > return this; > } Thanks, I need to review and think about this. There are actually over ten base data types in my software. Lynn |
Lynn McGuire <lynnmcguire5@gmail.com>: Jun 17 01:57PM -0500 On 6/16/2020 3:18 PM, Alf P. Steinbach wrote: > X/Y problem where you have a Real Issue X, you imagine that Y could be a > good solution to X, you can't make Y work cleanly, and you ask about Y? > - Alf This is code that I have had in our product since 2003 that I am revising at the moment and disturbed by our duplicated code in each of the 10+ methods. This code was actually written in Smalltalk back in 1990 or so. Two of my programmers and I converted it to C++ in 2002-3 when we rebuilt the product totally in Win32 C++. ObjPtr is our base class that mimics the base object class in Smalltalk. Works very well for us in 450,000+ lines of C++ code. I had not though about using a template. I had a bad experience with a template a couple of decades ago in the Visual C++ compiler and stay away from them now. Thanks, Lynn |
Jorgen Grahn <grahn+nntp@snipabacken.se>: Jun 17 09:05PM On Wed, 2020-06-17, Lynn McGuire wrote: ... > I had not though about using a template. I had a bad experience with a > template a couple of decades ago in the Visual C++ compiler and stay > away from them now. I can happily recommend templates; they are good for all kinds of tasks in all kinds of programs. I wish it was easier to learn how to apply them, though. For me it's still trial and error: I can "smell" an opportunity to simplify code with use of templates, but not know exactly how to proceed. Then after some failed attempts, I may end up with a really clean solution. /Jorgen -- // Jorgen Grahn <grahn@ Oo o. . . \X/ snipabacken.se> O o . |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 17 12:24AM +0100 On Tue, 16 Jun 2020 16:11:31 -0700 > So, basically, I need to use placement new and explicit dtor via > calling the dtor ~Type(). Then it becomes a "real object" in C++. I > thought that POD would be different in a sense. Yes, although you don't actually need to call the destructor because your types are trivial. But there are other issues with your code which I have alluded to in other posts. I should use 'new unsigned char[...]' instead of std::malloc, placement new your 'header' struct into it and (as you do at present) cast the buffer part to char* if you really want char* instead of unsigned char* for the buffer. As I have indicated in those posts I think that cast is valid but you never know with C++17/20: if the committee don't understand the rules who are we to say. As I have also mentioned in other posts I agree with your sentiments about PODs/trivial types. That seems to me to be another fail in the standard. It ought to be valid in my view, but it isn't. |
Juha Nieminen <nospam@thanks.invalid>: Jun 17 06:36AM > Technically, constructing the 'header' object in the malloc'ed buffer > is also reputed to be undefined behaviour if you do it otherwise that > through placement new, even though 'header' is a trivial type. Thinking about it, it might actually have merit to worry about such things being UB, no matter how "technically" and how obscure the rule may be. One could easily just think like "who cares if it's "technically" UB? There's no practical implementation where it would cause anything else than intended behavior." The problem is, UB allows the compiler to do whatever it wants. Including not doing what the programmer "intended" for it to do. Tehcnically speaking if the compiler detects UB, it's allowed to think "this is UB, I don't need to do anything here, I'll just skip the whole thing and optimize it all away". Suddenly you might find yourself with an incredibly obscure "compiler bug" where the compiler isn't generating the code you wrote... when in fact it's not a compiler bug at all. I remember a particularly nasty bug many years ago in the Linux kernel that was caused precisely by this kind of thing. That part of the kernel code was technically UB... and the compiler did whatever it wanted and not the thing that the code was "intending" it to do (and, IIRC, it optimized that part away, which caused the intended thing to not happen. I don't remember now the exact reason, but might have had something to do with deliberately dererencing a null pointer, which is UB, and which the compiler was "optimizing" away because the standard allowed it to.) |
Juha Nieminen <nospam@thanks.invalid>: Jun 17 06:37AM > Don't care for the spec. It works with any compiler and > it will work with any compiler that will ever exist. The problem is that if you trigger UB, the compiler is allowed to do whatever it wants with it. Including not doing what you want. |
Scott Newman <scott69@gmail.com>: Jun 17 08:42AM +0200 >> it will work with any compiler that will ever exist. > The problem is that if you trigger UB, the compiler is allowed to > do whatever it wants with it. Including not doing what you want. Not in this case. |
Juha Nieminen <nospam@thanks.invalid>: Jun 17 06:47AM >> The problem is that if you trigger UB, the compiler is allowed to >> do whatever it wants with it. Including not doing what you want. > Not in this case. What do you mean? The compiler is *always* allowed to do whatever it wants if something is UB. |
Scott Newman <scott69@gmail.com>: Jun 17 09:06AM +0200 >> Not in this case. > What do you mean? The compiler is *always* allowed to do whatever it wants > if something is UB. There's no UB with what Chris does initially. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 17 10:49AM +0100 On Wed, 17 Jun 2020 06:36:24 +0000 (UTC) > now the exact reason, but might have had something to do with deliberately > dererencing a null pointer, which is UB, and which the compiler was > "optimizing" away because the standard allowed it to.) Yes I agree that is the problem with undefined behaviour. Which I suppose is one reason why the standard committee should be careful before spraying it over the standard and undermining programmers' well established past practices. |
Manfred <noname@add.invalid>: Jun 17 03:27PM +0200 On 6/17/2020 12:39 AM, Chris M. Thomasson wrote: >> Would would one need to cast the return value of malloc to (X*)? > Why would one need to cast the return value? void* can do it as is, > right? In pure C... In C yes, in C++ a type cast is required. A static_cast would suffice. This is why in my other posts I wonder why such a type cast could not be enough (at least for PODs) to overcome the issue about object creation and lifetime that is described in the proposal. Instead of going down some contrived path like requiring malloc to "look into the future" and such. |
Manfred <noname@add.invalid>: Jun 17 03:35PM +0200 On 6/17/2020 1:11 AM, Chris M. Thomasson wrote: > Touche! So, basically, I need to use placement new and explicit dtor via > calling the dtor ~Type(). Then it becomes a "real object" in C++. I > thought that POD would be different in a sense. If you look at the quote from Bjarne's "The C++ Programming Language" that I posted earlier you see that he himself does not require using placement new for malloc on a type with no constructor like this. Apparently, for some reason, the standard landed somewhere else. |
boltar@nowhere.co.uk: Jun 17 03:20PM On Wed, 17 Jun 2020 15:27:26 +0200 >> Why would one need to cast the return value? void* can do it as is, >> right? In pure C... >In C yes, in C++ a type cast is required. A static_cast would suffice. A static_cast is just a more verbose C cast and the two are interchangable. |
Manfred <noname@add.invalid>: Jun 17 05:41PM +0200 >>> right? In pure C... >> In C yes, in C++ a type cast is required. A static_cast would suffice. > A static_cast is just a more verbose C cast and the two are interchangable. No, they are not. https://en.cppreference.com/w/cpp/language/expressions#Conversions |
boltar@nowhere.co.uk: Jun 17 03:46PM On Wed, 17 Jun 2020 17:41:33 +0200 >> A static_cast is just a more verbose C cast and the two are interchangable. >No, they are not. >https://en.cppreference.com/w/cpp/language/expressions#Conversions Its close enough as substitute in 99% of circumstances. Though personally I prefer a C cast, C++ has enough verbiage as it is. |
Manfred <noname@add.invalid>: Jun 17 07:13PM +0200 >> https://en.cppreference.com/w/cpp/language/expressions#Conversions > Its close enough as substitute in 99% of circumstances. Though personally > I prefer a C cast, C++ has enough verbiage as it is. Read more carefully: A C-style cast can perform a reinterpret_cast (possibly even combined with a const_cast). This is a major difference with static_cast. Moreover, verbosity has been widely publicized as intentional for cast operators, for obvious reasons. |
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 17 06:32PM +0100 On Wed, 17 Jun 2020 15:35:06 +0200 Manfred <noname@add.invalid> wrote: [snip] > that I posted earlier you see that he himself does not require using > placement new for malloc on a type with no constructor like this. > Apparently, for some reason, the standard landed somewhere else. He even gives an example (4th edition) of the dangers of realloc, while constructing trivial types in malloc'ed memory as part of the explanation, on the basis that using malloc for trivial types is fine. The fact is that until C++17's new object lifetime rules everyone believed this usage was OK with trivial types, and it was commonplace to static_cast malloc'ed memory to a pointer to such types and manipulate them. The first time I heard that this was now thought to be incorrect was about 2 years ago. There will be shed-loads of code out there which does this. Similarly the new requirement in C++17 for std::launder as a supplement to reinterpret_cast and the strict aliasing rules for some cases. The standard committee probably managed to persuade themselves that pre-C++17 usage was undefined in some way (you can pretty much persuade yourself of anything in the C++ standard if you try hard enough). But instead of supporting ancient usage they decided to deliberately break it. I really can't respect that kind of thing, and it has certainly affected my approach when considering programming languages. I heard tell that Stroustrup was opposed to the object lifetime changes in C++17: if so, he lost the argument. |
James Kuyper <jameskuyper@alumni.caltech.edu>: Jun 17 01:05PM -0700 > On Wed, 17 Jun 2020 17:41:33 +0200 > Manfred <noname@add.invalid> wrote: > >On 6/17/2020 5:20 PM, boltar@nowhere.co.uk wrote: ... > >https://en.cppreference.com/w/cpp/language/expressions#Conversions > Its close enough as substitute in 99% of circumstances. Though personally > I prefer a C cast, C++ has enough verbiage as it is. No, they are not interchangeable - they're not even close to being 99% interchangeable. "The conversions performed by (4.1) — a const_cast (8.2.11), (4.2) — a static_cast (8.2.9), (4.3) — a static_cast followed by a const_cast, (4.4) — a reinterpret_cast (8.2.10), or (4.5) — a reinterpret_cast followed by a const_cast, can be performed using the cast notation of explicit type conversion. (4.6) — a pointer to an object of derived class type or an lvalue or rvalue of derived class type may be explicitly converted to a pointer or reference to an unambiguous base class type, respectively; (4.7) — a pointer to member of derived class type may be explicitly converted to a pointer to member of an unambiguous non-virtual base class type; (4.8) — a pointer to an object of an unambiguous non-virtual base class type, a glvalue of an unambiguous non-virtual base class type, or a pointer to member of an unambiguous non-virtual base class type may be explicitly converted to a pointer, a reference, or a pointer to member of a derived class type, respectively." (8.4) Therefore, if you run into a C-style cast, there's 7 different types of conversions it might be performing that cannot be performed by a static_cast<> alone. I've no hard figures on this, but I suspect that the number of C-style casts that perform one of those 7 other conversions is a lot higher than 1%, and those casts cannot be replaced by a static_cast<>. Replacing a working static_cast<> with a C-style cast always results in working code, but is a bad idea. However, your comment about preferring C-style casts suggests that you might not agree. I'll explain why it is for the benefit of other people - feel free to stop reading here. Stroustruap split the C style cast into multiple different named casts precisely because accidentally using a C style cast to perform a conversion more dangerous than the one you thought you were performing was one of the most common mistakes made by C programmers. Every conversion that can be performed by a C-style cast that cannot be done by a static_cast<> is a more dangerous conversion than a static_cast<>. Stroustrup's idea was to have multiple different named casts, each of which could only do some of the things that a C-style cast could do, so you would only get one of the more dangerous conversions if you explicitly requested it. It is an error requiring a diagnostic to try to use one of those casts to perform a conversion that it couldn't do. Those diagnostics are the key benefit that comes proper use of the named casts. |
Juha Nieminen <nospam@thanks.invalid>: Jun 17 06:44AM > if (cond1 && cond2 && cond3 && cond4) { > DoSomething(); > } Only possible if there isn't any other code inside those conditionals. Like: if(!cond1) return; doSomething(); if(!cond2) return; doSomethingElse(); if(!cond3) return; andSomethingOther(); if(!cond4) return; theFinalThing(); |
Juha Nieminen <nospam@thanks.invalid>: Jun 17 06:41AM >>Have you learned how to count to three yet? > If you think a major rebalance would only involved updating 3 nodes then > you have no idea how balancing works. Rebalancing a binary tree (such as a red-black tree) requires updating O(log(n)) nodes. Most usually approximately 2*log(n) nodes (no big-O). So if your tree has 100 million nodes, rebalancing it after an insertion or deletion requires updating about 54 nodes (give or take). |
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page. To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com. |
No comments:
Post a Comment