spin lock

SQL Server 2007. 2. 27. 16:37

오늘은 스레드 동기화에 대해서 제 생각(다년간의 삽질)을 정리하려고 합니다.  

한가지 궁금한점이 있습니다. 제 블로그에 하루에 약200명정도 오시는 것 같은데 덧글을 남기거나 의견을 주시는 분은 하루에 1분도 없습니다. 제가 틀리거나 잘 못 알고 있는 것이 있을텐데...

Please do not heasitate any opinion say or write to me^,.^

사설이 길었네요 그럼 시작합니다유^^

[Thread Synchronization Overview]

-
동기화 관련 시스템 콜들은 CPU 자원을 많이 소모하기 때문에 최대한 피하는 것이 좋다.

(CPU
자원을 많이 소모하기 때문에 사용않할 수도 없고 어찌해야 하죠 ㅠ,.)

-
동기화 관련 객체 LOCK/RELEASE 사이의 코드는 최대한 짧게 작성해야 한다.

(
너무 짧게 하면 오히려 동기화가 안되는 경우가 있었습니다. 반대로 코드를 길게 동기화를 하면 성능이 낮아집니다. 양날의 검같습니다. 특히 STL사용할 때는 주의 해야 합니다. 좀더 자세한 내용은 제블로그 에 있습니다.)


[Critical Section]
크리티컬 섹션이 유저 모드에서 동작하기 때문에 일반적으로 뮤텍스보다 빠르다(2 ~ 10)는 것은 널리 알려져 있다. 그러면 왜 빠른지에 대해서 간단하게 살펴 보겠습니당.

1)
스레드가 EnterCriticalSection 함수를 호출한 경우, 유저모드에서 우선적으로 CRITICAL_SECTION 구조체 내부의 변수를 검사하게 된다. 이 값이 off인 경우, 바로 atomic 연산을 통해 이 값을 on으로 바꾸고 다음 작업을 진행하게 된다. 커널모드로 내려가지 않고 유저모드에서 바로 동작을 수행합니다.

2)
크리티컬 섹션이 다른 스레드에 점유되어 있는 경우, 싱글 CPU 상에서는 바로 커널 모드로 넘어가서 동기화 작업이 수행되고, 다중 CPU 상에서는 Spin Count만큼 대기(busy-waiting)하면서, 락이 풀리기를 기다리게 된다.

3)
커널 모드로 넘어가게 되면, 해당 스레드는 세마포어를 이용한 WAIT 상태가 된다.

4)LeaveCriticalSection
함수를 호출하게 되면, CRITICAL_SECTION 구조체 내부의 변수를 off로 바꾸고, 세마포어를 이용한 경우에는 기다리고 있는 스레드에게 통지를 해주게 된다.

5)EnterCriticalSection
한 스레드에서 또 EnterCriticalSection을 호출 하면 내부적으로 Count 변수를 올려서 Deadlock을 막아준다. .EnterCriticalSection을 호출한 횟수만큼 LeaveCriticalSection을 호출해 주어야 한다.

정리해보자면 크리티컬 섹션이 뮤텍스보다 빠른 것은 2가지 경우다.



1)
같은 크리티컬 섹션 객체에 접근하는 스레드의 숫자가 적어서, CRITICAL_SECTION 내부 락 변수만으로 동기화가 이루어질 때.

2)
적당한 스핀 카운트를 통해 WAIT 상태로 가기 전에 락을 획득할 수 있을 때. (SMP 머신의 경우)

, 크리티컬 섹션 관련 스레드가 최대한 커널 모드로 들어가지 않도록 해줘야한다는 말이다.

[Critical Section Spin Count]
위에서도 설명했듯이, SMP 시스템에서 크리티컬 섹션과 관련된 병목을 줄이기 위해서는 적당한 스핀 카운트를 설정 해야 한다. 커널 모드로 들어가기 보다는 유저 모드에서 busy-waiting을 하는 게 나을 수 있기 때문이다. 이를 위한 API 함수가


SetCriticalSectionSpinCount
InitializeCriticalSectionAndSpinCount
(
자세한 내용은 MSDN을 함 살펴 보이소)

Spin Count 값으로서 어느 정도의 값을 주는지는 당연히 시스템/애플리케이션마다 틀리다.
참고로 MS-SQL 에서 스핀카운트 값을 4000으로 설정합니다.(16진수인지 10진수인지 혼동되요-,.)
그리고 스핀카운트는 CPU가 최소 HyperThrading 을 지원하는 곳에서 성능이 향상된다.




[Semaphore Throttles (Thundering Herd(
놀란 양떼)]

하나의 동기화 객체(크리티컬 섹션 또는 뮤텍스)를 기다리는 스레드가 너무 많은 경우, 세마포어를 하나 더 둬서, 해당 동기화 객체를 기다리는 스레드의 숫자를 줄여주는 것이 성능 향상에 도움이 될 수 있다.

(
이 내용은 이해하기 어려울 것같습니다. 저도 처음으로  Efficient C++ 책에서 읽은 내용인데 무심코 넘어갔습니다. 그러나 MultiCPU에서 초강력 슈퍼 울트라 초특급 MultiThreading 프로그램을 개발할때는 꼭 알고 있어야 합니다. 나중에 따로  Efficient C++ 책 내용을 기초로 글을 작성하겠습니다. 기둘리이소^^)


while (TRUE)

{ // Worker loop
   WaitForSingleObject (hThrottleSem, INFINITE);
   WaitForSingleObject (hMutex, INFINITE);
      ... Critical code section ...
   ReleaseMutex (hMutex);
   ReleaseSemaphore (hThrottleSem, 1, NULL);
} // End of worker loop

 

스핀잠금

단기적 잠금이 필요한 경우에 SQL Server는 스핀 잠금(spinlock)으로 구현된 래치를 사용하여 상호 배타적 기능을 얻는다. 스핀 잠금은 순수하게 상호 배타성 기능을 위해서만 사용되고, 결코 사용자 데이터를 잠그지 않는다. 스핀 잠금은 래치보다 훨씬 가볍다.(그리고 래치는 데이터 페이지와 인덱스 잎 페이지에 사용되는 오나전한 잠금보다 더 가볍다.) 스핀 잠금은 SQL Server에만 있는 기능이고, 이 프로세서 형식을 구현하기 위해 프로세서 관련 어셈블리 언어가 사용된다. 스핀 잠금은 각 프로세서 형식과 관련된 몇 라인의 어셈블리 언어로 구현된다. 스핀 잠금을 요청한 개체는 이 잠금이 즉시 사용 가능하게 될 때까지 반복적으로 자신의 요청을 전달한다. 스핀 잠금은 일상적으로 자주 사용되지 않는 리소스에 대해 SQL Server내에서 종종 mutex로 사용된다. 리소스가 자주 사용된다면, 기다리다가 운영체제에 의해 할당되는 것보다 반복 요청이 더 좋을 정도로 스핀 잠금 시간이 충분히 짧다. 스핀 잠금은 리소스 대기 시간이 짧다고 예상되는 경우에 사용된다.

*********************************************************************************************************************************************************

 

AND