같은 것을 알고 싶었 기 때문에 측정했습니다. 내 상자 (3.612361GHz의 AMD FX (TM) -8150 8 코어 프로세서)에서 자체 캐시 라인에 있고 이미 캐시 된 잠금 해제 된 뮤텍스를 잠금 및 잠금 해제하려면 47 클럭 (13ns)이 걸립니다.
두 개의 코어 (CPU # 0 및 # 1 사용) 간의 동기화로 인해 두 스레드에서 102 ns마다 한 번만 잠금 / 잠금 해제 쌍을 호출 할 수 있었으므로 51 ns마다 한 번씩 호출하면 약 38 시간이 걸립니다. 스레드가 잠금을 해제 한 후 복구하려면 ns 다음 스레드가 다시 잠금을 해제합니다.
이것을 조사하는 데 사용한 프로그램은 여기에서 찾을 수 있습니다.
https://github.com/CarloWood/ai-statefultask-testsuite/blob/b69b112e2e91d35b56a39f41809d3e3de2f9e4b8/src/mutex_test.cxx
내 상자에 특정한 하드 코딩 된 값 (xrange, yrange 및 rdtsc 오버 헤드)이 있으므로 작동하기 전에 실험해야합니다.
해당 상태에서 생성되는 그래프는 다음과 같습니다.
다음 코드에서 벤치 마크 실행 결과를 보여줍니다.
uint64_t do_Ndec(int thread, int loop_count)
{
uint64_t start;
uint64_t end;
int __d0;
asm volatile ("rdtsc\n\tshl $32, %%rdx\n\tor %%rdx, %0" : "=a" (start) : : "%rdx");
mutex.lock();
mutex.unlock();
asm volatile ("rdtsc\n\tshl $32, %%rdx\n\tor %%rdx, %0" : "=a" (end) : : "%rdx");
asm volatile ("\n1:\n\tdecl %%ecx\n\tjnz 1b" : "=c" (__d0) : "c" (loop_count - thread) : "cc");
return end - start;
}
두 개의 rdtsc 호출은 '뮤텍스'를 잠그고 잠금을 해제하는 데 걸리는 클럭 수를 측정합니다 (내 상자의 rdtsc 호출에 대한 39 클럭 오버 헤드). 세 번째 asm은 지연 루프입니다. 지연 루프의 크기는 스레드 0의 경우보다 스레드 1의 경우 1보다 작으므로 스레드 1이 약간 더 빠릅니다.
위 함수는 크기가 100,000 인 타이트한 루프에서 호출됩니다. 스레드 1에서는이 기능이 약간 빠르지 만 뮤텍스 호출로 인해 두 루프가 동기화됩니다. 이것은 잠금 / 잠금 해제 쌍에 대해 측정 된 클록 수가 스레드 1에 대해 약간 더 크다는 사실에서 그래프에서 볼 수 있습니다. 그 아래 루프에서 더 짧은 지연을 설명합니다.
위의 그래프에서 오른쪽 아래 지점은 지연 loop_count가 150 인 측정 값이며, 아래에서 왼쪽을 향한 지점을 따라 loop_count는 각 측정마다 하나씩 감소합니다. 77이되면 두 스레드에서 102 ns마다 함수가 호출됩니다. 그에 따라 loop_count가 더 줄어들면 더 이상 스레드를 동기화 할 수 없으며 뮤텍스가 실제로 대부분 잠기기 시작하여 잠금 / 잠금 해제에 필요한 클럭 수가 증가합니다. 또한 함수 호출의 평균 시간은 이로 인해 증가합니다. 플롯 포인트가 다시 오른쪽으로 올라갑니다.
이것으로 우리는 50 ns마다 뮤텍스를 잠금 및 잠금 해제하는 것이 내 상자에 문제가 아니라는 결론을 내릴 수 있습니다.
결론적으로 OP의 질문에 대한 답변은 더 많은 뮤텍스를 추가하는 것이 경합이 적은 한 더 좋습니다.
뮤텍스를 가능한 짧게 잠그십시오. 루프 외부에 배치하는 유일한 이유는 루프가 100ns마다 한 번 (또는 동시에 해당 루프를 50ns로 실행하려는 스레드 수) 또는 13ns 번 반복되는 경우입니다. 루프 크기는 경합에 의한 지연보다 지연됩니다.
편집 : 나는 지금 그 주제에 대해 더 많은 지식을 얻었으며 여기에 제시 한 결론을 의심하기 시작합니다. 우선, CPU 0과 1은 하이퍼 스레딩 된 것으로 판명되었습니다. AMD가 8 개의 실제 코어를 가지고 있다고 주장하지만, 다른 두 코어 사이의 지연이 훨씬 더 크기 때문에 (즉, 2와 3, 4와 5, 6과 7과 같이 0과 1이 쌍을 이룹니다.) ). 둘째, std :: mutex는 뮤텍스에 대한 잠금을 즉시 얻지 못할 때 실제로 시스템 호출을 수행하기 전에 잠금을 약간 회전시키는 방식으로 구현됩니다 (의심 할 정도로 느릴 것입니다). 제가 여기서 측정 한 것은 절대적으로 가장 이상적인 위치입니다. 실제로 잠금 및 잠금 해제는 잠금 / 잠금 해제마다 시간이 훨씬 더 걸릴 수 있습니다.
결론적으로, 뮤텍스는 원자로 구현됩니다. 코어 간 원자를 동기화하려면 내부 버스를 잠 가야하며 이는 수백 클록 사이클 동안 해당 캐시 라인을 고정시킵니다. 잠금을 확보 할 수없는 경우 스레드를 휴면 상태로 만들기 위해 시스템 호출을 수행해야합니다. 시스템 호출은 10 mircoseconds 정도입니다. 일반적으로 스레드가 어쨌든 휴면 상태이기 때문에 실제로 문제가되지는 않습니다.하지만 스레드가 정상적으로 회전하는 시간 동안 잠금을 얻을 수없고 시스템 호출도 마찬가지이므로 높은 경합에 문제가있을 수 있습니다. 잠시 후 자물쇠를 가져 가십시오. 예를 들어, 여러 스레드가 꽉 루프에서 뮤텍스를 잠그고 잠금을 해제하고 각각이 1 마이크로 초 정도 잠금을 유지하는 경우, 그들은 끊임없이 잠들고 다시 깨어났다는 사실에 의해 엄청나게 느려질 수 있습니다. 또한 스레드가 잠 들어 다른 스레드가 깨어 나면 해당 스레드는 시스템 호출을 수행해야하며 ~ 10 마이크로 초 지연됩니다. 이 지연은 다른 스레드가 커널에서 해당 뮤텍스를 기다리고있을 때 뮤텍스를 잠금 해제하는 동안 발생합니다 (회전이 너무 오래 걸린 후).