하드웨어 최적화 오류(메모리 배리어)

개발조하·2024년 1월 19일

GameServer

목록 보기
6/9
post-thumbnail

1. 하드웨어 최적화로 인한 멀티쓰레드에서의 오류 (실습)


아무리 생각해봐도 r1과 r2 모두 0이 나올 수 있는 경우의 수가 없다.
어떻게 0이 되어서 무한루프를 빠져나올 수 있었을까?

  • 멀티스레딩과 동시성
    : 멀티스레드 프로그램에서 여러 스레드가 동시에 실행되며, 이들의 실행 순서는 운영 체제의 스케줄러에 의해 결정된다. 이로 인해 스레드의 작업 실행 순서는 예측 불가능하며 매번 다를 수 있다.

  • 순서 교환(Out-of-order execution)
    : 현대 CPU는 성능 최적화를 위해 명령어를 프로그램 코드에 명시된 순서와 다르게 실행할 수 있다. 이는 멀티스레드 환경에서 예측하기 어려운 결과를 초래할 수 있다.

💡 r1 == 0 && r2 == 0이 가능한 시나리오:

  • Thread_1에서의 실행 순서:
    r1 = x; 가 먼저 실행되고, 이 시점에서 x는 여전히 0이다.
    그 후에 y = 1;이 실행된다.
  • Thread_2에서의 실행 순서:
    r2 = y; 가 먼저 실행되고, 이 시점에서 y는 여전히 0이다.
    그 후에 x = 1;이 실행된다.

이 시나리오에서는 Thread_1의 y = 1;과 Thread_2의 x = 1;이 각각 r1 = x;와 r2 = y;보다 나중에 실행된다. 이는 CPU의 순서 교환 또는 스레드 스케줄링에 의해 발생할 수 있다.

이러한 현상을 방지하기 위해서는 추가적인 동기화 메커니즘을 사용해야한다. (메모리 배리어)

2. 메모리 배리어

2.1 코드 재배치 억제

  • Full Memory Barrier (ASM MFENCE, C# Thread.MemoryBarrier)
    : Store/Load 둘 다 막는다.

  • Store Memory Barrier (ASM SFENCE)
    : Store만 막는다.

  • Load Memory Barrier (ASM LFENCE)
    : Load만 막는다.

ㄴ 보통은 위 예시처럼 Full Memory Barrier를 많이 사용한다.

2.2 가시성

멀티스레드 환경에서, 하나의 스레드에 의해 수행된 변경사항이 다른 스레드에 즉각적으로 보이지 않을 수 있다. 이는 CPU 캐시, 컴파일러 최적화, 시스템 아키텍처 등 다양한 요인 때문이다.

  • 가시성 보장
    : 한 스레드에서 수행된 메모리 작업이 메모리 장벽을 통과한 후, 다른 스레드에게 가시적이 되도록 한다. 즉, 메모리 장벽 이전에 변경된 값들이 메모리 장벽 이후에 다른 스레드에게 '보인다'는 것을 보장한다.

(앞서 배웠던 volatile도 가시성의 기능을 했다.)

  • 추가 실습

📄참고자료
[인프런] c#과 유니티로 만드는 MMORPG 게임 개발 시리즈_4. 게임 서버

profile
Unity 개발자 취준생의 개발로그, Slow and steady wins the race !

0개의 댓글