I have a struct which contains two data members:
struct A{
short b;
short c;
};
and a vector containing many of these struct objects:
std::vector<A> vec;
at runtime the data members A.b
and A.c
from struct objects are set to zero/a value x
. However, b
and c
must be modified at the same time- one cannot be updated separately without the other. I was planning on using an atomic compare_exchange_weak()
to do the update.
I am not sure whether I should represent each struct as an std::atomic<A>
in the vector, or whether I should place a union inside the struct, to combine the two shorts in to a single uint32_t
and then modify this:
union A {
struct {
short b;
short c;
};
uint32_t d;
};
What would be the best solution? Should I store a vector of:
std::vector<uint32_t>
and upon accessing each element, reinterpret_cast to A, to obtain d
?
I would like the locking to be as least-intrusive as possible.
Portability is not required, this will be on Linux, 64-bit, x86, GCC 4.8+ compiler
Unless the hardware you are targetting supports double compare-and-swap (which is probably not the case), I think you only have two portable solutions:
Introduce a higher-level lock (mutex or spinlock depending on your preference) and carry all operations on b
and c
within the scope of the acquired lock. A mutex is heavy, but std::atomic_flag
is lock-free and very light-weight even in high-contention situations.
Merge both members into a single std::atomic<int>
and split that int
into short
s through bit masking. Note that this requires sizeof(int) >= 2 * sizeof(short)
. Use fixed-size integer types if you need to enforce that.
To determine which solution is the fastest, benchmarks, of course.
If you know the number of struct A
you will need at compile time, I'd suggest putting them into an std::array
. If you don't, std::vector
is fine as long as this number stays constant throughout the lifetime of the vector. Otherwise, since std::atomic<T>
is neither copyable nor movable, you will have to write your own copy/move constructor for struct A
.
I recommend wrapping the variables in a class with a getter and setter guarded by a mutex, and make the variables private.
Using an union could cause unforeseen functionality based on machine architecture and compiler flags.
EDIT Results of running a simple program that stores values of the given struct type (Linux 32bit, x86):
Simply make a union
of a large enough atomic type. This is what I use (the code snippet is not perfectly portable, using <cstdint>
types instead of short
and int
would surely be preferrable -- but it's good enough for me as it is), and it works perfectly fine and reliably since... practically forever:
union A {
struct {
short b;
short c;
};
std::atomic<int> d;
};
(In fact, my implementation is slightly more complicated: I'm wrapping the whole thing into another struct
out of habit, so A
is a struct
containing a union
rather than being a union
. Traditionally union
had weird constraints about constructors, my initial implementation predates C++0x, and my A
needs a constructor. But of course using C++11's <atomic>
these considerations become alltogether obsolete, since those artificial constraints no longer exist)
Note that std::atomic
may be lock-free but is not guaranteed to be (except for bool
). In practice, for anything the size of int
or short
, it is lock-free on every "serious, no-joke" architecture, and on most modern architectures it's lock-free for something of pointer size, too (though there exist exceptions, notably the very first generation of x86_64 chips from AMD).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With