Тривиальный пример переупорядочения операций с памятью - PullRequest
1 голос
/ 06 ноября 2019

Я пытался написать некоторый код, который позволил бы мне наблюдать за переупорядочением операций с памятью.

В следующем примере я ожидал, что в некоторых выполнениях set_values ​​() порядок назначения значений может измениться. В частности, уведомление = 1 может появляться перед остальными операциями, но не может произойти даже после нескольких итераций. Я скомпилировал код с оптимизацией -O3. Вот материал YouTube, на который я ссылаюсь: https://youtu.be/qlkMbxUbKfw?t=200

int a{0};
int b{0};
int c{0};
int notification{0};

void set_values()
{
    a = 1;
    b = 2;
    c = 3;
    notification = 1;
}

void calculate()
{
   while(notification != 1);
   a += b + c;
}

void reset()
{
    a = 0;
    b = 0;
    c = 0;
    notification = 0;
}

int main()
{
    a=6; //just to allow first iteration

    for(int i = 0 ; a == 6 ; i++)
    {
        reset();
        std::thread t1(calculate);
        std::thread t2(set_values);

        t1.join();
        t2.join();
        std::cout << "Iteration: " << i << ", " "a = " << a << std::endl;
    }
    return 0;
}

Теперь программа застряла в бесконечном цикле. Я ожидаю, что в некоторых итерациях порядок инструкций в функции set_values ​​() может измениться (из-за оптимизации на кэш-памяти). Например, уведомление = 1 будет выполнено до c = 3, что вызовет выполнение функции Calculate () и даст == 3, что удовлетворяет условию завершения цикла и подтверждения переупорядочения

Или, может быть, кто-то может предоставить другойтривиальный пример кода, который помогает наблюдать за переупорядочением операций с памятью?

Ответы [ 2 ]

0 голосов
/ 06 ноября 2019

Ваш компилятор оптимизирует неожиданным образом, но ему разрешено это делать, потому что вы нарушаете фундаментальное правило модели памяти C ++.

Вы не можете получить доступ к памяти из нескольких потоков, если хотя бы один из них является записывающим устройством.

Для синхронизации используйте либо std:mutex, либо используйте std:atomic<int> вместо intдля ваших переменных

0 голосов
/ 06 ноября 2019

Компилятор действительно может изменить порядок ваших назначений в функции set_values. Однако это не требуется. В этом случае нет необходимости переупорядочивать что-либо, поскольку вы назначаете константы для всех четырех переменных.

Теперь программа застряла в бесконечном цикле.

Этовероятно, потому что while(notification != 1); будет оптимизирован до бесконечного цикла.

Немного поработав, мы сможем найти способ заставить компилятор переупорядочить присваивание notify = 1 перед другими операторами, см. https://godbolt.org/z/GY-pAw. Обратите внимание, что программа читает x из стандартного ввода, это делается для того, чтобы заставить компилятор читать из области памяти.

Я также сделал переменную notification volatile, чтобы while(notification != 1); не оптимизировался.

Вы можете попробовать этот пример на своей машине, я смогчтобы постоянно проваливать утверждение, используя g ++ 9.2 и -O3, работающие на процессоре Intel Sandy Bridge.

Имейте в виду, что сам процессор может переупорядочивать инструкции, если они независимы друг от друга, см. https://en.wikipedia.org/wiki/Out-of-order_execution. Это, однако, немного сложно для тестирования и воспроизведения последовательно.

...