Пожалуйста, смотрите мои обновления в конце ответа, ситуация резко изменилась с Visual Studio 2015. Оригинальный ответ ниже.
Я сделал очень простой тест и согласно моим измерениям std::mutex
примерно в 50-70 раз медленнее, чем CRITICAL_SECTION
.
std::mutex: 18140574us
CRITICAL_SECTION: 296874us
Редактировать: Оказалось, что после еще нескольких тестов это зависит от количества потоков (перегрузок) и количества ядер ЦП. Как правило, std::mutex
медленнее, но насколько это зависит от использования. Ниже приведены обновленные результаты тестирования (протестировано на MacBook Pro с Core i5-4258U, Windows 10, Bootcamp):
Iterations: 1000000
Thread count: 1
std::mutex: 78132us
CRITICAL_SECTION: 31252us
Thread count: 2
std::mutex: 687538us
CRITICAL_SECTION: 140648us
Thread count: 4
std::mutex: 1031277us
CRITICAL_SECTION: 703180us
Thread count: 8
std::mutex: 86779418us
CRITICAL_SECTION: 1634123us
Thread count: 16
std::mutex: 172916124us
CRITICAL_SECTION: 3390895us
Ниже приведен код, который произвел этот вывод. Скомпилировано с Visual Studio 2012, настройки проекта по умолчанию, конфигурация релиза Win32. Обратите внимание, что этот тест может быть не совсем правильным, но он заставил меня дважды подумать, прежде чем переключать код с использования CRITICAL_SECTION
на std::mutex
.
#include "stdafx.h"
#include <Windows.h>
#include <mutex>
#include <thread>
#include <vector>
#include <chrono>
#include <iostream>
const int g_cRepeatCount = 1000000;
const int g_cThreadCount = 16;
double g_shmem = 8;
std::mutex g_mutex;
CRITICAL_SECTION g_critSec;
void sharedFunc( int i )
{
if ( i % 2 == 0 )
g_shmem = sqrt(g_shmem);
else
g_shmem *= g_shmem;
}
void threadFuncCritSec() {
for ( int i = 0; i < g_cRepeatCount; ++i ) {
EnterCriticalSection( &g_critSec );
sharedFunc(i);
LeaveCriticalSection( &g_critSec );
}
}
void threadFuncMutex() {
for ( int i = 0; i < g_cRepeatCount; ++i ) {
g_mutex.lock();
sharedFunc(i);
g_mutex.unlock();
}
}
void testRound(int threadCount)
{
std::vector<std::thread> threads;
auto startMutex = std::chrono::high_resolution_clock::now();
for (int i = 0; i<threadCount; ++i)
threads.push_back(std::thread( threadFuncMutex ));
for ( std::thread& thd : threads )
thd.join();
auto endMutex = std::chrono::high_resolution_clock::now();
std::cout << "std::mutex: ";
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(endMutex - startMutex).count();
std::cout << "us \n\r";
threads.clear();
auto startCritSec = std::chrono::high_resolution_clock::now();
for (int i = 0; i<threadCount; ++i)
threads.push_back(std::thread( threadFuncCritSec ));
for ( std::thread& thd : threads )
thd.join();
auto endCritSec = std::chrono::high_resolution_clock::now();
std::cout << "CRITICAL_SECTION: ";
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(endCritSec - startCritSec).count();
std::cout << "us \n\r";
}
int _tmain(int argc, _TCHAR* argv[]) {
InitializeCriticalSection( &g_critSec );
std::cout << "Iterations: " << g_cRepeatCount << "\n\r";
for (int i = 1; i <= g_cThreadCount; i = i*2) {
std::cout << "Thread count: " << i << "\n\r";
testRound(i);
Sleep(1000);
}
DeleteCriticalSection( &g_critSec );
// Added 10/27/2017 to try to prevent the compiler to completely
// optimize out the code around g_shmem if it wouldn't be used anywhere.
std::cout << "Shared variable value: " << g_shmem << std::endl;
getchar();
return 0;
}
Обновление от 27.10.2017 (1) :
Некоторые ответы предполагают, что это нереалистичный тест или не представляет сценарий «реального мира». Это правда, этот тест пытается измерить издержки из std::mutex
, он не пытается доказать, что разница незначительна для 99% приложений.
Обновление от 27.10.2017 (2) :
Похоже, что ситуация изменилась в пользу std::mutex
со времен Visual Studio 2015 (VC140). Я использовал VS2017 IDE, точно такой же код, как и выше, конфигурацию выпуска x64, оптимизации отключены, и я просто переключал «Набор инструментов платформы» для каждого теста. Результаты очень удивительны, и мне действительно любопытно, что зависло в VC140.