모든 이야기

틀릴 수도 있는데 CPU는 왜 먼저 계산할까: 분기예측과 추측실행

갈림길 앞에서 멈추면 뒤가 빈다

조립 라인이 제품 하나의 완성을 기다린 뒤 다음 제품을 받는다면 많은 설비가 놀게 된다. CPU도 명령을 가져오기, 해석하기, 실행하기 같은 단계를 겹쳐 진행하는 파이프라인으로 처리량을 높인다. 그런데 다음에 갈 주소가 조건에 따라 달라지는 분기를 만나면 문제가 생긴다. 조건의 답이 나올 때까지 기다리면 뒤를 채울 명령을 가져오지 못한다.

분기예측기는 과거의 분기 결과 같은 정보를 이용해 다음 경로를 먼저 고른다. 반복문이 여러 번 계속되었다면 이번에도 계속될 가능성을 이용하는 식이다. 조건식을 대신 정확히 계산한 것이 아니라, 답을 기다리는 동안 쓸 가설을 세운 것이다. 점프할지 여부뿐 아니라 어디로 점프할지도 제때 알아야 한다. MIT 6.823, 분기예측과 파이프라인

실행했다고 결과를 확정한 것은 아니다

예측한 경로의 명령을 실제로 진행시키는 것이 추측실행이다. 여기서 반드시 구분할 단어가 실행과 확정이다. 계산 장치가 덧셈을 끝냈다는 사실과, 그 결과가 프로그램의 올바른 상태로 인정되었다는 사실은 다르다.

전형적인 비순차 실행 코어에서는 준비된 명령부터 계산할 수 있지만, 재정렬 버퍼 같은 장치가 명령의 순서를 관리한다. 결과는 조건이 확인되기 전까지 임시 상태로 남고, 프로그램 순서에 맞춰 확정된다. 이 마지막 단계를 커밋 또는 리타이어라고 부른다. 분기 뒤의 명령이 일찍 끝나도, 앞선 예측이 틀렸으면 그 결과를 정상 결과로 확정할 수 없다.

오예측이 확인되면 잘못된 경로에서 진행한 명령들을 버리고 올바른 주소에서 다시 가져온다. 따라서 정상적인 동작에서 예측 오류는 계산 정답을 바꾸는 대신 시간과 자원을 소모한다. 다만 프로그램에 보이는 결과를 버린다고 캐시 같은 내부 상태 변화까지 모두 되돌리는 것은 아니다. Intel, 추측실행의 실행·확정·폐기 구분

한 번 틀릴 때 몇 사이클을 잃는가

잘못 가져온 명령을 버리는 것만으로 일이 끝나지 않는다. 정확한 경로로 방향을 돌리고 명령 공급과 실행 흐름을 다시 채워야 한다. 조건을 알아내는 시점이 늦을수록 잘못된 방향으로 진행할 여지도 커진다. 그러나 오예측 비용을 모든 CPU에서 같은 고정값으로 생각해서는 안 된다. 파이프라인 구조, 분기 앞의 의존성, 명령 공급 상태 등에 따라 달라진다.

다른 지연과의 중첩을 무시하는 단순 모형에서는 오예측의 추가 비용을 이렇게 계산할 수 있다.

추가 CPI ≈ b × m × P

CPI는 확정된 명령 하나당 걸린 평균 클록 사이클 수다. b는 확정된 명령 중 분기의 비율, m은 그 분기들의 오예측 비율, P는 오예측 한 번의 평균 추가 사이클이다. 각 비율의 분모를 구분해야 한다. 전체 명령 중 틀린 분기 비율을 m에 넣으면 b를 중복으로 곱하게 된다.

가상 예로 명령의 20%가 분기이고 그중 10%를 틀리며 한 번에 15사이클이 더 든다면 추가 CPI는 0.3이다. 확정 명령 1,000개 중 분기 200개, 오예측 20번, 추가 지연 300사이클로 풀어 보면 쉽다. 이는 제품 측정치가 아니라 평균 비용을 세는 예다. 실제 성능 카운터도 확정 명령과 확정된 분기의 오예측을 구분해 집계하므로, 이벤트 정의를 확인해야 한다. Intel, 명령·분기 성능 이벤트

예측률 하나로 설계를 고를 수 없는 이유

예측 정확도를 높이려면 더 많은 이력을 저장하고 패턴을 구별하는 회로가 필요할 수 있다. 그러나 다음 주소는 빨리 내놓아야 한다. 정확하지만 답이 너무 늦거나 자원을 지나치게 쓰는 예측기가 항상 유리한 것은 아니다. 두 경로를 모두 실행하는 방법도 있지만, 분기가 이어질수록 처리해야 할 후보가 불어나 유용한 일에 쓸 자원이 줄어든다.

또한 맞힌 분기라도 목표 주소를 얻는 데 지연이 있으면 비용이 완전히 0이 되지는 않는다. 실제 성능은 정확도, 예측 응답 속도, 틀렸을 때의 회복 비용을 함께 봐야 한다. MIT, 목표 주소 예측과 오예측 복구

CPU가 바쁘게 많은 명령을 실행한 모습만으로 유용한 일을 많이 끝냈다고 판단할 수 없는 이유도 여기에 있다. 중요한 것은 예상 경로에서 얼마나 움직였는지가 아니라, 올바른 결과로 확정한 명령과 최종 작업 완료 시간이다.

틀릴 수도 있는데 CPU는 왜 먼저 계산할까: 분기예측과 추측실행 · ariser