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 shorts 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