1. 문제의식
GPU codegen에서 중요한 것은 단순히 “맞는 CUDA 코드를 생성하는 것”이 아니다.
더 중요한 질문은 이것이다.
생성한 kernel code shape가
compiler backend를 거친 뒤에도
의도한 SASS execution shape로 유지되는가?
CUDA/C++ 코드에서 같은 의미의 연산을 작성하더라도, 최종 SASS에서는 서로 다른 형태가 될 수 있다.
예를 들어 high-level에서는 둘 다 dependent arithmetic chain처럼 보일 수 있다.
acc = acc + c;
acc = acc + c;
acc = acc + c;
acc = fmaf(acc, a, b);
acc = fmaf(acc, a, b);
acc = fmaf(acc, a, b);
하지만 실제 SASS에서는 다음처럼 크게 달라질 수 있다.
FADD R14, R7, R14
FADD R14, R7, R14
FADD R14, R7, R14
또는:
FFMA R15, R14, R15, ...
MOV R17, ...
FFMA R14, R15, R14, ...
따라서 codegen에서는 “연산 의미”뿐 아니라 “compiler가 보존할 가능성이 높은 code shape”를 함께 고려해야 한다.
2. 실험군의 역할
이번 실험군은 크게 두 단계로 볼 수 있다.
1. arithmetic_dependency_probe
C++ expression 기반 dependent / independent arithmetic 비교
2. controlled_arithmetic_chain_probe
ADD/FMA dependent chain을 더 직접적으로 통제한 비교
첫 번째 실험은 high-level kernel code shape가 compiler에 의해 어떻게 다른 SASS shape로 변할 수 있는지를 보여준다.
두 번째 실험은 source operand와 accumulator 형태를 더 강하게 제한했을 때, SASS shape가 어느 정도 안정적으로 보존될 수 있는지를 보여준다.
3. 이전 실험: C++ expression 기반 probe
이전 arithmetic_dependency_probe에서는 다음 ordering이 관찰되었다.
independent_fma ≈ independent_add
> dependent_fma_chain
> dependent_add_chain
처음에는 dependent_fma_chain > dependent_add_chain이 이상해 보였다.
FMA는 수학적으로 multiply-add이고, ADD보다 더 많은 연산을 하는 것처럼 보이기 때문이다.
하지만 SASS 확인 결과, dependent ADD와 dependent FMA는 같은 형태의 chain이 아니었다.
이전 dependent ADD는 비교적 순수한 single-register chain에 가까웠다.
FADD R14, R14, ...
FADD R14, R14, ...
FADD R14, R14, ...
반면 dependent FMA는 R14/R15를 오가며 source setup 또는 MOV가 섞인 형태였다.
FFMA R15, R14, R15, ...
MOV ...
FFMA R14, R15, R14, ...
따라서 이전 실험에서 나타난 FMA와 ADD의 progress 차이는 FMA와 ADD의 intrinsic latency 차이가 아니라, C++ expression이 최종 SASS에서 서로 다른 effective instruction stream으로 lowering된 결과로 보는 것이 더 타당하다.
이 실험의 교훈은 다음이다.
C++ source level에서 같은 “dependent chain” 의도를 가졌더라도,
compiler backend를 거치면 ADD path와 FMA path의 SASS shape가 달라질 수 있다.
4. Controlled probe: SASS shape를 더 강하게 제한한 실험
후속 controlled_arithmetic_chain_probe에서는 ADD와 FMA를 더 직접적인 single-accumulator chain에 가깝게 만들었다.
핵심 형태는 다음이다.
ADD:
R14 <- R14 + R7
FMA:
R14 <- R2 * R14 + R7
실제 SASS에서도 8-chain 구간은 매우 깨끗하게 확인된다.
4.1 FMA 8-chain
/*0270*/ FFMA R14, R2, R14, R7
/*0280*/ FFMA R14, R2, R14, R7
/*0290*/ FFMA R14, R2, R14, R7
/*02a0*/ FFMA R14, R2, R14, R7
/*02b0*/ FFMA R14, R2, R14, R7
/*02c0*/ FFMA R14, R2, R14, R7
/*02d0*/ FFMA R14, R2, R14, R7
/*02e0*/ FFMA R14, R2, R14, R7
즉 FFMA R14, R2, R14, R7가 8회 반복된다. R14는 destination이면서 다음 instruction의 source가 되므로 strict dependent chain이다.
4.2 ADD 8-chain
/*0300*/ FADD R14, R7, R14
/*0310*/ FADD R14, R7, R14
/*0320*/ FADD R14, R7, R14
/*0330*/ FADD R14, R7, R14
/*0340*/ FADD R14, R7, R14
/*0350*/ FADD R14, R7, R14
/*0360*/ FADD R14, R7, R14
/*0370*/ FADD R14, R7, R14
여기서도 FADD R14, R7, R14가 8회 반복된다. 역시 R14 -> R14 -> R14로 이어지는 strict dependent chain이다.
따라서 8-chain 기준으로는 ADD와 FMA 모두 매우 유사한 SASS shape로 통제되었다.
5. 8-chain에서 SASS shape가 안정적으로 유지된 이유
8-chain에서 SASS shape가 고정될 수 있었던 이유는 다음과 같이 볼 수 있다.
5.1 source operand가 단순하고 고정되어 있었다
FMA는 반복적으로 다음 구조를 가진다.
FFMA R14, R2, R14, R7
ADD는 반복적으로 다음 구조를 가진다.
FADD R14, R7, R14
여기서 accumulator는 R14로 고정되고, source operand도 R2, R7처럼 이미 준비된 register로 고정된다.
즉 매 instruction마다 새로운 상수를 materialize하거나, 새로운 source를 준비하기 위해 MOV를 끼울 필요가 적다.
5.2 chain 길이가 짧았다
8-step chain은 ptxas가 하나의 accumulator register를 유지하기 쉬운 길이다.
R14 → R14 → R14 → ... → R14
이 정도 길이는 중간 temporary register를 도입하지 않아도 register allocation과 scheduling이 가능하다.
5.3 live register pressure가 상대적으로 낮았다
8-chain은 필요한 live value가 많지 않다.
R14: accumulator
R2 : multiplier / fixed source
R7 : addend / fixed source
이 구조는 compiler가 굳이 accumulator를 다른 register로 옮기거나 expression을 분해할 압력이 낮다.
5.4 expression complexity가 낮았다
이전 C++ expression 기반 FMA는 여러 상수, source setup, register 교대가 섞였다.
반면 controlled 8-chain은 거의 다음 패턴만 반복한다.
same op
same accumulator
same sources
same dependency edge
이런 형태는 compiler가 변경할 여지가 적다.
6. 16-chain에서 나타난 SASS shape 변화
16-chain에서는 두 종류의 형태가 함께 나타난다.
하나는 매우 깨끗한 R14-only 16-step chain이다.
6.1 strict R14-only FMA16
/*07b0*/ FFMA R14, R2, R14, R7
/*07c0*/ FFMA R14, R2, R14, R7
/*07d0*/ FFMA R14, R2, R14, R7
/*07e0*/ FFMA R14, R2, R14, R7
/*07f0*/ FFMA R14, R2, R14, R7
/*0800*/ FFMA R14, R2, R14, R7
/*0810*/ FFMA R14, R2, R14, R7
/*0820*/ FFMA R14, R2, R14, R7
/*0830*/ FFMA R14, R2, R14, R7
/*0840*/ FFMA R14, R2, R14, R7
/*0850*/ FFMA R14, R2, R14, R7
/*0860*/ FFMA R14, R2, R14, R7
/*0870*/ FFMA R14, R2, R14, R7
/*0880*/ FFMA R14, R2, R14, R7
/*0890*/ FFMA R14, R2, R14, R7
/*08a0*/ FFMA R14, R2, R14, R7
여기서는 FFMA R14, R2, R14, R7가 16회 반복된다.
6.2 strict R14-only ADD16
/*08c0*/ FADD R14, R7, R14
/*08d0*/ FADD R14, R7, R14
/*08e0*/ FADD R14, R7, R14
/*08f0*/ FADD R14, R7, R14
/*0900*/ FADD R14, R7, R14
/*0910*/ FADD R14, R7, R14
/*0920*/ FADD R14, R7, R14
/*0930*/ FADD R14, R7, R14
/*0940*/ FADD R14, R7, R14
/*0950*/ FADD R14, R7, R14
/*0960*/ FADD R14, R7, R14
/*0970*/ FADD R14, R7, R14
/*0980*/ FADD R14, R7, R14
/*0990*/ FADD R14, R7, R14
/*09a0*/ FADD R14, R7, R14
/*09b0*/ FADD R14, R7, R14
여기서는 FADD R14, R7, R14가 16회 반복된다.
이 구간만 보면 16-chain 역시 매우 안정적으로 보존되었다.
하지만 전체 SASS에는 다른 형태의 16-chain path도 존재한다.
6.3 중간 register를 거치는 16-chain
일부 path에서는 16-step sequence가 R14만 반복되지 않고, R10 같은 중간 register를 거친다.
FMA 쪽 예시는 다음과 같다.
/*03b0*/ FFMA R14, R2, R14, R7
...
/*0410*/ FFMA R14, R2, R14, R7
/*0420*/ FFMA R10, R2, R14, R7
/*0430*/ FFMA R10, R2, R10, R7
...
/*0490*/ FFMA R10, R2, R10, R7
/*04a0*/ FFMA R14, R2, R10, R7
이 구조는 다음 흐름을 만든다.
R14 → R10 → R10 → ... → R14
ADD 쪽에도 같은 형태가 보인다.
/*04c0*/ FADD R14, R7, R14
...
/*0520*/ FADD R14, R7, R14
/*0530*/ FADD R10, R7, R14
/*0540*/ FADD R10, R7, R10
...
/*05a0*/ FADD R10, R7, R10
/*05b0*/ FADD R14, R7, R10
이 역시 dependency는 유지된다.
R14 → R10 → R10 → ... → R14
다만 shape는 R14-only strict chain과 다르다.
이 부분이 중요하다.
chain이 유지되는 것과
SASS shape가 완전히 동일하게 유지되는 것은 다르다.
7. Transformability 관점의 해석
이번 실험군은 kernel code shape를 다음 세 가지 등급으로 나눠볼 수 있게 해준다.
7.1 Stable shape
compiler가 거의 그대로 유지한 형태다.
예:
FFMA R14, R2, R14, R7 x 8
FADD R14, R7, R14 x 8
특징:
- 짧은 chain
- fixed source operands
- single accumulator
- 낮은 expression complexity
- 낮은 temporary pressure
8-chain controlled path가 여기에 해당한다.
7.2 Locally stable shape
핵심 dependency는 유지되지만, 일부 register flow가 바뀌는 형태다.
예:
R14 → R10 → R10 → R14
특징:
- chain은 유지됨
- 하지만 accumulator register가 중간에 바뀜
- shape가 완전히 R14-only는 아님
- fine-grained timing 해석에는 주의 필요
일부 16-chain path가 여기에 해당한다.
7.3 Transformed shape
high-level 의도는 유지되지만 SASS instruction stream이 상당히 달라지는 형태다.
예:
C++ dependent FMA expression
→ R14/R15 교대
→ MOV/source setup
→ ADD path와 다른 instruction mix
이전 arithmetic_dependency_probe의 dependent FMA path가 여기에 가깝다.
8. Codegen implication
이 결과는 AI compiler / kernel generator에 중요한 기준을 준다.
Codegen은 단순히 다음을 결정하는 수준에서 끝나면 안 된다.
ADD를 쓸 것인가?
FMA를 쓸 것인가?
unroll을 8로 할 것인가?
unroll을 16으로 할 것인가?
실제로는 다음 질문까지 봐야 한다.
이 source-level shape가 ptxas를 거친 뒤에도
의도한 SASS-level dependency shape로 유지될 가능성이 높은가?
따라서 lowering decision은 다음 요소를 포함해야 한다.
lowering decision =
semantic correctness
+ expected SASS shape stability
+ dependency shape preservation
+ register pressure risk
+ compiler transform risk
+ validation cost
특히 latency hiding, dependency chain, ILP exposure를 조절하는 kernel에서는 SASS shape stability가 중요하다.
9. Codegen rule 후보
이번 실험에서 바로 뽑을 수 있는 rule은 다음과 같다.
Rule 1. 성능 민감 dependent chain은 source operand를 고정하라
Good:
R14 <- R2 * R14 + R7
R14 <- R2 * R14 + R7
R14 <- R2 * R14 + R7
Risky:
R14 <- a0 * R14 + b0
R14 <- a1 * R14 + b1
R14 <- a2 * R14 + b2
source operand가 매번 달라지면 compiler가 source setup, MOV, temporary register를 삽입할 가능성이 커진다.
Rule 2. chain length가 길어질수록 SASS shape validation이 필요하다
8-chain은 안정적으로 유지되었지만, 16-chain에서는 일부 path에서 R10, R8, R2 같은 중간 register가 등장한다.
따라서 긴 dependent chain은 다음 검증이 필요하다.
- chain이 유지되는가?
- accumulator가 중간에 바뀌는가?
- MOV/source setup이 끼는가?
- path별 SASS shape가 동일한가?
Rule 3. C++ expression 기반 codegen은 transform risk가 있다
C++ expression은 readable하고 portable하지만, compiler가 적극적으로 변형할 수 있다.
따라서 exact execution shape가 중요한 probe나 latency-sensitive kernel은 다음 중 하나가 필요하다.
- inline PTX
- 제한된 expression pattern
- volatile / asm barrier
- SASS validation
Rule 4. progress 결과는 SASS shape와 함께 해석해야 한다
JSON이나 benchmark 결과만 보면 어떤 구조가 실행되었는지 알 수 없다.
이번 실험군에서처럼:
JSON result
→ SASS validation
→ transformability interpretation
순서로 가야 한다.
10. 최종 결론
이번 실험군의 핵심은 단순히 “ADD와 FMA 중 무엇이 빠른가”가 아니다.
더 중요한 결론은 이것이다.
kernel code shape는 compiler backend를 거치며 변형될 수 있고,
그 변형 가능성 자체가 codegen의 중요한 설계 대상이다.
controlled 8-chain에서는 source operand와 accumulator dependency가 단순했기 때문에 SASS가 안정적으로 유지되었다.
FFMA R14, R2, R14, R7 x8
FADD R14, R7, R14 x8
반면 긴 chain이나 C++ expression 기반 workload에서는 중간 register, source setup, 다른 register flow가 생길 수 있다.
따라서 codegen의 목표는 단순히 CUDA 코드를 생성하는 것이 아니라:
compiler가 최종 SASS에서 원하는 execution shape로 보존할 가능성이 높은 code shape를 선택하는 것
이어야 한다.
한 문장으로 정리하면 이렇다.
좋은 GPU codegen은 semantic lowering만 하는 것이 아니라, compiler transformability를 예측하고 SASS shape stability를 관리해야 한다.
'GPU Probing Lab' 카테고리의 다른 글
| 10. Chain 7 Tail Distribution Probe (0) | 2026.05.20 |
|---|---|
| Irregular FMA Dependency Shape Probe (0) | 2026.05.20 |
| Controlled Arithmetic Chain Probe 문서화 - chain length, progress scaling, SASS shape 변화 (0) | 2026.05.19 |
| Controlled Arithmetic Chain Probe 결과 정리 - dependent FMA vs dependent ADD 해석 검증 (0) | 2026.05.19 |
| CUDA PTX / SASS 분석 문서 - CUDA 코드가 실제 GPU 실행 구조로 바뀌는 과정 (0) | 2026.05.19 |