Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the compiler allowed to optimize out this busy waiting loop?

#include <iostream>
#include <thread>
#include <mutex>

int main()
{
    std::atomic<bool> ready = false;

    std::thread threadB = std::thread([&]() {
        while (!ready) {}

        printf("Hello from B\n");
    });

    std::this_thread::sleep_for(std::chrono::seconds(1));

    printf("Hello from A\n");

    ready = true;

    threadB.join();

    printf("Hello again from A\n");
}

This is an example from the CppCon talk https://www.youtube.com/watch?v=F6Ipn7gCOsY&ab_channel=CppCon (min 17)

The objective is to first print Hello from A then allow threadB to start. It is clear that busy waiting should be avoided because it uses a lot of CPU.

The author said that the while (!ready) {} loop can be optimized (by putting the value of ready into a register) by the compiler because the compiler sees that threadB never sleeps so ready could never be changed. But even if the thread never sleeps another thread could still change the value, right? There is no data race because ready is atomic. The author states that this code is UB. Can somebody explain why the compiler is allowed to do such an optimization?

like image 620
Ashot Avatar asked Aug 28 '21 20:08

Ashot


People also ask

What is LoopLoop optimization?

Loop Optimization is the process of increasing execution speed and reducing the overheads associated with loops. It plays an important role in improving cache performance and making effective use of parallel processing capabilities.

What is busy waiting in Linux?

Busy waiting, also known as spinning, or busy looping is a process synchronization technique in which a process/task waits and constantly checks for a condition to be satisfied before proceeding with its execution.

What are some of the most effective compiler optimizations?

Many optimizations that operate on abstract programming concepts (loops, objects, structures) are independent of the machine targeted by the compiler, but many of the most effective optimizations are those that best exploit special features of the target platform.

What is busy looping in operating system?

Busy looping is usually used to achieve mutual exclusion in operating systems. Mutual exclusion prevents processes from accessing a shared resource simultaneously. A process is granted exclusive control to resources in its critical section without interferences from other processes in mutual exclusion.


Video Answer


1 Answers

The author admits in one of the comments below the video that he was wrong:

I had thought so, but it appears I was wrong; the compiler cannot hoist the atomic read out of the loop. The advice at @17:54 is still correct — you should still be very careful and beware of situations where the compiler might reorder or coalesce or eliminate atomic accesses in general — but this particular while-loop is NOT actually such a situation. For some (mostly theoretical) examples of how a compiler might optimize atomic access patterns, see JF Bastien's N4455 "No Sane Compiler Would Optimize Atomics" http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4455.html

like image 67
danadam Avatar answered Oct 24 '22 00:10

danadam