본문 바로가기

GPU Probing Lab

Arithmetic Dependency Probe SASS 분석 문서

1. 목적

이번 분석의 목적은 arithmetic_dependency_probe의 JSON 결과가 실제 CUDA SASS 구조와 일치하는지 확인하는 것이다.

이전 실행 결과에서는 다음과 같은 progress ordering이 관찰되었다.

independent_fma ≈ independent_add
> dependent_fma_chain
> dependent_add_chain

평균 progress는 대략 다음과 같았다.

Role평균 progress상대 해석

independent_fma 약 56,459 기준
independent_add 약 56,458 independent FMA와 거의 동일
dependent_fma_chain 약 52,929 independent FMA 대비 약 93.75%
dependent_add_chain 약 49,401 independent ADD 대비 약 87.5%

이 결과만 보면 다음 질문이 생긴다.

왜 dependent_fma_chain이 dependent_add_chain보다 높은 progress를 보였는가?

직관적으로는 FMA가 ADD보다 더 무거워 보이기 때문에, 이 결과가 이상해 보일 수 있다. 따라서 최종 SASS를 확인하여 실제로 compiler가 어떤 instruction stream을 만들었는지 검증했다.


2. SASS 덤프 대상

빌드 결과에서 CUDA kernel object는 다음 위치에 존재했다.

build\CMakeFiles\probe_runner_arithmetic_dependency.dir\src\probes\arithmetic_dependency_probe\kernel.cu.obj

SASS 덤프는 다음 명령으로 생성했다.

& "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.6\bin\cuobjdump.exe" --dump-sass "build\CMakeFiles\probe_runner_arithmetic_dependency.dir\src\probes\arithmetic_dependency_probe\kernel.cu.obj" > "results\raw\arithmetic_dependency_probe.kernel.sass.txt"

이후 PowerShell에서 다음 명령으로 주요 instruction을 검색했다.

Select-String -Path "results\raw\arithmetic_dependency_probe.kernel.sass.txt" -Pattern "Function|FFMA|FADD|FADD32I|FMUL" -Context 2,2

검색 결과 kernel 함수는 다음과 같이 확인되었다.

Function : _ZN41_GLOBAL__N__83b3e739_9_kernel_cu_79bc57eb28arithmetic_dependency_kernelEP27DeviceArithmeticProbeRecordyyi

또한 sm_86용 코드가 생성되어 있음을 확인했다.


3. SASS 확인 결과 요약

SASS 확인 결과, compiler는 probe가 의도한 네 가지 arithmetic role을 실제로 보존했다.

확인된 구조는 다음과 같다.

RoleSASS 구조해석

independent_fma 여러 register에 분산된 FFMA independent accumulator lane 노출
independent_add 여러 register에 분산된 FADD independent accumulator lane 노출
dependent_fma_chain R14/R15를 오가는 FFMA dependency chain dependency 유지, 다만 MOV/source setup 섞임
dependent_add_chain R14 단일 register의 반복 FADD chain 가장 순수한 single-register dependency chain

따라서 JSON 결과는 compiler 최적화로 인한 착시가 아니다.

즉, ADD가 제거되었거나 FMA로 바뀐 것이 아니며, dependent chain이 independent하게 풀린 것도 아니다.


4. Independent FMA 구조

SASS 초기화 구간에서 여러 accumulator register가 생성된다.

대표적으로 다음과 같은 FFMA instruction들이 확인된다.

FFMA.FTZ R7,  R0, R3, 1
FFMA.FTZ R8,  R0, R3, 2
FFMA.FTZ R9,  R0, R3, 3
FFMA.FTZ R10, R0, R3, 4
FFMA.FTZ R11, R0, R3, 5
FFMA.FTZ R5,  R0, R3, 6
FFMA.FTZ R6,  R0, R3, 7
FFMA.FTZ R4,  R0, R3, 8

loop body에서도 여러 accumulator가 독립적으로 갱신된다.

FFMA.FTZ R7,  R7,  R12, ...
FFMA.FTZ R8,  R8,  R13, ...
FFMA.FTZ R9,  R9,  R16, ...
FFMA.FTZ R11, R11, R12, ...
FFMA.FTZ R10, R10, R13, ...
FFMA.FTZ R5,  R5,  R16, ...
FFMA.FTZ R6,  R6,  R15, ...
FFMA.FTZ R4,  R4,  R19, ...

이 구조는 다음과 같은 independent accumulator lane을 만든다.

R7 chain
R8 chain
R9 chain
R10 chain
R11 chain
R5 chain
R6 chain
R4 chain

즉 independent_fma는 하나의 긴 dependency chain이 아니라, 여러 개의 독립적인 accumulator stream으로 lowering되었다.

