gpumode · 강의 아카이브
《GPU Mode》 L062 2025 · Scheduling DSL High priority transcript · failed

Exo 2 — Growing a scheduling language

Halide 가 깐 algorithm 과 schedule 의 분리 를 — 그 schedule 자체를 사용자 정의 가능한 라이브러리 로 만든 MIT 의 DSL. Exo 의 핵심 아이디어는 “scheduling language 는 fixed 가 아니라 grown 된다” 는 것 — 새 hardware 가 등장하면 컴파일러를 갱신하지 않고 스케줄과 instruction 을 사용자가 추가한다. Yuka Ikarashi 가 Exo 2 에서 깐 새 기능과 검증 모델, 그리고 Halide / TVM 과의 차이를 정리한 노트.

scheduling DSL algorithm/schedule split Exo user-defined instructions Halide TVM verified rewrite accelerator codegen
Y
Speaker
Yuka Ikarashi
MIT CSAIL · Exo 2 lead author
강의 번호
L062
스피커
Yuka Ikarashi
학습 우선순위
High · 정독
자료 상태
transcript 없음 · 논문 기반
§ 01강의가 풀려는 문제· why grow a scheduling language

새 hardware 가 나올 때마다 컴파일러를 다시 짜지 않으려면 — schedule 을 grow 한다

2024–25 시점의 hardware 풍경 — NVIDIA H100/B200, AMD MI300, Apple AMX, Google TPU, AWS Trainium, Meta MTIA, 그리고 D-Matrix / Groq / Cerebras / Tenstorrent 의 dataflow 칩. 각자 자기 ISA 와 자기 memory hierarchy 를 갖는다. 컴파일러가 모두 따라가는 건 불가능. Exo 의 답 — schedule 과 instruction 을 사용자가 라이브러리로 추가할 수 있게 한다.

강의 transcript 가 비어 있으므로 본 노트는 Exo 의 PLDI 2022 paper, Exo 2 의 후속 paper (Ikarashi et al. 2024+), exo-lang.dev 자료, github.com/exo-lang/exo repo 위에서 재구성한다. 강의에서 Yuka 가 강조한 정확한 demo / 채택 사례는 영상 검증 필요.

Exo 의 frame

기존 컴파일러 (Halide, TVM, MLIR) 의 schedule 과 lowering 은 컴파일러 코드 안에 박혀 있다. Exo 는 — 그 schedule rewrite 를 Python library 로 노출. 사용자가 자기 hardware 에 맞는 새 transformation 을 정의하고, 새 ISA instruction 을 추가하고, 그 결합으로 자기 커널을 짠다. 컴파일러 자체가 “growable” 한 것.

“scheduling language 는 fixed 일 필요 없다 — 사용자가 자기 hardware 에 맞게 키워가는 라이브러리가 될 수 있다.”Exo 의 thesis · 확인 필요
§ 02scheduling language 의 가치· what schedules buy you

Halide 의 큰 idea — “same algorithm, many schedules” 가 왜 중요한가

Halide (2012, MIT/Stanford) 가 깐 큰 idea — image / tensor 코드의 알고리즘 정의 (무엇을 계산하는가) 와 schedule (어떻게 도는가) 을 분리. tile size, loop order, vectorization, parallelization 을 하나의 큰 transformation 라이브러리로 표현. 같은 알고리즘이 — schedule 만 바꾸면 — CPU 의 SIMD, GPU 의 thread, ARM 의 NEON 을 다 쓸 수 있다.

측면
전통 C/CUDA
Halide / TVM
Exo
algorithm/schedule 분리 섞여 있음 분리 분리 + schedule 도 라이브러리화
새 hardware 추가 새 backend 코드 컴파일러 fork + 새 lowering 사용자가 new instruction + schedule lib 추가
정확성 보장 없음 (테스트만) 제한적 (타입 / scope) verified rewrite (semantics 보존)
표적 hardware 다양 주로 CPU / GPU / 일부 accel accel · DSP · 새 ISA · GEMMINI 등
사용자 학습 곡선 중간 중상 상 (PhD-친화적)

