Chapter 04

슈퍼스칼라·비순차·big.LITTLE

스마트폰 칩의 프라임 코어는 효율 코어보다 면적이 네 배쯤 크다. 그런데 같은 클럭에서 일을 네 배 빨리 하지는 못한다. 기껏해야 두세 배다. 그 큰 면적은 어디에 쓰였을까? 답은 "기다리지 않기"에 있다. 큰 코어는 한 사이클에 명령을 여덟 개, 열 개씩 들여다보고, 앞 명령이 캐시 미스로 멈춰 있으면 수백 개 뒤의 명령까지 먼저 처리해 둔다. 순서를 마음대로 바꾸면서도 결과는 프로그램에 적힌 순서와 똑같이 보이게 만드는 마법, 그것이 비순차 실행이다. 이 장에서는 그 마법의 부품인 레지스터 이름 바꾸기와 재정렬 버퍼를 직접 돌려 보고, 왜 칩에 큰 코어와 작은 코어를 함께 넣는지까지 실험한다.

순서 속에 숨은 병렬성

프로그램은 명령을 한 줄로 늘어놓지만, 명령들이 정말로 그 순서를 지켜야 하는 것은 아니다. a = b + c와 d = e × f는 서로 아무 관계가 없으니 동시에 해도 된다. 반면 g = a + d는 앞의 두 결과를 기다려야 한다. 명령 사이의 이런 "앞 결과를 써야 함" 관계를 화살표로 그리면 데이터 흐름 그래프(Dataflow graph)가 된다. 그래프에서 가장 긴 사슬, 즉 임계 경로가 계산 자원이 무한해도 넘을 수 없는 시간의 하한이다. 명령 수를 임계 경로 길이로 나눈 값이 그 프로그램이 원리적으로 가진 명령어 수준 병렬성(ILP, Instruction-Level Parallelism)이다.

명령 수—
하나씩 차례로 (지연 합)—
임계 경로—
이상적 IPC (자원 무한)—
그림 4-1. 눌러 보기왼쪽은 프로그램, 오른쪽은 각 명령이 가장 일찍 시작할 수 있는 사이클에 놓은 데이터 흐름 그래프다(덧셈 1, 곱셈 3, 적재 4사이클). 명령을 누르면 그 명령이 기다려야 하는 조상(파랑)과 그 명령을 기다리는 후손(분홍)이 밝아진다. 분홍 테두리는 임계 경로다. 연결 리스트는 다음 주소를 알려면 앞 적재가 끝나야 해서, 명령이 몇 개든 한 줄로 늘어선다.

실제 프로그램을 이렇게 분석하면, 분기 예측이 완벽하고 계산기가 무한하다는 이상적 조건에서 IPC가 수십~수백에 이르는 경우도 많다. 문제는 그 병렬성이 멀리 흩어져 있다는 것이다. 지금 실행할 수 있는 명령이 수십 줄 뒤에 있을 수 있다. 그것을 찾아내려면 많은 명령을 한꺼번에 들여다볼 수 있어야 한다.

슈퍼스칼라: 한 사이클에 여러 명령

3장의 파이프라인은 매 사이클 명령 하나를 들여보내므로 CPI가 1보다 작아질 수 없었다. 파이프라인을 여러 줄 깔아 한 사이클에 명령을 2개, 4개, 8개씩 인출·해독·실행하면 이 벽을 넘을 수 있다. 이것이 슈퍼스칼라(Superscalar)다. 한 사이클에 들여보내는 명령 수를 발행 폭(Issue width)이라 한다. 그런데 명령을 프로그램 순서대로만 들여보내면(순차 슈퍼스칼라) 앞 명령 하나가 막히는 순간 뒤의 모든 명령이 같이 멈춘다. 폭을 늘리며 두 방식을 비교해 보자.

순차 IPC—
비순차 IPC (창 128)—
이 프로그램의 ILP 한계—
그림 4-2. 만져 보기위쪽 두 표는 처음 몇 사이클 동안 매 사이클 어떤 명령(번호)이 발행되었는지 보여 준다. 칸 하나가 발행 슬롯이고, 빈칸은 버려진 슬롯이다(색: 덧셈 초록, 곱셈 주황, 적재 파랑). 아래 그래프는 폭에 따른 IPC다. 순차 방식은 곱셈·적재를 기다리는 동안 뒤의 독립 명령까지 묶여 폭 2~3에서 거의 멈추고, 비순차 방식은 프로그램의 ILP 한계 가까이 올라간다. 계산 장치는 폭만큼 있다고 가정했다.

