Study/CS

해저드(Hazard)란 무엇일까?

MuviSsum 2019. 12. 20. 17:55

파이프라이닝에서 나타나는 문제점을 뜻한다.

파이프라이닝이란? - 멀티 프로세서의 기본적인 능력(멀티 프로세서를 알면 이해 가능하다.) 클릭

해저드는 크게 3가지 종류로 나누는데,

1. 구조적 해저드

2. 데이터 해저드

3. 컨트롤 해저드

로 나뉜다. 순서대로 알아 보도록 하자.

 

1. 구조적 해저드

구조적 해저드는 거의 우리가 손댈 수 없는 문제이다.

자원이 한정되어 있는데, 우리가 자원을 새롭게 자원 복제하던지, 새로 끼울 수 없으니까 말이다.

'슈퍼스케일러'라면 말이 다르다. - 슈퍼스케일러 : 많은 패치 유닛을 가짐(슈퍼 컴퓨터라고 생각)

그래서 자원을 다 쓸 때까지 기다리는 'stall', 'bubble'이라는 delay instruction을 사용한다.

 

2. 데이터 해저드

우리가 해결할 수 있는 문제들이 여기 다 있다.

데이터 해저드는 3가지 종류로 나뉘는데,

(1) RAW : Read After Write (true dependency)

앞에서 사용한 데이터를 아직 한 파이프가 다 돌지 못 하여 write 되지 않은 상태라,

변경되지 않은 데이터를 사용할 수 없으므로 기다리는 것이다.

데이터 디펜던시에 의해 생겨난다. 

(2) WAW : Write After Write(output dependency)

2가지 일이 같이 일어나 같은 공간에 먼저 쓰인 데이터가 사라지는 것이다.

(3) WAR : Write After Read (Anti - Dependency)

2가지 일이 같이 일어나 의존성이 있는 관계의 데이터들이 일의 순서에 따라 결과가 달라진다.

위의 (2), (3)은 싱글코어 프로세서에서는 일어날 수 없는 일이다.

멀티코어 프로세서는 어려우니까 전문적으로 배우고,

(1)을 없앨 방법을 생각해 보자.

해결방법 첫 번째, 구조적 해저드 처럼 'stall'을 쓰자.

하지만 이건 오버헤드가 크다.

해결방법 두 번째, 클럭 한개를 앞 쪽 반을 write, 뒤 쪽 반을 read로 바꾸면 어떨까?

이 또한 한계가 있었다. 처리율을 좀 더 좋게 만들었지만 한계에 봉착하여

해결방법 세 번째, Forwarding or Bypassing

획기적인 방법이다. 실행단계에서 이미 결과 값이 나오니까 저장하기 전에 바로 써버리자는 생각이다.

그렇게 써서 매우 앞당겼지만, 한 가지 더 문제가 있었다.

load의 존재이다. load는 메모리에서 불러오기 때문에 실행 단계에서는 데이터의 값이

무슨 값인지 모르는 상태이므로 load뒤에 오는 명령어에 load 할 데이터가 있다면,

그 때는 또, 'stall'로 기다려야 한다. 그래서 실행하기 전 load로 쓸 데이터들을 먼저

불러오는 경향이 있다. C, JAVA 등 다 선언을 앞에 하는 이유가 이 이유라고 볼 수도 있다.

해결방법 네 번째, 컴파일러 측면에서 명령의 위치를 바꾸는 것이다.

데이터 디펜던시를 생각하여 R1 + R2 = R3, R2 + R4 = R5 같은 경우 순서를 바꾸면 안되지만,

R1 + R2 = R3, R6 + R4 = R5 같은 경우 순서를 바꿔버려도 결과가 같다.

이 기술을 '인스트럭션 스케줄링'이라고 하고 데이터 해저드를 줄여준다.

 

3. 컨트롤 해저드

얘는 Branch문 때문에 생겨난 희대의 문제아다.

다른 해저드보다 많이 크리티컬한 경향이 있다.

왜냐하면 EXEC에서 확인 불가능하고 Branch 자체가 코스트가 큰 명령어라

적어도 메모리 연결 단계까지 가야 확인 가능하기 때문이다.

그래도 사람들은 굴하지 않고 해결을 하려 했고, 몇 가지 해결 방법이 나왔다.

(1) 당연히 첫 번째 해결방법은 'stall', 기다리기

(2) 두 번째는 예측이다. Taken 인지 NOT-Taken 인지 구분을 하여 먼저 실행시켜 놓는 것이다.

Branch는 2가지로 나누어지는 구문이기 때문에 1/2 확률을 맞춘다면

Flush ( 예측하여 실행했던 구문들이 Branch 결과가 반대로 나와 싹 버리는 것 ) 를 하는 횟수가

매우 줄어들 것이다. 미리 예측을 해놓는 정적 예측방법과 History를 통해 예측하는 동적 예측방법이 있다.

정적 예측방법은 그냥 우리가 예측하여 저장해놔 컴퓨터는 그에 따라서 예측을 하는 것이고,

동적 예측방법은 History 기반으로 데이터의 지역성을 이용한 건데,

Branch를 쓸 때, Array(공간 지역성), Loop(시간 지역성)을 통해, 예측을 한다.

예측 비트는 2bit prediction 사용하며, 스테이트를 4개 가진다. 강약을 추가한 형태라고 한다.

Taken - Weakly Taken - Weakly NOT-Taken - NOT-Taken = 00, 01, 10, 11 로 설명되며,

Taken 하거나 NOT - Taken 하냐에 따라 한칸 씩 이동한다.

Ex) 처음 상태는 Taken 일 때,

Not-Taken 발생 : Taken -> Weakly Taken 

한번 더 Not-Taken 발생 : Weakly Taken -> Weakly NOT-Taken 

Taken 발생 : Weakly NOT-Taken -> Weakly Taken 

위와 같이 진행 된다.

(3) 마지막은 딜레이 슬롯인데, 남는 슬롯(남는 자원)을 최대한 이용하게 하는 것을 말한다.

사실 난 인스트럭션 스케줄링과 다른 점을 모르겠다. 그냥 인스트럭션 스케줄링같은 것인데,

컨트롤 해저드를 제거하는 기법이라고 생각하면 되겠다.

반응형