Halide 의 한계는 — schedule 의 set 이 컴파일러에 박혀 있다는 점. split, tile, vectorize, parallel, fuse 정도. 새 hardware 의 특수 instruction (예: GEMMINI 의 systolic array load) 을 표현하려면 컴파일러를 수정해야 한다. Exo 는 이 자리를 그 transformation 도 라이브러리화하는 식으로 푼다.

§ 03algorithm vs schedule· Exo 의 separation

Exo 의 두 단계 — proc 정의 + 그 위의 schedule transformation

Exo 의 입력은 Procedure (proc) — Python decorator 안의 함수가 ImperativeIR 로 lowering. 처음에는 단순한 reference algorithm — naive nested loop 의 GEMM, conv. 그 다음 같은 proc 위에 schedule transformation 을 chain. 각 transformation 이 새 proc 를 반환 (functional).

# Exo 의 reference GEMM
@proc
def gemm(M:size, N:size, K:size,
         A:f32[M,K], B:f32[K,N], C:f32[M,N]):
    for i in seq(0, M):
        for j in seq(0, N):
            for k in seq(0, K):
                C[i,j] += A[i,k] * B[k,j]
# schedule 의 chain — 각 단계가 새 proc 반환
g = gemm
g = divide_loop(g, "i", 16, ["io", "ii"])
g = divide_loop(g, "j", 16, ["jo", "ji"])
g = reorder_loops(g, "ii jo")
g = stage_mem(g, "C[ii,ji]", "Cb")
g = call_eqv(g, "Cb", gemm_tile_inst)

각 함수 — divide_loop, reorder_loops, stage_mem — 가 Exo 의 표준 transformation 라이브러리에 있는 한 종류. 모두 입력 proc 을 받아 출력 proc 을 돌려주는 순수 함수.

  • divide_loop — loop 를 outer / inner 로 split (tile 만들기).
  • reorder_loops — nested loop 의 순서 교환.
  • stage_mem — 특정 access 를 임시 buffer 에 stage (= shared memory 처럼).
  • call_eqv — 어떤 inner proc 가 사용자 정의 instruction 과 동치임을 알리고 그 instruction 으로 치환.
cursor 라는 핵심 추상

Exo 2 의 새 기능 — cursor. 코드의 특정 위치 (예: 안 쪽 loop, 특정 statement) 를 가리키는 1급 객체. transformation 이 cursor 를 받고 cursor 를 반환. 사용자 정의 schedule 라이브러리를 만들 때 이 cursor 가 필요한 robust composition 을 제공.

FIG · schedule transformation pipelinesame algorithm, multi-step rewrite
L0 · ALGORITHM naive nested loop GEMM. O(MNK) 의 단순 reference. 어떤 hw 도 비효율.
L1 · TILE divide_loop(i, 16, ...), divide_loop(j, 16, ...) 로 16×16 tile 구조. cache 친화.
L2 · STAGE MEM tile 안 C 를 register 또는 SRAM 에 stage. stage_mem(g, "C[ii,ji]", "Cb").
L3 · INSTR tile-level GEMM 을 hw-specific instruction 으로 치환 — call_eqv(g, ..., gemm_tile_inst).
L4 · CODEGEN Exo 가 C 코드 (또는 hw-specific) 를 emit. 사용자 instruction 의 textual form 이 그대로 드러남.
매 단계가 같은 알고리즘의 다른 표현. semantic equivalence 가 보존되는 transformation 만 통과. 각 단계의 proc 를 사용자가 직접 inspect / debug 가능.
§ 04Exo 2 의 새 기능· cursor · scheduling lib

Exo 1 → Exo 2 — 사용자가 자기 schedule 라이브러리를 짤 수 있게

Exo 1 (PLDI 2022) 이 깐 idea — algorithm/schedule 분리 + 사용자 정의 instruction. Exo 2 는 한 단계 더 — schedule transformation 자체도 사용자가 정의하는 함수 가 되게 한다. 즉 “tile + stage + replace_with_inst” 같은 자주 쓰이는 패턴을 사용자가 자기 라이브러리로 묶고, 다른 사용자가 import 해서 쓴다.

