CPU 시간 식 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
# 03.CPU 시간 식: 문서의 예제, 표, 카드 답을 분수로 정확히 확인한다.
# 실행: python3 03_cpu-time-equation_verify.py (표준 라이브러리만 쓴다)
from fractions import Fraction as F
import random
PS = F(1, 10**12)
NS = F(1, 10**9)
def cpu_time(ic, cpi, tc):
"""CPU 시간 = 명령어 수 x CPI x 클록 주기"""
return F(ic) * F(cpi) * F(tc)
def avg_cpi(mix):
"""mix: [(비율 또는 개수, CPI)] -> 가중 평균 CPI"""
total = sum(F(c) for c, _ in mix)
return sum(F(c) * F(k) for c, k in mix) / total
if __name__ == "__main__":
# 예시: 명령어 20억 개, CPI 1.5, 2.5 GHz
tc = 1 / F(25 * 10**8)
assert tc == 400 * PS
assert cpu_time(2 * 10**9, F(3, 2), tc) == F(6, 5) # 1.2초
assert 2 * 10**9 * F(3, 2) == 3 * 10**9 # 30억 사이클
# 주기와 주파수
assert 1 / F(4 * 10**9) == 250 * PS
# 예제 1: A(200 ps, CPI 2.5), B(350 ps, CPI 1.2), 같은 명령어 집합
ta = F(5, 2) * 200
tb = F(6, 5) * 350
assert ta == 500 and tb == 420
assert ta / tb == F(25, 21) and abs(float(ta / tb) - 1.19) < 0.001
# 예제 2: 명령어 섞임 -> 평균 CPI
mix = [(F(45, 100), 1), (F(25, 100), 3), (F(10, 100), 2), (F(20, 100), 2)]
assert avg_cpi(mix) == F(9, 5) # 1.8
parts = [F(45, 100) * 1, F(25, 100) * 3, F(10, 100) * 2, F(20, 100) * 2]
assert parts == [F(45, 100), F(75, 100), F(20, 100), F(40, 100)]
assert parts[1] / F(9, 5) == F(5, 12) # 적재가 시간의 41.7%
# 적재를 3 -> 2 사이클로 줄이면 CPI 1.55, 1.161배
mix2 = [(F(45, 100), 1), (F(25, 100), 2), (F(10, 100), 2), (F(20, 100), 2)]
assert avg_cpi(mix2) == F(31, 20)
assert F(9, 5) / F(31, 20) == F(36, 31)
# 예제 3: 두 가지 코드. 종류 A/B/C의 CPI = 1/2/4
seq1 = [(3, 1), (2, 2), (1, 4)]
seq2 = [(6, 1), (1, 2), (0, 4)]
c1 = sum(n * k for n, k in seq1)
c2 = sum(n * k for n, k in seq2)
assert (c1, c2) == (11, 8)
assert avg_cpi(seq1) == F(11, 6) and avg_cpi(seq2) == F(8, 7)
# MIPS 값의 함정: 4 GHz, 컴파일러 1(50억, CPI 1.2) vs 컴파일러 2(80억, CPI 1.0)
f = 4 * 10**9
t1 = cpu_time(5 * 10**9, F(6, 5), F(1, f))
t2 = cpu_time(8 * 10**9, 1, F(1, f))
assert t1 == F(3, 2) and t2 == 2
m1 = F(5 * 10**9) / (t1 * 10**6)
m2 = F(8 * 10**9) / (t2 * 10**6)
assert m1 == F(10000, 3) and m2 == 4000 and m2 > m1 and t2 > t1
assert m1 == F(f) / (F(6, 5) * 10**6) # MIPS = 클록 속도 / (CPI x 10^6)
# 오해 항목: 3 GHz에 CPI 2.4 vs 2 GHz에 CPI 1.2
assert F(24, 10) / 3 == F(4, 5) and F(12, 10) / 2 == F(3, 5)
assert F(4, 5) / F(3, 5) == F(4, 3)
# 스스로 설명: 식의 단위가 초로 떨어진다 (명령어 x 사이클/명령어 x 초/사이클)
# 무작위로: 시간 = 사이클 수 x 주기 = 사이클 수 / 주파수
rnd = random.Random(1)
for _ in range(1000):
ic = rnd.randint(1, 10**10)
cpi = F(rnd.randint(1, 50), 10)
fr = rnd.randint(10**8, 5 * 10**9)
assert cpu_time(ic, cpi, F(1, fr)) == ic * cpi / fr
# 카드 C2: 15억 개, CPI 2, 주기 250 ps -> 0.75초
assert cpu_time(15 * 10**8, 2, 250 * PS) == F(3, 4)
# 카드 C3: 클록을 3 GHz -> 4 GHz로 올렸더니 CPI가 1.5 -> 2.1
assert F(15, 10) / 3 == F(1, 2) and F(21, 10) / 4 == F(21, 40)
assert F(21, 40) / F(1, 2) == F(21, 20) # 5% 느려진다
# 카드 C4: 10초, 2 GHz -> 6초에 끝내려는데 사이클이 1.5배 든다 -> 5 GHz
cycles = 10 * 2 * 10**9
assert F(cycles * 3, 2) / 6 == 5 * 10**9
# 카드 C5: 새 컴파일러가 명령어 수를 0.8배로, CPI를 1.1배로
assert F(8, 10) * F(11, 10) == F(22, 25) and 1 / F(22, 25) == F(25, 22)
print("ALL CHECKS PASSED")