Workspace IndexAlgorithms › Day 17

Parallel Algorithm Models TODO

Algorithms · Day 17 / 100 · A. Advanced Algorithms & Data Structures (Day 1-19)

Concept

The work-span model views a parallel computation as a DAG and summarizes it with two numbers. Work T1 is the total amount of computation (the time on a single processor); span T-infinity is the length of the critical path of dependencies (the time you can't shrink even with infinite processors); parallelism is defined as T1/T-infinity. A good scheduler (e.g., work-stealing) guarantees that Tp is roughly T1/p + T-infinity, so parallelism needs to be comfortably larger than p to approach linear scaling. Amdahl's law says that for a fixed problem size, a serial fraction s caps the speedup at 1/s. Gustafson's law instead assumes that as processors increase, people scale up the problem size too, so the work done in a fixed time grows roughly proportional to the number of processors. The two aren't contradictory — they just hold different things fixed.

When adding cores doesn't improve performance, failing to tell apart whether the cause is the serial portion, the critical path, or scheduling overhead leads to tuning the wrong thing.

Code & Formula

# 병렬 알고리즘 모델 — DAG의 work(T1)/span(T∞)을 계산하고, Amdahl vs Gustafson 속도향상을 비교한다.

from collections import defaultdict

# 태스크: id -> (실행시간, 선행 태스크 목록)
tasks = {
    "a": (2, []), "b": (3, ["a"]), "c": (1, ["a"]),
    "d": (4, ["b"]), "e": (2, ["c"]), "f": (1, ["d", "e"]),
}

def topo_order():
    indeg = {k: len(v[1]) for k, v in tasks.items()}
    children = defaultdict(list)
    for k, (_, ds) in tasks.items():
        for d in ds:
            children[d].append(k)
    ready = [k for k, v in indeg.items() if v == 0]
    order = []
    while ready:
        n = ready.pop()
        order.append(n)
        for c in children[n]:
            indeg[c] -= 1
            if indeg[c] == 0:
                ready.append(c)
    return order

order = topo_order()
work = sum(d for d, _ in tasks.values())            # T1: 프로세서 1개로 걸리는 총 시간
finish = {}
for t in order:
    start = max((finish[d] for d in tasks[t][1]), default=0)
    finish[t] = start + tasks[t][0]
span = max(finish.values())                          # T∞: 임계 경로(의존성 사슬) 길이
parallelism = work / span

def amdahl(p, serial_fraction):
    return 1 / (serial_fraction + (1 - serial_fraction) / p)

def gustafson(p, serial_fraction):
    return p - serial_fraction * (p - 1)

print(f"work T1={work}, span T∞={span}, 병렬성 T1/T∞={parallelism:.2f}")
for p in (1, 2, 4, 8):
    lower_bound = max(work / p, span)                 # 좋은 스케줄러가 보장하는 하한
    print(f"p={p}: 하한 Tp>={lower_bound:.2f}, "
          f"Amdahl(s=0.1)={amdahl(p, 0.1):.2f}x, Gustafson(s=0.1)={gustafson(p, 0.1):.2f}x")

Exercise

Compute the work and span of a parallel merge sort or parallel prefix sum by hand, then in Go increase the number of goroutines from 1 up to the core count and overlay the measured speedup curve against the prediction.

Practical Connection

This is the basis for judging how far parallelization pays off in block-execution parallelism (where inter-transaction state dependencies form the DAG's edges) or in Verex's batch settlement and off-chain matching engine.

If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/dev-100-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.


한국어

병렬 알고리즘 모델 TODO

Algorithms · Day 17 / 100 · A. 고급 알고리즘·자료구조 (Day 1–19)

work-span, Amdahl vs Gustafson

개념

work-span 모델은 병렬 계산을 DAG로 보고 두 값으로 요약한다. work T1은 전체 연산량(프로세서 1개로 걸리는 시간), span T∞는 의존성 사슬의 임계 경로 길이(프로세서가 무한히 많아도 못 줄이는 시간)이며, 병렬성은 T1/T∞로 정의된다. 좋은 스케줄러(예: work-stealing)는 Tp가 대략 T1/p + T∞ 수준임을 보장하므로, 병렬성이 p보다 충분히 커야 선형 스케일에 가까워진다. Amdahl의 법칙은 문제 크기를 고정한 채 직렬 비율 s가 있으면 속도향상이 1/s로 상한이 걸린다고 말한다. Gustafson의 법칙은 프로세서가 늘면 사람들이 문제 크기도 함께 키운다고 가정해, 고정 시간 안에 처리할 수 있는 일이 프로세서 수에 거의 비례해 늘어난다고 본다. 둘은 모순이 아니라 '무엇을 고정하느냐'가 다른 관점이다.

코어를 늘렸는데 성능이 안 오를 때, 원인이 직렬 구간인지 임계 경로인지 스케줄링 오버헤드인지 구분하지 못하면 엉뚱한 곳을 튜닝하게 된다.

코드 · 수식

# 병렬 알고리즘 모델 — DAG의 work(T1)/span(T∞)을 계산하고, Amdahl vs Gustafson 속도향상을 비교한다.

from collections import defaultdict

# 태스크: id -> (실행시간, 선행 태스크 목록)
tasks = {
    "a": (2, []), "b": (3, ["a"]), "c": (1, ["a"]),
    "d": (4, ["b"]), "e": (2, ["c"]), "f": (1, ["d", "e"]),
}

def topo_order():
    indeg = {k: len(v[1]) for k, v in tasks.items()}
    children = defaultdict(list)
    for k, (_, ds) in tasks.items():
        for d in ds:
            children[d].append(k)
    ready = [k for k, v in indeg.items() if v == 0]
    order = []
    while ready:
        n = ready.pop()
        order.append(n)
        for c in children[n]:
            indeg[c] -= 1
            if indeg[c] == 0:
                ready.append(c)
    return order

order = topo_order()
work = sum(d for d, _ in tasks.values())            # T1: 프로세서 1개로 걸리는 총 시간
finish = {}
for t in order:
    start = max((finish[d] for d in tasks[t][1]), default=0)
    finish[t] = start + tasks[t][0]
span = max(finish.values())                          # T∞: 임계 경로(의존성 사슬) 길이
parallelism = work / span

def amdahl(p, serial_fraction):
    return 1 / (serial_fraction + (1 - serial_fraction) / p)

def gustafson(p, serial_fraction):
    return p - serial_fraction * (p - 1)

print(f"work T1={work}, span T∞={span}, 병렬성 T1/T∞={parallelism:.2f}")
for p in (1, 2, 4, 8):
    lower_bound = max(work / p, span)                 # 좋은 스케줄러가 보장하는 하한
    print(f"p={p}: 하한 Tp>={lower_bound:.2f}, "
          f"Amdahl(s=0.1)={amdahl(p, 0.1):.2f}x, Gustafson(s=0.1)={gustafson(p, 0.1):.2f}x")

연습

병렬 머지소트나 병렬 prefix sum의 work와 span을 손으로 계산한 뒤, Go에서 goroutine 수를 1부터 코어 수까지 늘려가며 실측 속도향상 곡선을 예측치와 겹쳐 그려 보라.

실무 · Verex 연결

블록 실행 병렬화(트랜잭션 간 상태 의존성이 곧 DAG의 간선)나 Verex의 배치 정산·오프체인 매칭 엔진에서 어디까지 병렬화 이득이 나는지 판단하는 기준이 된다.

공부한 날 원본 커리큘럼(docs/knowledge/dev-100-curriculum.md)의 이 줄에 노트 링크와 ✅ 를 붙이면, 이 자리는 노트 본문으로 바로 이어집니다. 노트 없이 이 페이지에 바로 적어도 됩니다 — 다만 다시 생성하면 덮어쓰이므로, 남길 글은 docs/algorithms/ 의 마크다운으로 쓰는 편이 안전합니다.

← 16. NP-난해와 환원18. 조합 생성·그레이 코드·순열 열거 (TAOCP 4권) →