Я годами использовал 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, спасибо за ваши комментарии — я попробую все предложенное, когда доберусь до машины сегодня вечером.
Код от «Олаф Дитче»
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
Поскольку мне это интересно, я сам проверил его на 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
Я предполагаю, что это аппаратный вопрос.
Когда вы пишете volatile, вы говорите компилятору не принимать ничего о переменной, но, насколько я понимаю, аппаратное обеспечение все равно будет воспринимать ее как нормальную переменную. Это означает, что переменная будет в кеше все время.
Когда вы используете Atomic, вы используете специальные аппаратные инструкции, которые, вероятно, означают, что переменная извлекается из основной памяти каждый раз, когда она используется.
Разница во времени согласуется с этим объяснением.