본문 바로가기

SASS_Probe

fma_contract_f32: FFMA Contraction Pattern Experiment

1. 실험 목적

이 실험의 목적은 CUDA C/C++ 코드에서 작성한 FP32 multiply-add 표현이 SASS 단계에서 어떤 조건으로 FFMA 로 contraction 되는지 확인하는 것

핵심 질문은 다음과 같다

  • av * bv + cv 는 SASS 에서 FFMA 로 변환되는가
  • 중간 변수 tmp 를 두어도 FFMA 로 합쳐지는가
  • multiply 와 add 사이에 관계없는 코드가 끼어도 FFMA 로 합쳐지는가 
  • 중간값을 global memory 에 저장하면 contraction 이 깨지는가
  • __fmul_rn, __fadd_rn 으로 반올림 지점을 강제하면 FFMA 가 억제되는가
  • volatile memory boundary 는 contraction 을 막는가
  • __noinline__ 함수 경계는 contraction 을 막는가?

이번 실험은 단순히 a * b + c 가 FFMA 로 변환되는지만 확인하는 것이 아니다 

더 중요한 목표는 컴파일러가 소스 코드의 줄 순서가 아니라 값의 생산자, 소비자 관계, 즉 def-use graph 를 기준으로 연산을 재작성하는지 확인하는 것이다.

 

2. 실험 대상

파일:

kernels/01_arithmetic/fma_contract_f32.cu

덤프 결과:

sass/sm86/01_arithmetic/fma_contract_f32.sass
ptx/sm86/01_arithmetic/fma_contract_f32.ptx

대상 아키텍처:

sm_86

확인된 주요 커널:

fma_direct_kernel
fma_tmp_kernel
fma_gap_kernel
fma_store_tmp_kernel
fma_rn_intrinsic_kernel
fma_volatile_global_kernel
fma_noinline_boundary_kernel

 

3. 전체 결과 요약

  • fma_direct_kernel 
    • av * bv + cv
      • FFMA
      • 기본 multiply-add contraction
  • fma_tmp_kernel
    • tmp = av * bv
    • out = tmp + cv
      • FFMA
      • source temporary 는 SASS 에서 물질화되지 않음
  • fma_gap_kernel
    • multiply 와 add 사이에 unrelated code 삽입
      • FFMA
      • 소스상 떨어져 있어도 def-use 관계로 contraction 
  • fma_store_tmp_kernel
    • scratch[i] = tmp
      • FMUL + FADD + STG
      • 중간값 저장으로 tmp 가 실제 레지스터 값으로 물질화
  • fma_run_intrinsic_kernel
    • __fmul_rn, __fadd_rn
      • FMUL +  FAD
      • 반올림 지점 강제로 contraction 억제
  • fma_volatile_global_kernel
    • volatile store/load
      • FMUL + STG + LDG + FADD
      • 관찰 가능한 memory boundary  로 contraction 차단
  • fma_noinline_boundary_kernel
    • noinline function boundary
      • FMUL + CALL + FADD
      • 함수 경계가 grpah rewrite 를 차단

핵심 결론

FFMA contracttion 은 소스 코드 문자열 패턴 매칭이 아니다.

컴파일러는 값의 흐름을 추적하고, 허용 가능한 경우 multiply-add graph 를 FFMA primitive 로 재작성 한다.

 

4. 커널별 분석

4.1 fma_direct_kernel

float out = av * bv + cv;
y[i] = out;

FFMA R11, R4, R3, R7 ;
STG.E [R8.64], R11 ;

av * bv + cv 형태가 SASS 에서 FFMA 하나로 변환되었다.

이는 가장 기본적인 FMA contraction 

  • source
    • out = av * bv + cv
  • compiler graph
    • out = add(mul(av, bv), cv)
  • SASS
    • FFMA

 

4.2 fma_tmp_kernel

float tmp = av * bv;
float out = tmp + cv;
y[i] = out;

FFMA R11, R4, R3, R7 ;
STG.e [R8.64], R11 ;

