슈퍼스칼라와 비순차 실행 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.
# 한 사이클에 여러 명령어를 시작하는 프로세서의 장난감 일정표 (실제 하드웨어가 아니라 파이썬 흉내다)
# 실행: python3 34_superscalar-out-of-order_verify.py
# 가정: 명령어마다 실행에 1사이클(lw는 2사이클)이 들고, 결과는 끝난 다음 사이클부터 쓸 수 있다.
# 가져오기·해독·확정 단계는 세지 않고 '실행을 시작하는 사이클'만 센다.
PROG = [ # (이름, 쓰는 레지스터, 읽는 레지스터들, 걸리는 사이클)
("lw $t0, 0($s0)", "t0", ["s0"], 2),
("add $t1, $t0, $s1", "t1", ["t0"], 1),
("sub $t2, $t1, $s2", "t2", ["t1"], 1),
("lw $t3, 4($s0)", "t3", ["s0"], 2),
("add $t4, $t3, $s1", "t4", ["t3"], 1),
("or $t5, $t4, $t2", "t5", ["t4", "t2"], 1),
]
def schedule(prog, width, out_of_order):
"""돌려주는 것: 명령어마다 실행을 시작한 사이클(1부터)."""
ready_at = {} # 레지스터 -> 값을 쓸 수 있는 첫 사이클
start = [None] * len(prog)
cycle = 0
while None in start:
cycle += 1
issued = 0
for i, (_, dst, srcs, lat) in enumerate(prog):
if start[i] is not None:
continue
# 앞선 명령어가 만들 값이면 그 값이 나올 때까지 기다린다
producers_done = True
for s in srcs:
prod = [j for j in range(i) if prog[j][1] == s]
if prod:
j = prod[-1]
if start[j] is None or start[j] + prog[j][3] > cycle:
producers_done = False
if producers_done and issued < width:
start[i] = cycle
issued += 1
elif not out_of_order:
break # 순서대로만: 앞이 막히면 뒤도 못 간다
return start
def finish(prog, start):
return max(s + p[3] - 1 for s, p in zip(start, prog))
if __name__ == "__main__":
s1 = schedule(PROG, 1, False)
s2 = schedule(PROG, 2, False)
s3 = schedule(PROG, 2, True)
assert s1 == [1, 3, 4, 5, 7, 8] and finish(PROG, s1) == 8
assert s2 == [1, 3, 4, 4, 6, 7] and finish(PROG, s2) == 7
assert s3 == [1, 3, 4, 1, 3, 5] and finish(PROG, s3) == 5
# 사이클당 명령어 수(IPC) = 명령어 수 / 사이클 수
assert round(6 / 8, 2) == 0.75 and round(6 / 7, 2) == 0.86 and round(6 / 5, 2) == 1.2
# 폭을 4로 늘려도 5사이클 밑으로는 못 간다: lw -> add -> sub -> or 사슬이 2 + 1 + 1 + 1 = 5사이클
assert finish(PROG, schedule(PROG, 4, True)) == 5 and finish(PROG, schedule(PROG, 100, True)) == 5
# 서로 기대지 않는 명령어 6개라면 폭 2로 3사이클, IPC 2
indep = [(f"add $t{i}", f"t{i}", ["s0"], 1) for i in range(6)]
assert finish(indep, schedule(indep, 2, False)) == 3 and finish(indep, schedule(indep, 1, False)) == 6
# 줄줄이 기대는 명령어 6개라면 폭이 아무리 넓어도 6사이클
chain = [(f"add $t{i}", f"t{i}", [f"t{i-1}"] if i else ["s0"], 1) for i in range(6)]
assert finish(chain, schedule(chain, 4, True)) == 6
print("ALL CHECKS PASSED")