Exo 1 — primitive set 고정

built-in transformation 만

divide_loop, reorder_loops, stage_mem 같은 ~20 개의 built-in. 사용자는 이걸 chain 만 한다. 새 패턴이 생기면 결국 컴파일러를 fork.

한계 — 같은 8 step 의 chain 을 매번 반복. 추상화 안 됨.

Exo 2 — schedule lib 가능

cursor + composable schedule

cursor 추상 + 사용자 정의 schedule 함수. 자기 hw 의 conventional pattern (예: “tile-stage-load-vec-mma”) 을 한 함수로 묶음.

새 가용성 — 같은 hw 그룹 안에서 schedule lib 공유. community / vendor 가 자기 hw 의 schedule 을 sub-library 로 publish.

cursor — Exo 2 의 새 1급 객체

cursor 는 proc 안의 특정 위치 (loop, statement, expression 의 특정 instance) 를 가리킨다. transformation 이 cursor 를 받아 — 그 자리에서 rewrite. 같은 변환을 여러 자리에 반복 적용할 때, naming 만으로 파악되지 않는 위치를 정확히 가리킨다.

# Exo 2 — cursor 를 받는 사용자 schedule
def tile_and_vectorize(p, loop_cursor, tile_size):
    p = divide_loop(p, loop_cursor, tile_size,
                    ["o", "i"])
    p = vectorize(p, p.find_loop("i"))
    return p

# 같은 schedule 을 여러 loop 에 적용
g = gemm
g = tile_and_vectorize(g, g.find_loop("i"), 16)
g = tile_and_vectorize(g, g.find_loop("j"), 8)

이 추상화 덕에 — 한 hw 그룹의 community 가 모여 “ARM Neon 의 conv2d 표준 schedule”, “GEMMINI 의 dense GEMM schedule” 같은 라이브러리를 만들 수 있다. 컴파일러를 수정하지 않고.

§ 05사용자 정의 instruction· Custom ISA

외부 hardware 의 instruction 을 Exo 안에서 표현 — 그리고 schedule 이 그 instruction 으로 lowering

Exo 의 다른 핵심 기능 — instruction 도 사용자 정의. 어떤 hw 의 inner-loop tile 처리 instruction 을 — Exo 안에서 reference Python 함수로 정의 + 그 instruction 의 C/asm 표현을 명시. schedule 은 이 instruction 을 호출하는 형태로 lowering.

# 예 — 가상의 16×16 systolic GEMM tile instruction
@instr("my_tile_mma_16x16(&{C_data}, &{A_data}, &{B_data})")
def gemm_tile_inst(C: f32[16,16] @ Mem,
                   A: f32[16,16] @ Mem,
                   B: f32[16,16] @ Mem):
    # reference semantics — 이게 hw 가 하는 일
    for i in seq(0, 16):
        for j in seq(0, 16):
            for k in seq(0, 16):
                C[i,j] += A[i,k] * B[k,j]

이 정의는 두 가지를 한다 — (1) semantics: instruction 이 정확히 어떤 결과를 만드는지. Exo 가 schedule 의 정확성을 검증할 때 이 reference 를 쓴다. (2) codegen: 실제 emit 시 instruction 의 textual form (C 호출 / asm) 으로 나간다.

memory annotation @Mem

Exo 는 buffer 가 어디에 살고 있는지 (HBM, SRAM, register) 를 type 차원에서 추적. instruction 이 “이 16×16 buffer 가 systolic 의 register 에 있어야 한다” 같은 제약을 표현 가능. schedule 의 stage_mem 이 이 위치를 정확히 만들어준다. 잘못된 위치에 있으면 컴파일이 거부.

그래서 새 hw 를 지원할 때 사용자가 하는 일 — (a) 그 hw 의 instruction 을 @instr 로 모두 정의, (b) 그 hw 의 memory hierarchy (HBM, SRAM, scratchpad) 를 annotation 으로 정의, (c) 자기 알고리즘을 그 instruction 으로 lowering 시키는 schedule chain 을 짠다. 컴파일러는 손 안 댄다.

§ 06검증· verified rewrite

