Bagaimana std :: lock_guard lebih cepat dari std :: mutex :: lock ()?

9

Saya berdebat dengan seorang kolega, tentang lock_guard, dan dia mengusulkan bahwa lock_guard mungkin lebih lambat daripada mutex :: lock () / mutex :: unlock () karena biaya instantiate dan unistantiate class lock_guard.

Lalu saya membuat tes sederhana ini dan, anehnya, versi dengan lock_guard hampir dua kali lebih cepat daripada versi dengan 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;
}

Hasil di mesin saya:

Take: 41 ms
Take: 22 ms

Adakah yang bisa menjelaskan mengapa dan bagaimana hal ini bisa terjadi?

Eduardo Fernandes
sumber
2
dan berapa kali Anda melakukan pengukuran?
artm
7
Silakan kirim flag kompiler Anda ... Pembandingan akan tergantung pada tingkat optimisasi ...
Macmade
10
Kiat Pro: Saat melakukan pengukuran seperti ini, tukar pesanan untuk memastikan itu bukan hanya data / instruksi dingin yang menyebabkan masalah: coliru.stacked-crooked.com/a/81f75a1ab52cb1cc
NathanOliver
2
Hal lain yang membantu ketika melakukan pengukuran seperti ini: letakkan semuanya dalam satu lingkaran yang lebih besar, sehingga Anda menjalankan set pengukuran keseluruhan, katakanlah, 20 kali setiap kali berjalan. Biasanya pengukuran nanti akan menjadi yang benar-benar bermakna, karena pada saat itu cache telah menetap ke perilaku apa pun yang mungkin terjadi dalam jangka panjang.
Mark Phaedrus
2
Meskipun std::lock_guardsedikit lebih lambat, kecuali Anda dapat membuktikan bahwa itu penting dalam hal kinerja, kenaikan kecepatan tidak akan membatalkan manfaat lain dari penggunaan std::lock_guard(terutama RAII). Jika g++ada sesuatu yang bisa melempar atau apapun yang mungkin berubah menjadi sesuatu yang berpotensi lebih rumit di masa depan Anda hampir harus menggunakan semacam objek untuk memiliki kunci.
François Andrieux

Jawaban:

6

Rilis build menghasilkan hasil yang sama untuk kedua versi.

The DEBUGbuild menunjukkan ~ 33% lebih lama waktu untuk func2; perbedaan yang saya lihat di pembongkaran yang func2menggunakan __security_cookiedan memanggil @_RTC_CheckStackVars@8.

Apakah Anda menghitung waktu DEBUG?

EDIT: Selain itu, ketika melihat RELEASEpembongkaran, saya perhatikan bahwa mutexmetode disimpan dalam dua pendaftar:

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

dan dipanggil dengan cara yang sama dari keduanya func1dan func2:

010F1067  call        edi  
....
010F107F  call        ebx  
Vlad Feinstein
sumber