Comment std :: lock_guard peut-il être plus rapide que std :: mutex :: lock ()?


9

Je discutais avec un collègue à propos de lock_guard, et il a proposé que lock_guard soit probablement plus lent que mutex :: lock () / mutex :: unlock () en raison du coût d'instanciation et de non-différenciation de la classe lock_guard.

Ensuite, j'ai créé ce test simple et, étonnamment, la version avec lock_guard est presque deux fois plus rapide que la version avec mutex :: lock () / mutex :: unlock ()

#include <iostream>
#include <mutex>
#include <chrono>

std::mutex m;
int g = 0;

void func1()
{
    m.lock();
    g++;
    m.unlock();
}

void func2()
{
    std::lock_guard<std::mutex> lock(m);
    g++;
}

int main()
{
    auto t = std::chrono::system_clock::now();
    for (int i = 0; i < 1000000; i++)
    {
        func1();
    }

    std::cout << "Take: " << std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - t).count() << " ms" << std::endl;

    t = std::chrono::system_clock::now();
    for (int i = 0; i < 1000000; i++)
    {
        func2();
    }

    std::cout << "Take: " << std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - t).count() << " ms" << std::endl;

    return 0;
}

Les résultats sur ma machine:

Take: 41 ms
Take: 22 ms

Quelqu'un peut-il clarifier pourquoi et comment cela peut être?


2
et combien de fois avez-vous pris vos mesures?
artm

7
Veuillez poster vos drapeaux de compilateur ... L'analyse comparative dépendra du niveau d'optimisation ...
Macmade

10
Conseil de pro: lorsque vous effectuez des mesures comme celle-ci, permutez la commande pour vous assurer qu'il ne s'agit pas uniquement de données / instructions froides à l'origine du problème: coliru.stacked-crooked.com/a/81f75a1ab52cb1cc
NathanOliver

2
Une autre chose qui est utile lorsque vous effectuez des mesures comme celle-ci: mettez le tout dans une boucle plus grande, de sorte que vous exécutez l'ensemble de mesure entier, disons, 20 fois à chaque exécution. Habituellement, les mesures ultérieures seront celles qui sont réellement significatives, car d'ici là, le cache s'est installé dans le comportement qu'il est susceptible d'avoir à long terme.
Mark Phaedrus

2
Même s'il std::lock_guardétait un peu plus lent, à moins que vous ne puissiez prouver qu'il est important en termes de performances, ce gain de vitesse n'invalidera pas les autres avantages de l'utilisation std::lock_guard(principalement RAII). Si g++quelque chose qui peut jeter ou tout ce qui pourrait se transformer en quelque chose de potentiellement plus compliqué à l'avenir vous presque devez utiliser une sorte d'objet à posséder la serrure.
François Andrieux

Réponses:


6

La version finale produit le même résultat pour les deux versions.

La DEBUGconstruction affiche un temps de ~ 33% plus long pour func2; la différence que je vois dans le démontage qui func2utilise __security_cookieet invoque @_RTC_CheckStackVars@8.

Vous chronométrez DEBUG?

EDIT: De plus, en regardant le RELEASEdémontage, j'ai remarqué que les mutexméthodes étaient enregistrées dans deux registres:

010F104E  mov         edi,dword ptr [__imp___Mtx_lock (010F3060h)]  
010F1054  xor         esi,esi  
010F1056  mov         ebx,dword ptr [__imp___Mtx_unlock (010F3054h)]  

et appelé de la même façon des deux func1et func2:

010F1067  call        edi  
....
010F107F  call        ebx  
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.