18 / 24 · 개념
의존성, 자원 hazard와 forwarding
앞선 결과가 필요한 연산에서 stall과 forwarding의 적용 조건을 비교합니다.
내용은 무료로 볼 수 있습니다. 수강 신청하면 학습 기록을 저장할 수 있어요.
파이프라인이 겹치면 순서만으로 충분하지 않다
독립된 입력 스트림의 f(a,b)는 각 거래가 이전 결과를 읽지 않으므로 여러 입력을 안전하게 겹칠 수 있습니다. 반면 작은 레지스터 파일을 사용하는 연산기는 뒤의 연산이 앞의 결과를 필요로 할 수 있습니다. 앞선 쓰기가 완료되기 전에 같은 레지스터를 읽는 read-after-write 의존성을 데이터 hazard라고 합니다. 이것은 숫자 연산이 틀린 문제가 아니라 올바른 숫자를 너무 이른 시점에 읽은 문제입니다.
초기 R1=4이고 I0가 R1←R1+3, I1이 R2←R1×2라고 합시다. 이 예제의 한 단계 실행기는 에지 0에 I0의 피연산자 4를 저장하고 다음 사이클에 결과 7을 계산하여 에지 1에 R1을 씁니다. I1이 에지 1에 R1을 샘플하면 레지스터 파일의 이전 값 4를 읽을 수 있어 R2=8이 됩니다. 순차 실행의 기대 결과는 R1=7,R2=14입니다. 같은 에지 읽기/쓰기의 의미를 따로 정하지 않으면 이 경계가 모호합니다.
| 해결 방식 | I1이 받는 값 | 필요한 조건 |
|---|---|---|
| 그냥 이전 R1 읽기 | 4 | 이 예제에서는 잘못된 결과 |
| 한 에지 더 stall | 에지 2의 R1=7 | 소스와 제어를 함께 보존 |
| 실행 결과 forwarding | 에지 1 직전 결과 7 | 결과가 에지 전에 준비되고 경로가 타이밍을 만족 |
forwarding은 아직 레지스터 파일에 쓰지 않은 계산 결과를 소비자의 입력 mux로 직접 보내는 것입니다. 단순히 목적 레지스터 번호가 일치하는 것만으로 선택하지 않습니다. 앞선 거래가 valid이고 실제로 그 레지스터를 쓰며 결과가 준비되어 있어야 합니다. 여러 앞선 쓰기가 같은 번호를 대상으로 하면 소비자보다 앞선 것 중 가장 최근 결과를 선택합니다. 무효 bubble이나 flush된 거래를 forwarding하면 폐기한 값이 살아납니다.
결과가 아직 없는 load에는 forwarding만으로 해결할 수 없습니다. 주소를 보냈지만 메모리 응답이 두 에지 뒤에 오는 경우 소비자는 결과 준비까지 멈춰야 합니다. stall을 걸면 해당 연산의 opcode, 소스 번호, tag도 함께 보존하고, 앞의 독립 단계가 계속 움직여도 되는지 결정합니다. 전체 파이프라인을 정지하는 간단한 설계와 필요한 단계만 정지하는 설계는 검증 조건이 다릅니다.
자원 hazard도 있습니다. 단일 포트 메모리를 같은 사이클에 두 단계가 사용하려 하면 데이터 의존성이 없어도 둘 다 실행할 수 없습니다. 우선순위로 하나를 기다리게 하거나 포트를 늘려야 합니다. 분기처럼 다음 실행 경로가 바뀌는 경우에는 제어 hazard가 생기며 잘못 들어온 거래를 flush하는 정책이 필요합니다. 각 hazard는 '누가 무엇을 기다리나'와 '어떤 상태를 유지하나'라는 질문으로 분석합니다.
이 강좌의 스트리밍 capstone은 거래 간 레지스터 의존성을 일부러 넣지 않습니다. 대신 이 예제를 손으로 계산하여 실제 ISA 또는 누산기를 추가할 때 왜 stall·forwarding·commit 계약이 필요한지 이해합니다. 독립 입력 파이프라인의 성공을 CPU 의존성 처리의 성공으로 확대 해석하지 않습니다.
직접 생각해 보기
R1=4에서 I0는 R1+3, I1은 결과×2입니다. 이전 값을 읽었을 때와 올바른 값으로 forwarding했을 때 I1 결과를 비교하세요. I0가 load로 바뀌고 결과가 아직 없으면 어떤 정책이 필요한가요?
해설 보기
이전 4를 읽으면 8, 앞선 결과 7을 받으면 14입니다. load 결과가 없으면 소비자를 stall하고 opcode·피연산자 선택·tag를 보존해야 합니다. 목적 번호가 맞는다는 이유만으로 미완성 값을 forwarding하면 안 됩니다.