Почему std :: atomic & lt; bool & gt; намного медленнее чем изменчивый бул?

Я годами использовал volatile bool для контроля исполнения потоков, и он работал нормально

// in my class declaration
volatile bool stop_;

-----------------

// In the thread function
while (!stop_)
{
do_things();
}

Теперь, так как в C ++ 11 добавлена ​​поддержка атомарных операций, я решил попробовать это вместо

// in my class declaration
std::atomic<bool> stop_;

-----------------

// In the thread function
while (!stop_)
{
do_things();
}

Но это на несколько порядков медленнее, чем volatile bool!

Простой тестовый пример, который я написал, занимает около 1 секунды. volatile bool подход. С std::atomic<bool> Однако я ждал около 10 минут и сдался!

Я пытался использовать memory_order_relaxed флаг с load а также store к тому же эффекту.

Моя платформа:
Windows 7 64 бит
MinGW gcc 4.6.x

Что я делаю не так?

UPD

Да, я знаю, что volatile не делает переменный поток безопасным. Мой вопрос не о летучих, а о том, почему атомная смехотворно медленная.

UPD2
@all, спасибо за ваши комментарии — я попробую все предложенное, когда доберусь до машины сегодня вечером.

33

Решение

Код от «Олаф Дитче»

 USE ATOMIC
real   0m1.958s
user   0m1.957s
sys    0m0.000s

USE VOLATILE
real   0m1.966s
user   0m1.953s
sys    0m0.010s

ЕСЛИ ВЫ ИСПОЛЬЗУЕТЕ GCC SMALLER 4.7

http://gcc.gnu.org/gcc-4.7/changes.html

Добавлена ​​поддержка атомарных операций, определяющих модель памяти C ++ 11 / C11. Эти новые __atomic подпрограммы заменяют существующие встроенные подпрограммы __sync.

Атомная поддержка также доступна для блоков памяти. Инструкции без блокировки будут использоваться, если блок памяти имеет тот же размер и выравнивание, что и поддерживаемый целочисленный тип. Атомарные операции, которые не имеют поддержки без блокировки, остаются как вызовы функций. Набор функций библиотеки доступен на вики-странице GCC в разделе «Внешняя библиотека Atomics».

Так что да .. единственное решение — это обновить до GCC 4.7

30

Другие решения

Поскольку мне это интересно, я сам проверил его на Ubuntu 12.04, AMD 2.3 GHz, gcc 4.6.3.

#if 1
#include <atomic>
std::atomic<bool> stop_(false);
#else
volatile bool stop_ = false;
#endif

int main(int argc, char **argv)
{
long n = 1000000000;
while (!stop_) {
if (--n < 0)
stop_ = true;
}

return 0;
}

Составлено с g++ -g -std=c++0x -O3 a.cpp

Хотя, такой же вывод, как @aleguna:

  • просто bool:

    реальный 0m0.004s
    пользователь 0m0.000s
    sys 0m0.004s

  • volatile bool:

    $ time ./a.out
    реальный 0m1.413s
    пользователь 0m1.368s
    sys 0m0.008s

  • std::atomic<bool>:

    $ time ./a.out
    реальный 0m32.550s
    пользователь 0m32.466s
    sys 0m0.008s

  • std::atomic<int>:

    $ time ./a.out
    реальный 0m32.091s
    пользователь 0m31.958s
    sys 0m0.012s

12

Я предполагаю, что это аппаратный вопрос.
Когда вы пишете volatile, вы говорите компилятору не принимать ничего о переменной, но, насколько я понимаю, аппаратное обеспечение все равно будет воспринимать ее как нормальную переменную. Это означает, что переменная будет в кеше все время.
Когда вы используете Atomic, вы используете специальные аппаратные инструкции, которые, вероятно, означают, что переменная извлекается из основной памяти каждый раз, когда она используется.
Разница во времени согласуется с этим объяснением.

2