순차 슈퍼스칼라도 쓸모가 있다. 스마트폰의 효율 코어가 바로 2~3폭 순차 코어다. 단순해서 작고, 전력이 적다. 하지만 폭을 그 이상 늘리는 것은 순차 방식에선 의미가 없다. 그래서 고성능 코어는 명령을 일단 넓은 대기실에 받아 두고, 준비된 것부터 먼저 실행하는 비순차 실행(Out-of-order execution)을 쓴다. 그 대기실을 크게 만들려면 먼저 풀어야 할 문제가 하나 있다.

레지스터 이름 바꾸기: 거짓 의존 지우기

명령 사이 의존에는 세 종류가 있다. 앞 명령이 쓴 값을 뒤가 읽는 RAW(Read After Write, 참 의존), 앞 명령이 읽을 레지스터를 뒤가 덮어쓰는 WAR(Write After Read), 둘이 같은 레지스터에 쓰는 WAW(Write After Write). RAW만 진짜 데이터의 흐름이다. WAR과 WAW는 레지스터 이름이 32개뿐이라 같은 이름을 돌려쓰다 생긴 이름 충돌이다. 순서를 바꾸면 이 충돌 때문에 값이 엉킨다.

해법은 이름을 넉넉하게 바꿔 주는 것이다. 코어 안에는 프로그램이 보는 32개의 구조 레지스터보다 훨씬 많은(수백 개) 물리 레지스터가 있다. 명령이 레지스터에 쓸 때마다 빈 물리 레지스터를 새로 하나 내주고, "구조 레지스터 r1은 지금 물리 레지스터 p9에 있다"는 대응표, RAT(Register Alias Table)를 고친다. 뒤의 명령은 이 표를 보고 올바른 물리 레지스터를 읽는다. 이것이 레지스터 이름 바꾸기(Register renaming)다. 한 명령씩 넘기며 표가 어떻게 바뀌는지 보자.

RAW (참 의존)—
WAR (거짓)—
WAW (거짓)—
그림 4-3. 단계 진행왼쪽은 원래 프로그램(위)과 이름을 바꾼 프로그램(아래)이다. 곡선은 명령 사이 의존이다: 실선 파랑 RAW, 점선 주황 WAR, 점선 빨강 WAW. 이름 바꾸기를 마친 명령 사이에서는 WAR과 WAW가 사라지고 RAW만 남는다. 오른쪽은 RAT(구조 → 물리 레지스터)와 아직 쓰지 않은 물리 레지스터 목록(자유 목록)이다.

이름 바꾸기 뒤에 남은 것은 RAW뿐이므로, 명령은 피연산자가 준비되는 순서대로 아무 때나 실행해도 된다. 물리 레지스터가 언제 다시 자유 목록으로 돌아가는지도 중요하다. r1에 새 값을 쓴 명령이 확정(다음 절)되면 그 전의 r1 값을 담던 물리 레지스터는 더 이상 아무도 읽지 않으므로 반납한다. 물리 레지스터가 바닥나면 이름 바꾸기가 멈추고, 그 뒤로 명령이 들어오지 못한다. 큰 코어가 물리 레지스터를 수백 개씩 두는 이유다.

비순차 실행 코어: ROB와 순서대로 확정하기

비순차 코어의 일은 세 박자로 흐른다. ① 순서대로 들여보내기: 인출·해독·이름 바꾸기를 거친 명령을 프로그램 순서대로 재정렬 버퍼(ROB, Reorder Buffer)의 꼬리에 넣는다. ② 순서 없이 실행하기: 대기 중인 명령 가운데 피연산자가 준비되고 계산 장치가 비어 있는 것을 골라 실행한다. 이 대기실을 스케줄러(예약 스테이션)라 하며, 1960년대 IBM 360/91의 Tomasulo 알고리즘이 원형이다. ③ 순서대로 확정하기: ROB의 머리, 즉 가장 오래된 명령이 끝났으면 그 결과를 정식으로 확정(Commit, Retire)하고 ROB에서 뺀다.

