std::atomic treat a pair of atomic int32 as one atomic int64?
Asked Answered
D

2

5

I have a pair of unsigned int32

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

Sometimes I want to set start or end with compare exchange, so I don't want spurious failures that could be caused by using CAS on the entire 64bit pair. I just want to use 32 bit CAS.

_end.compare_exchange_strong(old_end, new_end);

Now I could fetch both start and end as one atomic 64bit read. Or two separate 32 bit reads. Wouldn't it be faster to do one 64bit atomic fetch (with the compiler adding the appropriate memory fence) rather than two separate 32 atomic bit reads with two memory fences (or would the compiler optimize that away?)

If so, how would I do that in c++11?

Danelledanete answered 13/1, 2012 at 22:55 Comment(2)
I would check if the standard allows them to be in unions.Marplot
@PlasmaHH: How would that help? It is undefined behavior to access members through a union outside of the member that the data was originally stored in. C++ doesn't say that it will make any kind of sense, even though plenty of users expect certain things around unions like this to work.Inhabitancy
B
3

The standard doesn't guarantee that std::atomics have the same size as the underlying type, nor that the operations on atomic are lockfree (although they are likely to be for uint32 at least). Therefore I'm pretty sure there isn't any conforming way to combine them into one 64bit atomic operation. So you need to decide whether you want to manually combine the two variables into a 64bit one (and only work with 64bit operations) or not.

As an example the platform might not support 64bit CAS (for x86 that was added with the first Pentium IIRC, so it would not be availible when compiling 486 compatible. In that case it needs to lock somehow, so the atomic might contain both the 64bit variable and the lock. Of

Concerning the fences: Well that depends on the memory_order you specify for your operation. If the memory order specifies that the two operations need to be visible in the order they are excuted in, the compiler will obviously not be able to optimize a fence away, otherwise it might. Of course assuming you target x86 only memory_order_seq_cst will actually emit a barrierinstruction from what I remember, so anything less would impede with instruction reordering done by the compiler, but wouldn't have an actual penalty).

Of course depending on your platform you might get away with treating two std::atomic<int32> as one of int64 doing the casting either via union or reinterpret_cast, just be advised that this behaviour is not required by the standard and can (at least theoretically) stop working at anytime (new compiler verison, different optimization settings,...)

Boatswain answered 13/1, 2012 at 23:42 Comment(2)
I think atomic<int64> could include a critical section on some platforms, there's no reason to believe either that the object is exactly 64 bits or that it's twice the size of atomic<int32>, nevermind having things line up right.Arcuate
True, but irrelevant to me. If atomic is any larger than it's underlying integral type I ditch it for a more sane implementation. I only care about x64.Danelledanete
E
1

If your two ints require atomic updates, then you must treat them as a single atomic 64-bit value, you really have no other choice. Separate integer updates are not atomic and not viable. I concur that Unions are not relevent here, and suggest that instead you simply cast the pair of integers as (INT64) and perform your Cas64.

Using a critical section is overkill--use the Cas64, they only cost about 33 machine cycles (unoposed), while critical sections cost more like 100 cycles unoposed.

Note that it is commonplace to perform this exact same operation on versioned pointers, which in 32-bit consist of a 32-bit pointer and a 32-bit pointer, updated together as one, using Cas64 as described. Also, they actually do have to "line up right", because you never want such values to overhang a cache line boundary.

Execute answered 16/3, 2012 at 6:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.