Q: C++: Optimized atomics for a rapid single-producer multiple-consumer environment I'm currently on a project which has quite tight loops and real-time requirements. Premature optimization is definitely not a vice here. I want to exploit the possible relaxed options of C++ atomics as much as possible to achieve my goals. Here is my (thread-unsafe) setup:
// Shared data structure
uint8 data[100];
uint64 timeStamp = 0;

// Consumer threads 1...N run this
void loopConsume() {
    uint64 lastTimeStamp = 0;
    uint8 localData[100];
    for (;;) {
        while (timeStamp == lastTimeStamp);
        memcpy(localData, latestData.data, 100);
        lastTimeStamp = timeStamp;
        doSomething(localData);
    }
}

// Producer callback (singleton)
void produce(uint8 *newData) {
    memcpy(latestData.data, newData, 100);
    latestData.timeStamp++;
}

Here is a clumsy (but ostensibly, correct) attempt at making it thread-safe (and thread-correct) using 2 default sequential consistency atomics:
#include <atomic>

// Shared variables
uint8 data[100];
std::atomic<uint64> timeStamp = 0;
std::atomic<int32> numReaders = 0; // -1 means producer is writing

// Consumer threads 1...N run this
void loopConsume() {
    uint64 lastTimeStamp = 0;
    uint8 localData[100];
    for (;;) {
        uint64 t = timeStamp.load();
        while (t == lastTimeStamp) t = timeStamp.load();
        lastTimeStamp = t;
        for (;;) {
            int32 r = numReaders.load();
            if (r >= 0 && numReaders.compare_exchange_weak(r, r + 1))
                 break; 
        }
        memcpy(localData, data, 100);
        doSomething(localData);
        numReaders.fetch_sub(1);
    }
}

// Producer callback (singleton)
void produce(uint8 *newData) {
    while (!numReaders.compare_exchange_weak(0, -1));
    memcpy(data, newData, 100);
    timeStamp.fetch_add(1);
    numReaders.store(0);
}

As you can see, there is a lot of atomic operations, all of which by default use sequential consistency. I'm fairly certain many of them can be relaxed. But which ones? Can I get away with fewer operations or a simpler setup?
Another question is contention. I expect there to be few readers and for them to copy over their data to their local variable quickly. Is there then any real advantage to having a kind read-write lock (as above), or just a regular mutex? If a regular mutex is faster in this case, can I get away with incorporating it into the time stamp atomic variable somehow?
Thanks a lot!