왜 마지막을 순서대로 할까? 중간에 예외(잘못된 메모리 접근, 0으로 나누기)나 분기 예측 실패가 일어나면, 그 명령 앞까지는 모두 끝났고 뒤는 하나도 일어나지 않은 깔끔한 상태로 되돌려야 하기 때문이다(정밀 예외(Precise exception)). ROB에 아직 확정되지 않은 결과는 언제든 버릴 수 있다. 결국 비순차 코어는 "속으로는 마음대로, 겉으로는 순서대로"다.

SIMULATOR

비순차 실행 코어

사이클—
비순차 IPC—
순차 IPC (같은 설정)—
ROB 평균 점유—
동시에 진행한 미스 (최대)—
위 격자가 ROB다(원형 버퍼). 칸 색: 회색 테두리 = 피연산자나 장치를 기다림, 파랑 = 실행 중, 빨강 = 캐시 미스로 메모리를 기다림, 초록 = 실행이 끝나 확정을 기다림, 빈칸 = 비어 있음. 굵은 테두리가 가장 오래된 명령(머리)이다. 칸 글자 L 적재, S 저장, × 곱셈, + 덧셈. 아래 그래프는 확정된 명령 수의 누적으로, 기울기가 IPC다. 분기 예측은 완벽하다고 가정했고, 적재 적중 4사이클, 곱셈 3사이클이다. 프로그램은 같은 반복문을 펼친 명령 300개다.
미스 비율 10%, 미스 지연 120사이클에서 ROB를 64에서 256으로 늘리면?

"배열 합" 프로그램에서 IPC가 몇 배쯤 될지 먼저 예상해 보자. 그리고 "연결 리스트"에서는?

답 보기

배열 합에서는 적재 주소가 서로 독립이라 ROB가 클수록 더 많은 미스를 동시에 보낼 수 있다. 미스 여러 개가 겹치면 기다리는 시간이 나누어지므로 IPC가 크게 오른다(시뮬레이터에서 대략 두 배 안팎). 반면 연결 리스트는 다음 주소가 앞 적재 결과라 미스가 하나씩만 진행된다. ROB를 아무리 키워도 IPC가 거의 그대로다. 큰 창이 숨길 수 있는 것은 독립적인 기다림뿐이다.

시뮬레이터에서 ROB 머리에 빨간 칸(미스)이 걸리면 어떤 일이 일어나는지 지켜보자. 뒤의 명령들은 계속 들어와 실행되고 초록으로 바뀌지만 확정되지 못한다. ROB가 꽉 차는 순간 들여보내기가 멈추고 코어 전체가 서 버린다. 이것이 ROB가 커야 하는 이유이자, 커도 끝이 있는 이유다. DRAM 접근 한 번(100 ns 남짓)은 4 GHz 코어에서 400사이클이고, 8폭 코어라면 그동안 명령 3,200개를 처리할 수 있었다. 어떤 ROB도 그만큼 크지는 않다.

스펙터: 투기 실행의 그림자

비순차 코어는 분기 예측을 믿고 아직 확정되지 않은 명령을 미리 실행한다(투기 실행). 틀리면 결과는 버리지만, 그 사이 캐시에 남은 흔적까지 지우지는 않는다. 2018년 공개된 Spectre·Meltdown 공격은 이 흔적의 접근 시간을 재서 비밀 데이터를 빼냈다. 성능을 위한 추측이 보안 구멍이 될 수 있다는 교훈은 16장에서 다시 다룬다.

메모리 수준 병렬성: 기다림을 겹치기

큰 ROB가 주는 가장 큰 선물은 계산의 병렬성이 아니라 기다림의 병렬성이다. 캐시 미스 여러 개를 동시에 메모리로 보내 놓으면, 하나씩 기다릴 때보다 전체 대기 시간이 미스 수만큼 나누어진다. 이를 메모리 수준 병렬성(MLP, Memory-Level Parallelism)이라 한다. 아래는 서로 독립인 적재가 일정 간격으로 나오고 모두 DRAM까지 가는 극단적인 경우다. ROB 크기를 바꾸며 미스들이 얼마나 겹치는지 보자.