tmp 라는 중간 변수가 존재하지만, SASS 에서는 FMUl 결과로 따로 물질화되지 않았다. 

 

컴파일러는 다음 구조를

tmp = mul(av, bv)
out = add(tmp, cv)

>>

out = fma(av, bv, cv)

CUDA 의 temporary variable 은 반드시 SASS 레지스터 lifetime 으로 대응되지 않는다.

 

4.3 fma_gap_kernel

float tmp = av * bv;

//unrelated operations
float k = av + cv;
float m = k * 3.0f + bv;
aux[i] = m;

float out = tmp + cv;
y[i] = out;

>>

FADD R11, R3, R7 ;
FFMA R13, R11, 3, R4 ;
FFMA R15, R4, R3, R7 ;
STG.E [R8.64], R13 ;
STG.E [R10.64], R15 ;

두 개의 FFMA 가 관찰되었다.

  • FFMA R13, R11, 3, R4

이는 unrelated 계산인

  • float m = k * 3.0f + bv;

가 FFMA 로 변환된 것

 

두 번째 

  • FFMA R15, R4, R3, R7 ;

이것이 원래 실험에서 보고자 한 핵심 

multiply 와 add 사이에 unrelated operation 이 끼어 있었지만, 최종적으로 tmp + cv 계산은 FFMA 로 합쳐졌다.

 

"컴파일러는 source line adjaceny를 기준으로 FMA 를 만드는 것이 아니라 컴파일러는 값의 def-use 관계를 추적한다."

이 실험은 FFMA contraction 이 단순 peephole optimization 보다 더 넓은 범위에서 일어날 수 있음을 보여준다.

 

4.4 fma_store_tmp_kernel

float tmp = av * bv;
scratch[i] = tmp;

float out = tmp + cv;
y[i] = out;

FMUl R13, R4, R3 ;
FADD R15, R6, R13 ;
STG.E [R8.64], R13 ;
STG.E [R10.64], R15 ;

이 경우 FFMA 가 나오지 않음

대신 다음 구조가 관찰

FMUl - FADD - STG scratch - STG y

tmp 의 값이 R13 에 실제로 만들어지고, 이 값이 두 곳에서 사용 

이것은 source-level temporary 가 실제 SASS value 로 물질화된 경우

중요한 차이는 scratch[i] = tmp, 이 store 때문에 tmp 는 단순한 내부 중간 표현이 아니라 외부에서 관찰 가능한 값이 된다. 따라서 이를 제거하고 FFMA 로 완전히 합치지 어렵다. 

"중간값이 memory 에 저장되면, 그 값은 실제 계산 결과로 물질화될 가능성이 커진다."

어떤 중간 tensor 가 실제로 저장되는지, register accumulator 안에서만 갱신되는지를 구분하는 기준이 된다. 

 

4.5 fma_rn_intrinsic_kernel

float tmp = __fmul_rn(av, bv)
float out = __fadd_rn(tmp, cv)
y[i] = out

>>

FMUl R0, R4, R3
FADD R11, R0, R7
STG.E [R8.64], R11

__fmul_rn 과 __fadd_rn 을 사용하면 FFMA 가 억제된다.

이는 각 연산의 반올림 지점을 명시했기 때문

"FFMA contraction 은 단순 성능 최적화가 아니라 부동소수점 의미를 바꿔도 허용되는 조건에서만 수행된다"

 

4.6 fma_volatile_global_kernel

scratch[i] = av * bv
float tmp = scraatch[i]

float out = tmp + cv
y[i] = out

>>
FMUl R13, R4, R3 
STG.E.STRONG.SYS [R8.64], R13
LDG.E.STRONG.SYS R15, [R8.64] 
FADD R15, R6, R15
STG.E [R10.64], R15

volatile global memory 접근 때문에 store 와 load 가 SASS 에 명확히 남았다.

특히 다음 두 명령어가 중요하다

  • STG.E.STRONG.SYS
  • LDG.E.STRONG.SYS

이는 컴파일러가 해당 memory access 를 제거하거나 재배치하기 어렵다는 뜻

따라서 contraction 불가능

 

