Search code examples
c++multithreadingc++11concurrencyc++-chrono

How long does it take for one thread to notify another thread?


I want to measure how long it takes for a condition variable to notify another waiting thread. I wrote this example to try to capture the time difference:

bool ready{false};
mutex m;
condition_variable cv;
decltype(chrono::steady_clock::now()) t1;
decltype(chrono::steady_clock::now()) t2;

void notifying_thread() {
    {
        lock_guard<mutex> lg{m};
        ready = true;
    }
    t1 = chrono::steady_clock::now();
    cv.notify_one();
}

void waiting_thread() {
    unique_lock<mutex> ul{m};
    cv.wait(ul, [] { return ready; });
    t2 = chrono::steady_clock::now();
}

int main() {
    auto f1{async(notifying_thread)};
    auto f2{async(waiting_thread)};
    f1.get();
    f2.get();
    cout << chrono::duration_cast<chrono::microseconds>(t2 - t1).count() << " µs\n";
    return 0;
}

Did I do this right? Do I have my chrono::steady_clock::now()s in the right place? On average the time is ~25 µs, but sometimes it's even negative. How can it be negative? Shouldn't t1 always start before t2? Is the launch policy for async relevant?


Solution

  • Your are missing spurious wake ups in your reasoning. The std::condition_variable::wait overload with predicate is equivalent to

    while (!stop_waiting())
    {
        wait(lock);
    }
    

    wait(lock) can return even if the condition variable was not notified. Thats what the predicate is for. When there is spurious wake up you keep looping until the condition is fullfilled.

    Due to spurious wake up it can happen that wait(lock) unblocks before the other thread called notify_one but after it has set ready = true. In that case t2 can be an earlier time than t1.

    Moreover, as DanielLangr pointed out in a comment, when the condition is already fullfilled before even entering the above loop then wait(lock) is never called. Also in this case, t2 can be earlier than t1.

    Moreover, the two threads do not start immediately. If notifying_thread starts and waiting_thread starts only some time later, then you are basically measuring that time, not the time it takes to notify the condition variable.