전체 사이클—
평균 동시 미스 (MLP)—
미스를 하나씩 기다렸다면—
그림 4-4. 만져 보기막대 하나가 미스 하나가 메모리를 기다리는 시간이다(4폭 코어, 미스 12개). ROB 크기 ÷ 미스 간격만큼의 미스가 한 창 안에 들어오므로 그만큼 겹친다. 실제로는 L1 캐시가 동시에 처리할 수 있는 미스 수(MSHR, 보통 10~20여 개)와 DRAM 대역폭도 한계가 된다(5장, 7장).
$$\text{MLP} \approx \min\!\left(\frac{\text{ROB 크기}}{\text{미스 간 명령 수}},\; \text{MSHR 수}\right), \qquad T_{\text{미스 총}} \approx \frac{N_{\text{miss}} \cdot L_{\text{miss}}}{\text{MLP}}$$

큰 코어의 값: Pollack의 법칙

비순차 실행의 장부들, 즉 ROB, 스케줄러, 이름 바꾸기 표, 물리 레지스터, 넓은 해독기와 큰 분기 예측기는 면적과 전력을 많이 먹는다. 게다가 이 장부들은 크기를 키울수록 비용이 빠르게 는다. 스케줄러는 매 사이클 모든 대기 명령을 동시에 비교해야 하고, 레지스터 파일은 읽기·쓰기 포트 수의 제곱으로 커진다. 인텔의 Fred Pollack은 경험적으로 코어의 성능은 면적의 제곱근에 비례한다고 정리했다. 면적을 4배로 하면 성능은 2배쯤이다. 이것이 Pollack의 법칙(Pollack's rule)이다.

그렇다면 같은 면적에 큰 코어 하나를 둘까, 작은 코어 여럿을 둘까? 답은 일이 얼마나 병렬화되는가에 달려 있다. Hill과 Marty(2008)는 이 질문을 암달의 법칙과 Pollack의 법칙으로 풀었다. 칩 면적이 작은 코어 \(n\)개분이고, 큰 코어 하나가 작은 코어 \(r\)개분 면적을 쓴다고 하자.

$$\text{perf}(r) = \sqrt{r}, \qquad S_{\text{대칭}} = \frac{1}{\dfrac{1-p}{\text{perf}(r)} + \dfrac{p\, r}{\text{perf}(r)\, n}}, \qquad S_{\text{비대칭}} = \frac{1}{\dfrac{1-p}{\text{perf}(r)} + \dfrac{p}{\text{perf}(r) + n - r}}$$
\(p\): 병렬화되는 일의 비율. 대칭은 크기 \(r\)인 코어 \(n/r\)개, 비대칭은 큰 코어(\(r\)) 1개 + 작은 코어 \(n-r\)개. 직렬 구간은 가장 빠른 코어 하나가, 병렬 구간은 모든 코어가 함께 한다.
큰 코어 하나의 성능—
대칭 구성 속도 향상—
비대칭 구성 속도 향상—
그림 4-5. 끌어 보기그래프 위를 좌우로 끌면 큰 코어 크기 \(r\)이 바뀐다. 칩 면적은 작은 코어 16개분이다. 왼쪽 그림은 비대칭 구성의 평면도다. \(p\)가 0.99쯤으로 높으면 작은 코어를 많이 두는 편이 낫고, 0.5처럼 낮으면 큰 코어가 이긴다. 비대칭(큰 코어 하나 + 작은 코어 여럿)은 거의 모든 \(p\)에서 대칭보다 낫다. 스마트폰 SoC가 큰 코어와 작은 코어를 섞는 출발점이 이 계산이다.
코어연도종류해독 폭ROB (명령)비고
Arm Cortex-A5202023효율 (순차)3—순차 실행, 두 코어가 벡터 장치를 공유할 수 있음
Arm Cortex-A7202023중간 (비순차)5약 160면적·전력 효율 중심의 "성능 코어"
Arm Cortex-X42023프라임 (비순차)10약 3848개 ALU 안팎, 큰 L2(최대 2 MB)
Apple Firestorm (A14/M1)2020큰 코어8약 630 (추정)공개 측정 기반 추정값
Intel Golden Cove2021데스크톱 큰 코어6512마이크로옵 캐시에서 최대 8개/사이클
AMD Zen 42022데스크톱 큰 코어4 (+옵 캐시)320옵 캐시에서 최대 9개/사이클

수치는 제조사 발표(Arm Tech Day, Hot Chips 등)와 공개 분석의 대략값이다. 비공개 설계(Apple)는 마이크로벤치마크로 추정한 값이라 오차가 클 수 있다.

big.LITTLE: 큰 코어와 작은 코어의 분업

스마트폰의 일은 극단적으로 들쭉날쭉하다. 대부분의 시간은 알림 확인, 음악 재생, 백그라운드 동기화 같은 가벼운 일이고, 가끔 앱을 열거나 화면을 휙 넘길 때 짧고 무거운 일이 몰린다. 큰 코어는 무거운 일을 빨리 끝내지만 가벼운 일에는 낭비가 크다. 작은 코어는 느리지만 같은 일을 훨씬 적은 에너지로 한다. 둘을 함께 두고 일에 따라 골라 쓰는 구조를 Arm은 big.LITTLE이라 불렀고, 지금은 거의 모든 스마트폰 SoC와 노트북 칩(Apple의 P·E 코어, 인텔의 P·E 코어)이 같은 생각을 쓴다. 기준 칩 SB-1에는 큰 코어 3개와 작은 코어 4개가 있다.

코어마다 전압과 클럭을 조절하는 DVFS(12장)를 생각하면 그림이 더 분명해진다. 각 코어에는 "이만큼의 성능을 내려면 이만큼의 전력이 든다"는 곡선이 있다. 요구 성능을 그래프 위에서 끌어 보며 어느 코어가 더 효율적인지 보자.

요구 성능—
작은 코어로—
큰 코어로—
일 하나의 에너지 비—
그림 4-6. 끌어 보기그래프 위를 좌우로 끌어 요구 성능(가로축)을 정하면, 두 코어가 그 성능을 내는 데 필요한 클럭과 전력(왼쪽), 일 단위 하나에 드는 에너지(오른쪽)를 보여 준다. 성능 단위는 "IPC × GHz"(1 ms에 하는 일의 양). 큰 코어는 IPC 3.0·최대 3.6 GHz, 작은 코어는 IPC 1.2·최대 2.0 GHz, 전압은 클럭에 따라 오른다고 가정한 교육용 모델이다. 작은 코어가 낼 수 있는 성능 범위 안에서는 작은 코어가 일 하나를 두세 배 적은 에너지로 끝낸다.

곡선에서 세 가지가 보인다. 첫째, 같은 코어라도 클럭을 올릴수록 일 하나당 에너지가 커진다(전압이 함께 오르므로 \(CV^2\)가 는다). 둘째, 낮은 성능에서는 작은 코어가 훨씬 효율적이다. 큰 코어는 클럭을 낮춰도 넓은 장부를 굴리는 데 드는 에너지와 누설 전력이 있다. 셋째, 작은 코어가 낼 수 없는 성능은 큰 코어만 낼 수 있다. 그래서 운영체제 스케줄러의 일은 "각 일에 필요한 성능을 내는 가장 효율적인 코어와 클럭"을 고르는 것이 된다. 리눅스(안드로이드)의 에너지 인식 스케줄링(EAS, Energy-Aware Scheduling)은 칩 제조사가 제공한 이런 에너지 모델을 보고 일을 배치한다.

SIMULATOR

이종 코어 스케줄러

상황
스케줄링 정책
CPU 에너지 (1초)—
평균 전력—
마감을 놓친 일—
평균 응답 시간—
1초 동안 일이 도착하고, 정책이 일을 코어에 배정한다. 각 코어는 맡은 일을 마감 안에 끝낼 만큼만 클럭을 올린다(목표 이용률이 낮을수록 여유 있게 높은 클럭). 막대 색은 일의 종류, 진하기는 클럭 높이다. 아래 그래프는 시간에 따른 CPU 전력이다. 에너지 인식 정책은 일마다 "작은 코어로 마감을 지킬 수 있는가"를 먼저 따지고, 안 되면 큰 코어를 쓴다. 코어는 쉴 때 전원을 끈다고 가정했다.
"모두 큰 코어"가 에너지를 가장 많이 쓸까?

큰 코어는 일을 빨리 끝내고 바로 잠들 수 있다("빨리 끝내고 쉬기", race-to-idle). 그렇다면 오히려 에너지가 적게 들 수도 있지 않을까?

답 보기

잠든 동안의 전력이 0에 가깝고 일이 아주 무겁다면 race-to-idle이 유리할 때도 있다. 하지만 스마트폰의 가벼운 일 대부분에서는 큰 코어가 낮은 클럭으로 돌아도 작은 코어보다 일 하나당 에너지가 크다(그림 4-6). 시뮬레이터의 "음악 + 동기화"에서 두 정책을 비교해 보자. 반대로 "3D 게임"처럼 무거운 일에서 "모두 작은 코어"를 고르면 에너지는 적어도 마감을 줄줄이 놓친다(프레임이 끊긴다). 정답은 일마다 다르고, 그래서 스케줄러가 필요하다.

스마트폰 SoC의 코어 구성

2020년대 플래그십 SoC는 보통 "1 + 3 + 4"(프라임 1, 성능 3, 효율 4)나 "1 + 5 + 2" 같은 3단 구성을 쓴다. 프라임 코어는 한 스레드 성능(앱 실행, 웹 자바스크립트)을, 성능 코어는 여러 스레드가 필요한 일을, 효율 코어는 배경 일을 맡는다. 최근에는 효율 코어를 아예 빼고 중간 크기 코어를 여럿 두는 설계도 나왔다. 정답이 고정된 것이 아니라 일의 모양과 공정에 따라 계속 움직인다는 뜻이다.

핵심 정리

  1. 데이터 흐름 그래프의 임계 경로가 ILP의 상한을 정한다. 병렬성은 멀리 흩어져 있어, 많은 명령을 한꺼번에 들여다봐야 찾을 수 있다.
  2. 순차 슈퍼스칼라는 앞 명령이 막히면 함께 멈춰 폭 2~3에서 포화된다. 비순차 실행은 준비된 명령부터 실행해 ILP 한계에 다가간다.
  3. 레지스터 이름 바꾸기는 WAR·WAW 거짓 의존을 없앤다. ROB는 결과를 순서대로 확정해 정밀 예외와 분기 복구를 가능하게 한다.
  4. 큰 ROB의 핵심 이득은 독립적인 캐시 미스를 겹치는 메모리 수준 병렬성이다. 의존적인 미스(연결 리스트)는 창이 커도 숨길 수 없다.
  5. 성능은 면적의 제곱근에 비례하므로(Pollack), 큰 코어와 작은 코어를 섞고 DVFS 곡선을 아는 스케줄러가 일마다 가장 효율적인 코어를 고르는 것이 이득이다.

확인 퀴즈

Q1. 명령 12개로 이루어진 코드의 데이터 흐름 그래프에서 임계 경로가 4사이클이다. 자원이 무한할 때 이상적인 IPC는?

명령 12개를 4사이클 안에 끝낼 수 있으므로 12 ÷ 4 = 3이다. 실제 IPC는 발행 폭, 창 크기, 계산 장치 수에 막혀 이보다 낮다.

Q2. 다음 중 레지스터 이름 바꾸기로 없앨 수 없는 의존은?

RAW는 실제로 값이 흘러가는 참 의존이라 이름을 바꿔도 사라지지 않는다. WAR·WAW는 이름 충돌이라 물리 레지스터를 새로 주면 사라진다.

Q3. 비순차 코어가 실행은 순서 없이 하면서도 확정(commit)은 프로그램 순서대로 하는 주된 이유는?

ROB에 남아 있는 미확정 결과는 언제든 버릴 수 있으므로, 어떤 명령에서 문제가 생기든 그 앞까지만 반영된 상태로 돌아갈 수 있다.

Q4. 독립적인 캐시 미스가 명령 32개마다 하나씩 나온다. ROB가 256이고 다른 제약이 없다면 동시에 진행되는 미스는 대략 몇 개인가?

ROB 창 256개 안에 미스가 256 ÷ 32 = 8개 들어오므로 그만큼 겹친다. 실제로는 MSHR 수와 메모리 대역폭도 한계가 된다.

Q5. Pollack의 법칙에 따르면 코어 면적을 9배로 키웠을 때 단일 스레드 성능은 대략?

성능 ∝ √면적 이므로 √9 = 3배. 같은 면적이면 작은 코어 9개가 병렬 일에서 9배를 낼 수 있으니, 일의 병렬성에 따라 선택이 갈린다.

Q6. 에너지 인식 스케줄러가 가벼운 배경 일을 작은 코어에 보내는 근거로 가장 알맞은 것은?

DVFS 곡선에서 작은 코어가 낼 수 있는 성능 범위 안에서는 작은 코어의 일당 에너지가 큰 코어보다 낮다. 마감을 못 지킬 때만 큰 코어로 올린다.