문제

실제로 코드에서 스핀락을 사용하는 경우가 얼마나 자주 있습니까?사용 중 루프를 사용하는 것이 실제로 잠금 사용보다 성능이 뛰어난 상황을 접하는 것이 얼마나 일반적입니까?
개인적으로 스레드 안전성이 필요한 일종의 코드를 작성할 때 다양한 동기화 프리미티브를 사용하여 벤치마킹하는 경향이 있으며, 지금까지는 스핀록을 사용하는 것보다 잠금을 사용하는 것이 더 나은 성능을 제공하는 것 같습니다.실제로 잠금을 유지하는 시간이 아무리 짧더라도 스핀록을 사용할 때 받는 경합의 양은 잠금을 사용할 때 얻는 경합의 양보다 훨씬 큽니다(물론 다중 프로세서 시스템에서 테스트를 실행합니다).

나는 "낮은 수준" 코드에서 스핀락을 발견할 가능성이 더 높다는 것을 알고 있지만, 더 높은 수준의 프로그래밍에서도 이것이 유용하다고 생각하는지 알고 싶습니다.

도움이 되었습니까?

해결책

그것은 당신이하는 일에 달려 있습니다. 일반적인 응용 프로그램 코드에서는 스핀 락을 피하고 싶을 것입니다.

몇 가지 지침에 대해서만 잠금 장치를 잡고 대기 시간이 중요합니다. 스핀 락 매트는 잠금보다 더 나은 솔루션입니다. 그러나 이러한 경우는 드물다. 특히 C#이 일반적으로 사용되는 응용 분야에서는 드물다.

다른 팁

내 경험에 따르면 C#에서 "스핀 잠금"은 잠금을 사용하는 것보다 거의 항상 나빴습니다. 스핀 잠금이 잠금보다 성능이 뛰어난 경우는 거의 없습니다.

그러나 항상 그런 것은 아닙니다..NET 4에 시스템.스레딩.SpinLock 구조.이는 매우 짧은 시간 동안 잠금을 유지하고 반복적으로 잠금을 잡는 상황에서 이점을 제공합니다.MSDN 문서에서 병렬 프로그래밍을 위한 데이터 구조:

잠금 대기 시간이 짧을 것으로 예상되는 시나리오에서 SpinLock은 다른 형태의 잠금보다 더 나은 성능을 제공합니다.

스핀 잠금은 트리를 통한 잠금과 같은 작업을 수행하는 경우 다른 잠금 메커니즘보다 성능이 뛰어날 수 있습니다. 매우 짧은 시간 동안 각 노드에 잠금을 설정하는 경우 기존 잠금을 ​​수행할 수 있습니다.멀티스레드 장면 업데이트를 사용하는 렌더링 엔진에서 한 지점에서 이 문제를 만났습니다. 즉, Monitor.Enter를 사용한 잠금보다 성능이 뛰어난 스핀 잠금이 프로파일링되었습니다.

실시간 작업, 특히 장치 드라이버와의 경우 공정한 사용을 사용했습니다. 하드웨어 인터럽트에 묶인 세마포어와 같은 동기화 객체를 기다리는 (마지막으로 제가 이것을 할 때) 실제로 인터럽트가 발생하는 데 실제로 얼마나 오래 걸리더라도 적어도 20 마이크로 초를 씹습니다. 메모리 매핑 된 하드웨어 레지스터의 단일 점검과 RDTSC에 대한 점검 (기계를 잠그지 않도록 타임 아웃을 허용하기 위해)은 높은 Nannosecond 범위 (기본적으로 노이즈에서 다운)에 있습니다. 시간이 많이 걸리지 않는 하드웨어 수준의 핸드 쉐이킹의 경우 스핀 록을이기는 것은 정말 어렵습니다.

내 2C : 업데이트가 액세스 기준을 충족하면 좋은 스핀 록 후보자입니다.

  • 빠른, 즉 스핀 락을 획득하고, 업데이트를 수행하고, 스핀 락을 단일 스레드 Quanta에서 릴리스 할 시간이 있습니다.
  • 현지화 업데이트하는 모든 데이터는 이미로드 된 단일 페이지에 있으며, 스핀 록을 잡고있는 동안 TLB 미스를 원하지 않으며, 페이지 결함 스왑 읽기를 원하지 않습니다!
  • 원자 작업을 수행하기 위해 다른 잠금 장치가 필요하지 않습니다. 스핀 락 아래에서 자물쇠를 기다리지 마십시오.

생산 가능성이있는 모든 것을 위해서는 알려진 잠금 구조 (이벤트, 뮤트, 세마포어 등)를 사용해야합니다.

스핀 잠금 장치의 한 가지 사용 사례는 경합이 매우 낮지 만 많은 것을 가질 것입니다. 재귀 잠금을 지원할 필요가 없다면, 스핀 락을 단일 바이트로 구현할 수 있으며, 경합이 매우 낮 으면 CPU 사이클 폐기물이 무시할 수 있습니다.

