Does std :: atomic treat a pair of atomic int32 as one atomic int64?

I have a pair of unsigned int32

std::atomic<u32> _start; std::atomic<u32> _end; 

Sometimes I want to set the beginning or end using comparison, so I do not want the false crashes that can be caused by using CAS on the entire 64-bit pair. I just want to use 32 bit CAS.

 _end.compare_exchange_strong(old_end, new_end); 

Now I could get both the beginning and the end as a single atomic 64-bit read. Or two separate 32-bit reads. Wouldn't it be faster to make one 64-bit atomic choice (with a compiler adding the appropriate memory fence) and not two separate 32-bit bit reads with two memory barriers (or would the compiler optimize this?)

If yes, then how do I do this in C ++ 11?

+4
source share
2 answers

The standard does not guarantee that std::atomics is the same size as the base type, and that atomic operations are not blocked (although they are likely to be for uint32 , at least). Therefore, I am sure that there is no suitable way to combine them into one 64bit atomic operation. Therefore, you need to decide whether you want to manually combine the two variables into 64-bit (and work only with 64-bit operations) or not.

As an example, the platform may not support 64bit CAS (for x86, which was added with the first Pentium IIRC processor, so it will not be available when compiling compatibility with 486. In this case, it needs to be blocked somehow, so the atomic one may contain 64bit as a variable and lock.

As for the fences: Well, it depends on the memory_order that you specify for your operation. If the memory order indicates that the two operations should be visible in the order in which they are subtracted, the compiler obviously will not be able to optimize the guard, otherwise this could happen. Of course, assuming that you only target x86 with memory_order_seq_cst , it actually emits a barrier due to what I remember, so something less will prevent reordering of the commands executed by the compiler, but will not have an actual penalty).

Of course, depending on your platform, you can get away with considering two std::atomic<int32> as one of the int64 , casting either through union or reinterpret_cast , just keep in mind that this behavior is not required by the standard and can (at least least theoretically) stop working at any time (new compiler check, various optimization settings, ...)

+2
source

If your two ints require atomic updates, you should consider them as one atomic 64-bit value, you really have no other choice. Individual integer updates are not atomic and are not viable. I agree that Unions are not relevant here, and suppose instead you just throw a couple of integers like (INT64) and execute your Cas64.

Using a critical partition β€” excess β€” use Cas64, they cost only about 33 machine cycles (do not load), and critical sections cost more than 100 cycles not loaded.

Note that this is a common operation to perform this exact operation with pointer versions that in 32-bit form consist of a 32-bit pointer and a 32-bit pointer updated together as one using Cas64 as described. In addition, they really need to β€œline up” because you never want these values ​​to exceed the boundary of the cache line.

0
source

Source: https://habr.com/ru/post/1390929/


All Articles