Почему memory_order_relaxed и memory_order_seq_cst не имеют значения? - PullRequest
4 голосов
/ 27 марта 2012

Я играл с одним из примеров в C ++ Concurrency in Action, который использует std::memory_order_relaxed для чтения и записи 3 атомарных переменных из 5 разных потоков.Пример программы следующий:

#include <thread>
#include <atomic>
#include <iostream>

std::atomic<int> x(0);
std::atomic<int> y(0);
std::atomic<int> z(0);
std::atomic<bool> go(false);

const unsigned int loop_count = 10;

struct read_values
{
   int x;
   int y;
   int z;
};

read_values values1[loop_count];
read_values values2[loop_count];
read_values values3[loop_count];
read_values values4[loop_count];
read_values values5[loop_count];

void increment( std::atomic<int>* v, read_values* values )
{
    while (!go)
       std::this_thread::yield();

    for (unsigned i=0;i<loop_count;++i)
    {
       values[i].x=x.load( std::memory_order_relaxed );
       values[i].y=y.load( std::memory_order_relaxed );
       values[i].z=z.load( std::memory_order_relaxed );
       v->store( i+1, std::memory_order_relaxed );
       std::this_thread::yield();
    }
}

void read_vals( read_values* values )
{

   while (!go)
      std::this_thread::yield();

   for (unsigned i=0;i<loop_count;++i)
   {
      values[i].x=x.load( std::memory_order_relaxed );
      values[i].y=y.load( std::memory_order_relaxed );
      values[i].z=z.load( std::memory_order_relaxed );
      std::this_thread::yield();
   }
}

void print( read_values* values )
{
   for (unsigned i=0;i<loop_count;++i)
   {
      if (i)
         std::cout << ",";
      std::cout << "(" << values[i].x <<","
                       << values[i].y <<","
                       << values[i].z <<")";
   }
   std::cout << std::endl;
}

int main()
{
   std::thread t1( increment, &x, values1);
   std::thread t2( increment, &y, values2);
   std::thread t3( increment, &z, values3);
   std::thread t4( read_vals, values4);
   std::thread t5( read_vals, values5);

   go = true;

   t5.join();
   t4.join();
   t3.join();
   t2.join();
   t1.join();

   print( values1 );
   print( values2 );
   print( values3 );
   print( values4 );
   print( values5 );

   return 0;
}

Каждый раз, когда я запускаю программу, я получаю точно такой же вывод:

(0,10,10),(1,10,10),(2,10,10),(3,10,10),(4,10,10),(5,10,10),(6,10,10),(7,10,10),(8,10,10),(9,10,10)
(0,0,1),(0,1,2),(0,2,3),(0,3,4),(0,4,5),(0,5,6),(0,6,7),(0,7,8),(0,8,9),(0,9,10)
(0,0,0),(0,1,1),(0,2,2),(0,3,3),(0,4,4),(0,5,5),(0,6,6),(0,7,7),(0,8,8),(0,9,9)
(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0)
(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0),(0,0,0)

Если я изменяю с std::memory_order_relaxed на std::memory_order_seq_cst,Программа выдает точно такой же вывод!

Я бы ожидал отличного вывода от 2-х версий программы.Почему нет разницы между выходными данными для std::memory_order_relaxed и std::memory_order_seq_cst?

Почему std::memory_order_relaxed всегда дает одинаковые результаты при каждом запуске программы?

Я использую: - 32-битная Ubuntu установлена ​​как виртуальная машина (под VMWare) - Четырехъядерный процессор INtel - GCC 4.6.1-9

Код скомпилирован с: g ++ --std = c ++ 0x -g mem-order-relaxed.cpp -o relaxed -pthread

Обратите внимание, что -pthread необходим, в противном случае выдается следующая ошибка: завершается вызов после выброса экземпляра 'std :: system_error' what (): Операция notразрешено

Поведение, которое я наблюдаю, из-за отсутствия поддержки с GCC или в результате работы под VMWare?

Ответы [ 3 ]

6 голосов
/ 27 марта 2012

Сколько процессорных ядер вы присвоили виртуальной машине? Назначьте несколько ядер виртуальной машине, чтобы она могла использовать преимущества параллелизма.

2 голосов
/ 01 сентября 2013

Ваше использование yield приводит к тому, что производительность вашей программы в большей степени зависит от планировщика вашей платформы, чем от чего-либо другого.

С другой стороны, memory_order_relaxed не требует, чтобы компилятор переупорядочивал атомы, он просто позволяет компиляторуСделай так.Если компилятор доволен порядком, который он получает с memory_order_seq_cst, то он может фактически выдать точно такой же байт-код!Это особенно верно для x86, потому что набор инструкций уже предлагает так много гарантий упорядочения, так что это не такой большой скачок, чтобы прийти к memory_order_seq_cst.

0 голосов
/ 03 сентября 2013

Многие версии GCC игнорируют предоставленный вами порядок памяти и заменяют его последовательной последовательностью. Вы можете увидеть это в заголовочных файлах. Надеюсь, у них со временем будет лучшая реализация? Вы можете поиграть с эффектами relaxed против seq_cst, используя CDSChecker ...

...