이 구조에서는 하나의 accumulator 결과를 기다리는 동안 다른 accumulator instruction을 issue할 수 있다. 따라서 instruction latency가 일정 부분 숨겨진다.


5. Independent ADD 구조

independent_add도 마찬가지로 여러 register에 분산된 FADD instruction으로 생성되었다.

대표적인 형태는 다음과 같다.

FADD.FTZ R7,  R7,  ...
FADD.FTZ R8,  R8,  ...
FADD.FTZ R9,  R9,  ...
FADD.FTZ R10, R10, ...
FADD.FTZ R11, R11, ...
FADD.FTZ R5,  R5,  ...
FADD.FTZ R6,  R6,  ...
FADD.FTZ R4,  R4,  ...

즉 independent_add도 다음과 같은 independent accumulator lane을 가진다.

R7 chain
R8 chain
R9 chain
R10 chain
R11 chain
R5 chain
R6 chain
R4 chain

이 때문에 JSON 결과에서 independent_fma와 independent_add의 progress가 거의 동일하게 나온 것은 SASS 구조와 일치한다.

핵심은 다음이다.

independent workload에서는 FMA냐 ADD냐보다,
여러 accumulator를 통해 ILP를 노출하는 구조가 더 중요했다.

6. Dependent ADD chain 구조

dependent_add_chain은 가장 명확한 형태로 확인되었다.

SASS에서는 하나의 register R14를 반복 갱신하는 FADD chain이 생성되었다.

FADD.FTZ R14, R14, ...
FADD.FTZ R14, R14, ...
FADD.FTZ R14, R14, ...
FADD.FTZ R14, R14, ...
FADD.FTZ R14, R14, ...
FADD.FTZ R14, R14, ...
FADD.FTZ R14, R14, ...
FADD.FTZ R14, R14, ...

이는 매우 순수한 single-register dependency chain이다.

R14 → R14 → R14 → R14 → ...

따라서 다음 FADD는 이전 FADD의 결과가 준비될 때까지 기다려야 한다.

이 구조에서는 independent accumulator가 없기 때문에, instruction-level parallelism을 노출하기 어렵다.

즉 dependent_add_chain은 arithmetic latency와 scoreboard dependency가 가장 직접적으로 드러나는 role이다.


7. Dependent FMA chain 구조

dependent_fma_chain도 dependency chain을 유지한다.

다만 dependent ADD와 다르게, 하나의 register만 반복 갱신하는 형태가 아니라 R14와 R15를 오가며 dependency가 이어진다.

대표적인 형태는 다음과 같다.

FFMA.FTZ R15, R14, R15, ...
FFMA.FTZ R15, R15, R16, ...
FFMA.FTZ R14, R15, R14, ...
FFMA.FTZ R14, R14, R17, ...
FFMA.FTZ R14, R14, R15, ...
FFMA.FTZ R14, R14, R17, ...
FFMA.FTZ R14, R14, R15, ...
FFMA.FTZ R14, R14, R17, ...

dependency 흐름은 다음과 같다.

R14/R15 → R15 → R14 → R14 → ...

따라서 dependent FMA 역시 independent하게 풀린 것이 아니다.

하지만 dependent ADD와 비교하면 차이가 있다.

dependent ADD는 거의 순수하게 다음 형태다.

FADD R14, R14, imm
FADD R14, R14, imm
FADD R14, R14, imm

반면 dependent FMA는 다음과 같이 source setup을 위한 MOV instruction과 register 교대가 섞인다.

MOV ...
FFMA ...
MOV ...
FFMA ...
FFMA ...
MOV ...
FFMA ...

즉 dependent FMA는 dependency chain을 유지하지만, 최종 instruction stream 관점에서는 dependent ADD보다 약간 다른 scheduling shape를 가진다.


8. dependent FMA가 dependent ADD보다 높게 나온 이유

JSON 결과에서 dependent_fma_chain은 dependent_add_chain보다 높은 progress를 보였다.

이 결과는 다음처럼 해석하면 안 된다.

FMA는 ADD보다 본질적으로 빠르다.

또는:

FMA latency가 ADD latency보다 낮다.

이번 SASS 확인 결과를 반영하면 더 정확한 해석은 다음이다.

dependent ADD는 R14 단일 register를 반복 갱신하는 매우 순수한 FADD dependency chain으로 생성되었다.
반면 dependent FMA는 R14/R15를 오가는 FFMA dependency chain이며, 중간에 MOV/source setup instruction이 섞여 있다.
따라서 두 role은 모두 dependent chain이지만, 최종 SASS에서 형성된 effective dependency interval과 scheduling shape가 다르다.