4.7 fma_noinline_boundary_kernel

float tmp = av * bv
tmp = identity_noinline(tmp)

float out = tmp + cv
y[i] = out

>>

FMUL R8, R4, R3
CALL.REL.NOINC 0x160
FADD R5, R7, R8
STG.E [R2.64], R5
RET.REL.NODEC R2 0x0

__noinline__ 함수 경계가 유지되면서, multiply 와 add 가 하나의 local expression 으로 합쳐지지 ㅇ낳았다.

함수 호출 경계가 def-use grpah rewrite 를 막는 장벽처럼 작동했다.

물론 모든 함수 호출이 항상 contraction 을 막는 것은 아님

함수가 inline 되거나 컴파일러가 내부 의미를 완전히 알 수 있으면 다시 최적화 가능

하지만 noinline 으로 경계를 강제하면, ㅓㅁ파일러가 이를 합치지 못한다.

 

5. SASS 분석 관점에서의 의미

컴파일러는 소스 코드를 먼저 내부 표현으로 바꾸고, 그 안에서 값의 흐름을 재작성한다.

 

6. FlashAttention / Online Update 관점과의 연결

이번 실험은 작은 규모의 mul + add 예제지만, 더 큰 연산 구조를 이해하는 데 중요한 기준을 제공한다.

예를 들어 일반 attention은 다음과 같은 중간 결과를 만들 수 있다.

QK^T
softmax(QK^T)
softmax(QK^T) V

반면 FlashAttention류 구현은 중간 matrix를 global memory에 완전히 물질화하지 않고, tile 단위로 읽으면서 다음 값을 online으로 갱신한다.

running max
running sum
accumulator

이번 fma_contract_f32 실험에서 본 핵심도 이와 비슷하다.

source-level intermediate:
    tmp = av * bv

SASS-level result:
    tmp가 사라지고 FFMA로 fused 될 수 있음

반대로 중간값이 저장되면:

scratch[i] = tmp

SASS에서는 다음처럼 물질화된다.

FMUL
STG
FADD

따라서 앞으로 operator/layer 분석에서는 다음 질문을 던져야 한다.

이 중간값은 실제로 memory에 저장되는가?
아니면 register accumulator 안에서만 유지되는가?
중간 tensor가 물질화되는가?
아니면 online update 형태로 재작성되는가?

이것이 SASS를 통해 attention류 최적화를 관찰하는 핵심 기준이 된다.

 

7. 결론

이번 실험의 핵심 결론은 다음과 같다.

FFMA contraction은 소스 코드 문자열 패턴 매칭이 아니다.

컴파일러는 다음 소스 코드를:

float tmp = av * bv;
float out = tmp + cv;

단순히 줄 단위로 번역하지 않는다.

대신 내부적으로 다음과 같은 계산 그래프를 본다.

out = add(mul(av, bv), cv)

그리고 조건이 허용되면 다음과 같이 재작성한다.

out = fma(av, bv, cv)

SASS에서는 이것이 다음으로 나타난다.

FFMA

하지만 다음과 같은 경계가 생기면 contraction이 깨진다.

memory materialization
volatile access
rounding boundary
noinline call boundary

따라서 SASS 분석에서 중요한 것은 소스 코드의 변수명이나 줄 순서가 아니라, 실제로 다음 요소들이 어떻게 나타나는지다.

FFMA / FMUL / FADD
LDG / STG
CALL / RET
register reuse
memory boundary
rounding boundary

이번 fma_contract_f32 실험은 SASS 분석의 첫 번째 기준선을 제공한다.

소스 표현이 아니라,
컴파일러가 재구성한 계산 그래프와 물질화 경계를 분석해야 한다.

 

 

 

'SASS_Probe' 카테고리의 다른 글

clamp_f32 분석  (0) 2026.06.14
relu_f32 분석  (0) 2026.06.14
fma_f32 분석  (0) 2026.06.13
mul_f32 분석  (0) 2026.06.13
SASS 분석을 통한 연산 구조 해석과 최적화  (0) 2026.06.13