모든 transformation 이 semantics 를 보존한다 — 잘못된 schedule 은 거부된다

Exo 의 정확성 보장 — 모든 schedule transformation 이 semantic-preserving. divide_loop, reorder_loops, stage_mem 같은 각 primitive 가 정형적으로 검증된 rewrite. 사용자가 잘못된 위치에 transformation 을 걸면 컴파일러가 그 자리에서 거부.

예를 들어 — reorder_loops 가 두 loop 의 순서를 바꾸려는데 — 그 순서 교환이 dependency 를 깨면 (예: 안쪽이 바깥쪽의 update 에 의존), Exo 가 거부한다. “이 reorder 는 dependence 를 위반함” 이라는 정확한 에러 메시지.

  • data dependence 분석: 두 loop 의 array access pattern 을 정확히 본다.
  • scope safety: stage_mem 이 만든 buffer 가 read-before-write 를 안 깨도록.
  • memory annotation 일치: instruction 의 입력이 정확한 위치 (Mem, Reg, etc.) 에 있는지.
  • type correctness: tile 크기 match, alignment.
왜 이게 큰 차이인가

Halide / TVM 의 schedule 도 어느 정도 검증을 하지만 — 사용자 정의 transformation 이 들어가는 순간 그 보장이 약해진다. Exo 는 새 transformation 을 정의할 때도 같은 정확성 framework 안에서 정의된다. 즉 community / vendor 가 라이브러리를 publish 해도 그게 wrong code 를 만들 수 없다. scaling trust.

이 framework 는 PLDI 22 paper 의 본격 contribution. “effects” 라는 abstraction 이 핵심 — 각 instruction 이 어떤 buffer 의 어떤 영역을 read/write 하는지를 명시. transformation 이 이 effects 를 보존하는지 자동 검증.

“Exo 의 약속은 — 사용자가 raw C 를 직접 짜지 않으면서도, 그 만큼의 hw 통제력을 갖되, 그 통제가 safe 하다는 것.”Exo 의 thesis · 확인 필요
§ 07채택 사례· GEMMINI · Apple AMX

Exo 가 실제로 코드 만들고 있는 hardware

Exo paper 들이 보여준 실험 / production 사례 (확인 필요 — 강의에서 다룬 정확한 채택 list).

GEMMINI (UC Berkeley)
Chipyard 의 systolic-array accelerator. Exo 가 conv2d / GEMM schedule 을 정의해 GEMMINI 의 ISA 로 lowering. reference RISC-V baseline 대비 큰 speedup.
Apple AMX
M-series 의 행렬 unit. Apple 의 closed ISA — Exo 로 schedule 가능 (확인 필요). Halide 로는 어려운 자리.
x86 AVX-512
표준 SIMD ISA. Exo 가 다른 컴파일러와 비교 baseline 에서 자주 사용. MKL 수준 성능 도달 (확인 필요).
ARM Neon / SVE
모바일 / server CPU 의 SIMD. Exo 의 schedule 라이브러리 채택 사례.
RISC-V Vector ext
변동 vector length 의 새 ISA. Exo 가 schedule 의 portability 를 보여주는 platform.
GPU (CUDA / Metal)
아직은 main target 아님. SIMT 모델이 Exo 의 SIMD 모델과 좀 다름 — 확장 진행 중 (확인 필요).

Exo 의 niche 는 — GPU 보다 작고, GPU 보다 다양한 hardware. accelerator / DSP / 새 ISA. 그 영역에서 커널 한 개 짜는 게 큰 일이 되는 자리에서 Exo 가 효율을 낸다.

§ 08Halide / TVM 과 차이· comparison

같은 가족 안 다른 형제들 — 무엇이 다른가

기능
Halide
TVM
Exo
algorithm/schedule 분리 예 (원조)
사용자 정의 schedule 제한적 제한적 (template) full library
사용자 정의 instruction backend extension tensor intrinsic @instr · 핵심 디자인
정확성 검증 제한적 제한적 verified rewrite
표적 hw image · CPU · GPU 다양 (GPU 강함) accel · custom · DSP
자동 search autoschedule (제한) AutoTVM, Ansor 없음 (manual)
학습 곡선 중간 중상

