Interlocked 함수란?
- Interlocked 계열의 함수들은 Interlocked 계열의 함수들 간의 원자적 연산을 보장해 주는 함수입니다.
Interlocked 함수의 원리
- x86 아키텍처의 CPU라면 메모리 버스에 Interlocked 하드웨어 시그널을 실어서 다른 코어에서 해당 캐시 라인에 접근하지 못하도록 하여 Interlocked 함수를 이용한 원자적 연산을 지원합니다.
Interlocked 함수 사용 시 주의 사항
주의 사항 1
- 캐시 라인 단위로 메모리 버스를 잠그기 때문에 Interlocked 계열의 함수에 사용될 변수들은 캐시 라인에 맞추어져 있어야 원자적 연산을 보장받습니다.
주의 사항 2
- 멀티 쓰레드 환경에서 다른 목적으로 Interlocked 계열 함수에 사용될 변수들은 서로 다른 캐시 라인에 있어야 쓰레드 경합으로 인한 성능 저하가 발생되지 않습니다.
동일 캐시 라인에 대한 쓰레드 경합 테스트
테스트 방법
- 동일한 캐시 라인에 있는 두 개의 변수들을 두 개의 쓰레드를 생성하여 각 쓰레드에서 InterlockedIncrement 를 천 만번 호출할때 소요되는 시간을 측정하여 비교합니다.
테스트 코드
#include <iostream>
#include <Windows.h>
#include <process.h>
#pragma comment(lib, "Winmm.lib")
HANDLE gEvent;
CRITICAL_SECTION gCS;
unsigned __stdcall TestThread(void* p)
{
long* pNum = static_cast<long*>(p);
if (WaitForSingleObject(gEvent, INFINITE) != WAIT_OBJECT_0)
{
std::cout << "Wait Failed\n";
return 1;
}
LARGE_INTEGER start;
LARGE_INTEGER end;
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&start);
for (int i = 0; i < 10000000; ++i)
{
InterlockedIncrement(pNum);
}
QueryPerformanceCounter(&end);
EnterCriticalSection(&gCS);
std::cout << (double)(end.QuadPart - start.QuadPart) / frequency.QuadPart << " 초" <<std::endl;
LeaveCriticalSection(&gCS);
return 0;
}
int main()
{
timeBeginPeriod(1);
// 64Byte 경계에 맞춤
__declspec(align(64))
long num1, num2;
std::cout << "num1 주소 : " << &num1 << std::endl;
std::cout << "num2 주소 : " << &num2 << std::endl;
HANDLE handles[2];
InitializeCriticalSection(&gCS);
gEvent = CreateEvent(nullptr, true, false, nullptr);
handles[0] = (HANDLE)_beginthreadex(nullptr, 0, (_beginthreadex_proc_type)TestThread, &num1, 0, nullptr);
handles[1] = (HANDLE)_beginthreadex(nullptr, 0, (_beginthreadex_proc_type)TestThread, &num2, 0, nullptr);
Sleep(2000);
SetEvent(gEvent);
if (WaitForMultipleObjects(2, handles, true, INFINITE) != WAIT_OBJECT_0)
{
std::cout << "Handles Wait Failed\n";
}
timeEndPeriod(1);
return 1;
}
- 위와 같이 코드를 작성하여 각 쓰레드에서 InterlockedIncrement 테스트를 진행 하였습니다.
테스트 결과 비교
- 테스트 결과 약 3.5배 정도 차이 나는 것을 확인하였지만, 성능 차이 정도는 쓰레드의 개수 및 하드웨어 환경에 따라 결과는 달라질 수 있습니다.
Interlocked 계열 함수들
InterlockedIncrement
- 인자로 전달된 변수를 원자적으로 1 증가시키고 return 값은 증가시킨 값입니다.
InterlockedDecrement
- 인자로 전달된 변수를 원자적으로 1 감소시키고 return 값은 감소시킨 값입니다.
InterlockedExchange
- 원자적으로 첫 번째 인자의 값을 두 번째 인자의 값으로 변경하고 return 값은 첫 번째 인자의 변경 전 값입니다.
InterlockedCompareExchange ( CAS )
- 원자적으로 첫 번째 인자의 포인터가 가리키는 값과 세 번째 값을 비교하여 동일하면 첫 번째 인자가 가리키는 값을 두 번째 인자의 값으로 치환합니다. return 값은 기존에 첫 번째 인자의 포인터가 가리키는 값입니다.
- InterlockedCompareExchange 는 Compare And Swap 으로도 불립니다.
'멀티 쓰레드' 카테고리의 다른 글
싱글 쓰레드와 멀티 쓰레드의 장.단점 (0) | 2022.09.30 |
---|---|
데드락 ( Deadlock )에 대한 설명과 데드락 회피 방법 (0) | 2022.09.25 |
Windows의 유저 모드 동기화 객체 & 커널 모드 동기화 객체 (0) | 2022.09.21 |
스핀 락 ( Spin Lock ) 이란? (0) | 2022.09.19 |