실용적인 사용 사례의 경우, 종종 배열의 다른 요소에 대한 업데이트가 평행하게 안전하게 발생할 수있는 수천 개의 요소가 있습니다. 동시에 동일한 요소를 업데이트하려는 두 스레드의 확률은 매우 작지만 (경합이 낮음) 모든 요소마다 하나의 잠금이 필요합니다 (많은 것을 갖게 될 것입니다). 이 경우 일반적으로 병렬로 업데이트하는 배열과 동일한 크기의 ubytes 배열을 할당하고 (D 프로그래밍 언어)와 같이 스핀 락을 인라인으로 구현합니다.

while(!atomicCasUbyte(spinLocks[i], 0, 1)) {}
    myArray[i] = newVal;
atomicSetUbyte(spinLocks[i], 0);

반면에, 정기적 인 잠금 장치를 사용해야한다면, 포인터 배열을 물체에 할당 한 다음이 배열의 각 요소에 대해 뮤트 객체를 할당해야합니다. 위에서 설명한 것과 같은 시나리오에서는이 시나리오에서 이것은 단지 낭비입니다.

성능 임계 코드가있는 경우 그리고 당신은 그것이 현재보다 더 빠를 필요가 있다고 결정했습니다. 그리고 중요한 요소는 잠금 속도라고 판단한 다음 스핀 락을 시도하는 것이 좋습니다. 다른 경우에는 왜 귀찮게합니까? 일반 잠금 장치는 올바르게 사용하기가 더 쉽습니다.

다음 사항에 주목하십시오.

  1. 대부분의 MUTEXE의 구현은 스레드가 실제로 예정되지 않기 전에 잠시 동안 회전합니다. 이 때문에이 뮤텍스를 순수한 스피 락과 비교하기는 어렵습니다.

  2. 동일한 스핀 록에서 가능한 한 빨리 "빠르게"회전하는 여러 스레드는 모든 대역폭을 상환하고 프로그램 효율성을 크게 줄입니다. 회전 루프에 누프를 추가하여 작은 "수면"시간을 추가해야합니다.

응용 프로그램 코드에서 스핀 락을 사용해야 할 필요는 없습니다.

일반 OS에서 실행되는 C# 코드에서 스핀 락을 사용해야 할 이유가 없습니다. 바쁜 자물쇠는 대부분 응용 프로그램 수준의 폐기물입니다. 회전은 전체 CPU 타임 슬라이스를 사용할 수 있으며, 잠금 장치는 즉시 컨텍스트 스위치를 발생시킬 수 있습니다.

NR의 스레드 = NR이있는 고성능 코드 프로세서/코어의 NR이 도움이 될 수 있지만 해당 레벨에서 성능 최적화가 필요한 경우 차세대 3D 게임을 만들 가능성이 높으며 동기화 프리미티브가 불량한 내장 된 OS에서 작업하여 OS/드라이버 또는 C#을 사용하지 않는 경우.

나는 내에서 쓰레기 수집기의 세워진 단계에 스핀 잠금 장치를 사용했습니다. HLVM 그들이 쉽고 장난감 VM이기 때문에 프로젝트. 그러나 스핀 잠금 장치는 해당 맥락에서 비생산적 일 수 있습니다.

Glasgow Haskell Compiler의 쓰레기 수집기의 Perf Bugs 중 하나는 너무 성가시켜 이름이 있습니다. "마지막 핵심 둔화". 이것은 GC에서 스핀 락을 부적절하게 사용한 직접적인 결과이며 스케줄러로 인해 Linux에서 삭제됩니다. 그러나 실제로 다른 프로그램이 CPU 시간을 놓고 경쟁 할 때마다 효과가 관찰 될 수 있습니다.

효과는 두 번째 그래프에서 명확합니다 여기 그리고 마지막 핵심 이상의 영향을 미치는 것을 볼 수 있습니다. 여기, Haskell 프로그램이 5 개의 코어를 넘어서 성능 저하를 본다.

사용하는 동안 항상 이러한 점을 마음 속에 유지하십시오 스핀 락:

  • 빠른 사용자 모드 실행.
  • 단일 프로세스 내에서 스레드를 동기화하거나 공유 메모리 인 경우 여러 프로세스를 동기화합니다.
  • 개체가 소유 될 때까지 돌아 오지 않습니다.
  • 재귀를지지하지 않습니다.
  • "대기 중"동안 CPU의 100%를 소비합니다.

나는 누군가가 스핀 락을 사용하는 것이 좋은 생각이라고 생각했기 때문에 개인적으로 많은 교착 상태를 보았습니다.

스핀 락을 사용하는 동안 매우 조심하십시오

(나는 이것을 충분히 강조 할 수 없다).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top