ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 파이프라인과 멀티스레드
    C++/C++ 기타 2022. 10. 25. 22:25
    728x90

    오늘은 파이프라인의 개념에 대해서 간단히 알아보겠습니다.

     

    컴퓨터구조에서 파이프라인 개념의 핵심은

    코드의 실행순서가 임의로 바뀔 수 있다는 것입니다.

     

    CPU에서는

    1. Fetch
    2. Decode
    3. Excute
    4. Write-Back

    의 순서로 명령이 실행 됩니다.

     

    그런데,

    1. 실행순서가 바뀌어도 서로 연관이 없는 명령들이라면
    2. 순서를 바꿈으로써 속도가 더 빨라질 수 있다면

    CPU는 임의로 실행순서를 바꾸기도 합니다.

     

    예를 들어서

    int x=5;

    y=y+1;

    과같은 코드는 두 순서를 바꾸어도 아무런 문제가 없습니다.

     

    그래서 필요하다면 순서가 바뀌기도 하는거구요.

     

    이게 싱글스레드 상황에서는 아무런 문제가 발생하지 않습니다.

    그런데 멀티스레드 상황이라면 이야기가 달라집니다.

     

    int x = 0;
    int y = 0;
    int r1 = 0;
    int r2 = 0;
    
    volatile bool ready;
    
    void Thread_1()
    {
    	while (ready == false)
    		;
    
    	y = 1; //store y
    	r1 = x; //load x
    }
    
    void Thread_2()
    {
    	while (ready == false)
    		;
    
    	x = 1;//store x
    	r2 = y; //loady
    }
    
    
    int main()
    {
    	int count = 0;
    
    	while (true)
    	{
    		ready = false;
    		count++;
    		
    		x = y = r1 = r2 = 0;
    		std::thread t1(Thread_1);
    		std::thread t2(Thread_2);
    
    		ready = true;
    
    		t1.join();
    		t2.join();
    
    		if (r1 == 0 && r2 == 0)
    			break;
    	}
    
    }

    위와 같은 코드가 있다고 가정해보겠습니다.

     

    • 이 경우 x, y, r1, r2를 모두 0으로 초기화 합니다.
    • 스레드 1은 y=1 → r1=x를 실행합니다. 이 둘은 전혀 연관없는 코드입니다.
    • 스레드 2는 x=1 → r2=y를 실행합니다. 이 둘은 전혀 연관없는 코드입니다.
    • 스레드 1과, 2는 ready가 true로 바뀌는 동시에 작업을 시작합니다
    • 만약 r1과 r2가 모두 0이 된다면 실행을 종료합니다.

     

    논리적으로 봤을 때는 둘 모두 0이 되는 일이 없어야 합니다.

     

    r1에 x를 대입하기 위해서는 y에 1을 대입한 후에 실행할 수 있고

    r2에 y를 대입하기 위해서는 x에 1을 대입한 후에 실행할 수 있기 때문입니다.

     

    하지만, 파이프라인의 특성에 의해서 x와 y가 둘 모두 0이 되는 상황이 발생하게 됩니다.

     

    만약 스레드 1에서 코드의 실행순서가 파이프라인에 의해서

    r1=x → y=1로 바뀌었다고 가정해봅시다.

     

    그러면, 

    1. 스레드 1은 x가 0일 때 r1에 x를 대입
    2. 스레드 2는 x=1을 대입
    3. 스레드 2는 y가 0일 때 r2에 y를 ㄷ대입
    4. 스레드 1은 y=1을 대입

     

    이러한 시나리오가 가능해집니다.

     

    참고로 가시성이라는 개념이 있는데

    메모리에 있는 데이터와 캐시에 있는 데이터가 일치하지 않는 상황을 말합니다.

     

    CPU는 작업의 편의를 위해서 캐시를 이용합니다.

    메모리에서 캐시로 데이터를 읽어와서 캐시에대해서 작업을 수행한 후

    작업을 마치면 (혹은 주기적으로) 캐시의 값을 메모리에 씁니다(write)

     

    그런데 멀티스레드 환경에서 

    • 스레드 1이 메모리에서 x를 읽어와서 x를 1로 변경하고 메모리에 write하기 전에
    • 스레드 2가 x를 읽어오려고 할 때 그 값의 불일치가 발생할 수도 있습니다.

    희박하나 가능한 시나리오입니다.

     

     

    728x90

    'C++ > C++ 기타' 카테고리의 다른 글

    캐시(Cache)(feat. Hit Rate, 메모리 계층구조, Locality)  (0) 2022.10.24
    C++, volatile  (0) 2022.10.09

    댓글

Designed by Tistory.