c++multithreadinglow-latency

how to declare and use "one writer, many readers, one process, simple type" variable?


I have really simple question. I have simple type variable (like int). I have one process, one writer thread, several "readonly" threads. How should I declare variable?

I expect that when "writer" thread modifies value all "reader" threads should see fresh value ASAP.

It's ok to read and write variable at the same time, but I expect reader to obtain either old value or new value, not some "intermediate" value.

I'm using single-CPU Xeon E5 v3 machine. I do not need to be portable, I run the code only on this server, i compile with -march=native -mtune=native. Performance is very important so I do not want to add "synchronization overhead" unless absolutely required.


If I just use int and one thread writes value is it possible that in another thread I do not see "fresh" value for a while?


Solution

  • I have simple type variable (like int). I have one process, one writer thread, several "readonly" threads. How should I declare variable?

    volatile int std::atomic int

    Use std::atomic with memory_order_relaxed for the store and load

    It's quick, and from your description of your problem, safe. E.g.

    void func_fast()
    {
        std::atomic<int> a; 
        a.store(1, std::memory_order_relaxed);
    }
    

    Compiles to:

    func_fast():
        movl    $1, -24(%rsp)
        ret
    

    This assumes you don't need to guarantee that any other data is seen to be written before the integer is updated, and therefore the slower and more complicated synchronisation is unnecessary.

    If you use the atomic naively like this:

    void func_slow()
    {
        std::atomic<int> b;
        b = 1; 
    }
     
    

    You get an MFENCE instruction with no memory_order* specification which is massive slower (100 cycles more more vs just 1 or 2 for the bare MOV).

    func_slow():
        movl    $1, -24(%rsp)
        mfence
        ret
    

    See here

    (Interestingly on Intel the use of memory_order_release and _acquire for this code results in the same assembly language. Intel guarantees that writes and reads happen in order when using the standard MOV instruction).