web-dev-qa-db-fra.com

Pourquoi std :: atomic <bool> est-il beaucoup plus lent que bool volatil?

J'utilise bool volatile depuis des années pour le contrôle de l'exécution des threads et cela a bien fonctionné

// in my class declaration
volatile bool stop_;

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

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

Maintenant, comme c ++ 11 a ajouté la prise en charge des opérations atomiques, j'ai décidé d'essayer cela à la place

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

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

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

Mais c'est plusieurs ordres de grandeur plus lent que le volatile bool!

Le scénario de test simple que j'ai écrit prend environ 1 seconde pour terminer avec volatile bool approche. Avec std::atomic<bool> cependant j'attends depuis environ 10 minutes et j'ai abandonné!

J'ai essayé d'utiliser memory_order_relaxed drapeau avec load et store au même effet.

Ma plate-forme: Windows 7 64 bits MinGW gcc 4.6.x

Qu'est-ce que je fais mal?

UPD

Oui, je sais que volatile ne rend pas un thread variable sûr. Ma question n'est pas volatile, c'est pourquoi l'atomique est ridiculement lent.

PD2 @all, merci pour vos commentaires - je vais essayer toutes les suggestions quand j'arriverai à ma machine ce soir.

34
user1773602

Code de "Olaf Dietsche"

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

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

SI VOUS UTILISEZ GCC SMALLER 4.7

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

La prise en charge des opérations atomiques spécifiant le modèle de mémoire C++ 11/C11 a été ajoutée. Ces nouvelles routines __atomic remplacent les routines intégrées __sync existantes.

La prise en charge atomique est également disponible pour les blocs de mémoire. Des instructions sans verrouillage seront utilisées si un bloc de mémoire a la même taille et le même alignement qu'un type d'entier pris en charge. Les opérations atomiques qui ne prennent pas en charge le verrouillage sont laissées en tant qu'appels de fonction. Un ensemble de fonctions de bibliothèque est disponible sur le wiki atomique de GCC dans la section "External Atomics Library".

Alors oui ... la seule solution est de passer à GCC 4.7

31
KoKuToru

Comme je suis curieux à ce sujet, je l'ai testé moi-même sur 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;
}

Compilé avec g++ -g -std=c++0x -O3 a.cpp

Bien, même conclusion que @aleguna:

  • juste bool:

    réel 0m0.004s
    utilisateur 0m0.000s
    sys 0m0.004s

  • volatile bool:

    $ time ./a.out
    réel 0m1.413s
    utilisateur 0m1.368s
    sys 0m0.008s

  • std::atomic<bool>:

    $ time ./a.out
    réel 0m32.550s
    utilisateur 0m32.466s
    sys 0m0.008s

  • std::atomic<int>:

    $ time ./a.out
    réel 0m32.091s
    utilisateur 0m31.958s
    sys 0m0.012s

12
Olaf Dietsche

Je suppose que c'est une question de matériel. Lorsque vous écrivez volatile, vous dites au compilateur de ne rien supposer de la variable, mais si je comprends bien, le matériel la traitera toujours comme une variable normale. Cela signifie que la variable sera dans le cache tout le temps. Lorsque vous utilisez atomic, vous utilisez des instructions matérielles spéciales, ce qui signifie probablement que la variable est extraite de la mémoire principale chaque fois qu'elle est utilisée. La différence de timing est cohérente avec cette explication.

2
Olle Lindeberg