Come può std :: lock_guard essere più veloce di std :: mutex :: lock ()?


9

Stavo discutendo con un collega di lock_guard e mi ha proposto che lock_guard è probabilmente più lento di mutex :: lock () / mutex :: unlock () a causa del costo di istanza e non istituzione della classe lock_guard.

Poi ho creato questo semplice test e, sorprendentemente, la versione con lock_guard è quasi due volte più veloce della versione con 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;
}

I risultati sulla mia macchina:

Take: 41 ms
Take: 22 ms

Qualcuno può chiarire perché e come può essere?


2
e quante volte hai preso le tue misure?
artm


10
Suggerimento pro: quando si eseguono misurazioni come questa, scambiare l'ordine per assicurarsi che non siano solo i dati / istruzioni a freddo a causare il problema: coliru.stacked-crooked.com/a/81f75a1ab52cb1cc
NathanOliver

2
Un'altra cosa che è utile quando si eseguono misurazioni come questa: mettere il tutto in un ciclo più ampio, in modo da eseguire l'intero set di misurazioni, diciamo, 20 volte ogni ciclo. Di solito le misurazioni successive saranno quelle effettivamente significative, perché a quel punto la cache si è assestata su qualunque comportamento possa avere a lungo termine.
Mark Phaedrus,

2
Anche se è std::lock_guardstato un po 'più lento, a meno che tu non possa dimostrare che è importante in termini di prestazioni, quel guadagno di velocità non invaliderà gli altri vantaggi dell'uso std::lock_guard(principalmente RAII). Se g++è tutto ciò che può lanciare o qualsiasi cosa che potrebbe cambiare in qualcosa di potenzialmente più complicato in futuro quasi necessario utilizzare una sorta di oggetto da possedere la serratura.
François Andrieux il

Risposte:


6

La build di rilascio produce lo stesso risultato per entrambe le versioni.

La DEBUGbuild mostra ~ 33% di tempo in più per func2; la differenza che vedo nello smontaggio che func2usa __security_cookiee invoca @_RTC_CheckStackVars@8.

Stai pianificando DEBUG?

EDIT: Inoltre, osservando lo RELEASEsmontaggio, ho notato che i mutexmetodi sono stati salvati in due registri:

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

e chiamato allo stesso modo da entrambi func1e func2:

010F1067  call        edi  
....
010F107F  call        ebx  
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.