Exo 가 자동 search 를 안 한다는 점은 의도적 — Exo 는 전문가가 정확히 통제하는 도구. 자동 search 는 다른 layer (예: TVM 의 Ansor 처럼) 에서 한다는 분리. 강의 후속의 L063 (Search-based compilers) 와 정반대 방향에 있다는 점이 흥미로움.

Exo 의 design philosophy

“같은 알고리즘에 대해 모든 hw 가 자기만의 optimal schedule 을 갖는다. 그 schedule 은 자동 search 보다 — 그 hw 를 잘 아는 사람의 통제 하에 — 더 빠르고, 더 maintain 가능하게 만들 수 있다.” 이 frame 이 search-based 와 정반대.

§ 09한계· where Exo doesn't fit

Exo 가 해결 안 하는 자리

그럼에도 불구하고 — “새 hw 가 등장할 때 컴파일러를 fork 하지 않고 schedule 라이브러리만 추가” 라는 자리는 Exo 가 거의 유일. accelerator 가 늘어나는 시대에서 의미가 커지는 영역.

§ 10기억할 메모와 코드· Exo repo

다시 열었을 때 빠르게 잡혀야 할 것

algorithm vs schedule
Halide 의 큰 idea. Exo 가 그 schedule 자체를 라이브러리화.
@proc
Exo 의 reference algorithm 정의. Python decorator + nested loop.
@instr
사용자 정의 hw instruction. semantics + textual emit form.
cursor (Exo 2)
코드 위치의 1급 객체. composable schedule 의 핵심.
stage_mem
특정 access 를 임시 buffer 에 stage. memory hierarchy 매핑.
verified rewrite
모든 transformation 이 dependency / semantic 보존 검증 통과.
schedule library
Exo 2 의 새 영역 — community / vendor 가 자기 hw 의 schedule 을 publish.
vs Halide/TVM
자동 search 안 함. 정확성 검증 강함. 사용자 학습 곡선 가파름.

손에 새기기 — 실습 시퀀스

  1. Exo 설치 + 기본 GEMMpip install exo-lang. @proc 으로 naive nested-loop GEMM. Python 으로 직접 호출.
  2. schedule chain — divide_loop, reorder_loops, stage_mem 을 한 단계씩 chain. 각 단계 후 emit 된 C 코드를 직접 본다.
  3. cursor 실험 — 같은 변환을 두 loop 에 — cursor 를 손으로 잡아 — 적용. naming 만으로 잡기 어려운 위치를 cursor 로 정확히.
  4. 잘못된 reorder 시도 — dependence 를 깨는 reorder 를 시도. Exo 의 에러 메시지를 본다. "violates ..." 형태.
  5. 사용자 정의 instruction@instr 로 가상의 16×16 GEMM tile inst. call_eqv 로 schedule 의 안쪽 loop 를 그 inst 로 치환.
  6. schedule library 작성 — 자주 쓰는 “tile + stage + replace” 패턴을 사용자 함수로 묶는다. 다른 algorithm 에 reuse.
  7. Halide 와 head-to-head — 같은 GEMM 을 Halide 로도 짜고 같은 schedule 의 표현 차이를 비교.
§ 11다른 강의로· connections

이 강의의 frame 이 다른 강의에서 어떻게 다시 등장하는지

§ 12열린 질문· open questions

transcript 가 비어 있어 직접 검증해야 할 것들

검증 메모

본 노트는 Exo 의 PLDI 2022 paper 와 공식 자료의 일반적 framework 를 기반으로 재구성. 강의에서 Yuka 가 보여준 정확한 demo / channel 별 디테일 / 청중 Q&A 는 직접 영상 시청으로 검증 필요. 특히 cursor 의 정확한 type 시스템과 schedule library 의 표준화 디테일은 paper 의 detail 을 직접 읽기.

← Lecture 061 D-Matrix Corsair — Exo 가 lowering 할 수 있는 새 hw 의 사례 Lecture 063 → Search-Based Compilers — manual control 의 정반대 axis