class Program
{
static int x = 0;
static int y = 0;
static int r1 = 0;
static int r2 = 0;
static void Thread_1()
{
y = 1; //Store y
r1 = x; //Load x
}
static void Thread_2()
{
x = 1; //Store y
r2 = y; //Load x
}
static void Main(string[] args)
{
int count = 0;
while (true)
{
count++;
x = y = r1 = r2 = 0;
Task t1 = new Task(Thread_1);
Task t2 = new Task(Thread_2);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
if (r1 == 0 & r2 == 0)
break;
}
Console.WriteLine(count);
}
}
하드웨이 최적화를 알기위한 예제중 하나이다.
스레드 2개가 있고 각각 Store계산을 먼저하고 Load계산를 하게 만들었다. 그 후 Main()함수에서 반복문으로 스래드를 실행시키고 WaitAll()을 사용해서 두 스레드가 끝날 때까지 기다린 다음 r1과 r2가 만약 0이면 빠져나오게 만들었다.
스레드를 순서대로 실행을 한다면 무한루프에 빠지게 된다.
하지만 여러번 실행 결과 전부 반복문을 빠져나왔다.
반복문을 빠져 나올 수 있던 이유는 하드웨어가 최적화를 실행 했기 때문이다.
Thread_1() 함수안에 코드 두 줄은 서로 의존성(연관성)이 없고 하드웨어가 의존성이 없다고 판단하고 성능을 높이는 최적의 수를 찾다가 코드의 순서를 바꾼 것이다.
Thread_2 () 함수도 마찬가지로 최적화가 실행 되어서 반복문을 빠져 나올 수 있었다.
해결법
<메모리 배리어>
코드 재배치 억제, 가시성
- Full Memory Barrier (ASM MFENCE) : Store/Load 둘 다 막는다
static void Thread_1()
{
y = 1; //Store y
Thread.MemoryBarrier();
r1 = x; //Load x
}
static void Thread_2()
{
x = 1; //Store y
Thread.MemoryBarrier();
r2 = y; //Load x
}
- Store Memory Barrier (ASM SFENCE) : Store만막는다
- Load Memory Barrier (ASM LFENCE) : Load만막는다
예제2
int _answer;
bool _complete;
void A()
{
_answer = 123;
Thread.MemoryBarrier(); //Barrier1
_complete = true;
Thread.MemoryBarrier(); //Barrier2
}
void B()
{
Thread.MemoryBarrier(); //Barrier3
if (_complete)
{
Thread.MemoryBarrier(); //Barrier4
Console.WriteLine(_answer);
}
}
Barrier1 : _answer이 write 했다라는 억제와 가시성을 보여준다
Barrier2 : _complete가 write 했다라는 억제와 가시성을 보여준다
Barrier3 : _complete가 최신화가 되었는지 억제와 가시성을 보여준다
Barrier4 : _answer이 최신화가 되었는지 억제와 가시성을 보여준다
'게임서버' 카테고리의 다른 글
ReaderWriterLock 구현 연습 (0) | 2020.12.01 |
---|---|
캐시 이론 (0) | 2020.11.30 |
컴파일러 최적화 (0) | 2020.11.30 |
쓰레드 기초 (0) | 2020.11.30 |