즉 dependent_fma_chain > dependent_add_chain이라는 결과는 연산 종류의 절대 성능 차이가 아니라, 다음 요소가 결합된 실행 signature다.

- dependency chain shape
- register dependency 흐름
- MOV/source setup instruction
- scheduler가 볼 수 있는 instruction spacing
- scoreboard wait
- branch/control-flow 구조
- clock budget 기반 측정 방식

따라서 가장 안전한 결론은 다음이다.

이 커널 shape에서는 dependent ADD가 dependent FMA보다 더 강한 single-register dependency signature를 만들었다.

9. JSON 결과와 SASS 결과의 연결

기존 JSON 결과는 다음 ordering을 보였다.

independent_fma ≈ independent_add
> dependent_fma_chain
> dependent_add_chain

SASS 확인 결과 이 ordering은 다음 구조와 일치한다.

9.1 independent_fma ≈ independent_add

두 role 모두 여러 accumulator register로 분산되어 있다.

R7, R8, R9, R10, R11, R5, R6, R4

따라서 scheduler는 한 accumulator의 결과를 기다리는 동안 다른 accumulator instruction을 issue할 수 있다.

이 구조에서는 ADD와 FMA의 연산 종류보다 ILP exposure가 더 큰 영향을 준다.

9.2 dependent_fma_chain < independent_fma

dependent FMA는 R14/R15를 통해 dependency chain을 유지한다.

따라서 independent FMA처럼 여러 accumulator를 통해 latency를 숨기기 어렵다.

그 결과 independent FMA보다 progress가 낮게 나온다.

9.3 dependent_add_chain < independent_add

dependent ADD는 R14 단일 register chain이다.

R14 → R14 → R14 → R14

따라서 independent ADD보다 latency와 scoreboard dependency가 더 직접적으로 드러난다.

9.4 dependent_add_chain < dependent_fma_chain

dependent ADD는 더 순수한 single-register FADD chain이다.

반면 dependent FMA는 dependency chain이 유지되지만 R14/R15 교대와 MOV instruction이 섞인다.

따라서 두 chain의 effective execution signature가 달라지고, 이 실험에서는 dependent ADD가 더 낮은 progress를 보였다.


10. Codegen 관점의 의미

이번 결과는 codegen cost model에 중요한 힌트를 준다.

단순한 cost model은 보통 다음처럼 생각하기 쉽다.

cost = number_of_ops

하지만 이번 결과는 이 모델이 부족하다는 것을 보여준다.

더 현실적인 cost model은 다음 요소를 포함해야 한다.

cost = number_of_ops
     + dependency_chain_penalty
     - ILP_exposure_bonus
     + register_pressure_penalty
     + scheduling_shape_penalty

이번 probe의 핵심 교훈은 다음이다.

같은 arithmetic 계열 instruction이라도,
그 instruction들이 하나의 dependency chain으로 연결되어 있는지,
여러 independent accumulator lane으로 분산되어 있는지에 따라
warp progress signature가 달라진다.

즉 kernel codegen에서 중요한 것은 단순히 FLOPs 수를 줄이는 것이 아니다.

더 중요한 것은 다음이다.

- 긴 dependency chain을 피한다.
- independent accumulator lane을 충분히 노출한다.
- scheduler가 ready instruction을 계속 볼 수 있게 한다.
- ILP exposure와 register pressure 사이의 균형을 잡는다.

11. 확정 가능한 결론

이번 SASS 확인으로 확정할 수 있는 내용은 다음이다.

1. arithmetic_dependency_kernel은 sm_86 대상으로 정상 생성되었다.
2. FFMA와 FADD instruction이 실제 SASS에 존재한다.
3. independent FMA/ADD는 여러 accumulator register로 분산되어 있다.
4. dependent ADD는 R14 단일 register를 반복 갱신하는 strict FADD chain이다.
5. dependent FMA는 R14/R15를 오가는 FFMA dependency chain이다.
6. 따라서 JSON progress ordering은 compiler 최적화 착시가 아니라 실제 SASS 구조와 일관된다.

12. 주의해야 할 해석

이번 결과를 다음처럼 쓰면 안 된다.

FMA is faster than ADD.

또는:

FMA latency is lower than ADD latency.

정확한 표현은 다음이다.

In this kernel shape, dependent_add_chain produced a stricter single-register FADD dependency chain than dependent_fma_chain. As a result, dependent_add_chain showed lower progress under the same clock budget.

한국어로는 다음이 적절하다.

이번 커널 shape에서는 dependent ADD가 dependent FMA보다 더 강한 단일 register dependency chain으로 lowering되었고, 그 결과 동일 clock budget에서 더 낮은 progress signature를 보였다.