1. 문제의 출발점
GPU 기업과 AI 프레임워크 회사들은 이미 매우 높은 수준으로 최적화된 실행 환경을 제공한다.
NVIDIA는 cuBLAS, cuDNN, TensorRT와 같은 라이브러리를 제공하고, PyTorch와 TensorFlow 같은 프레임워크는 이러한 라이브러리를 활용해 대부분의 표준 연산을 효율적으로 실행한다. 각 기업은 자체 런타임, 그래프 최적화 방식, 커널 선택 전략, 메모리 관리 구조를 가지고 있으며, 사용자는 별도의 저수준 구현 없이도 높은 성능을 얻을 수 있다.
이러한 상황에서는 자연스럽게 다음과 같은 의문이 생긴다.
이미 충분히 최적화된 라이브러리와 실행 시스템이 존재하는데, 왜 다시 SASS와 Nsight Compute 단계까지 내려가 분석해야 하는가?
이 질문에 대한 답은 기존 최적화를 부정하거나 대체하는 데 있지 않다.
SASS와 Nsight Compute 분석의 목적은 기존 구현보다 무조건 빠른 코드를 직접 만드는 것이 아니라, 최적화된 시스템이 실제 하드웨어에서 어떤 방식으로 동작하는지 확인하고, 그 최적화가 적용되는 조건과 한계를 이해하는 데 있다.
2. 기존 라이브러리가 제공하는 것
GPU 라이브러리는 일반적으로 다음과 같은 표준 연산에 대해 매우 강력한 구현을 제공한다.
- 행렬 곱셈
- 합성곱
- 정규화
- Attention
- Reduction
- Activation
- 데이터 타입 변환
특히 GEMM과 convolution처럼 반복적으로 사용되는 연산은 수년간의 튜닝과 아키텍처별 최적화가 축적되어 있다.
이러한 라이브러리는 입력 크기, 데이터 타입, GPU 아키텍처에 따라 적절한 커널을 선택하며, Tensor Core, shared memory, vectorized load, asynchronous copy와 같은 기능도 적극적으로 활용한다.
따라서 범용 GEMM 커널 하나를 직접 구현하여 cuBLAS보다 항상 빠르게 만드는 것은 현실적으로 매우 어렵다.
그러나 실제 모델의 성능은 하나의 연산만으로 결정되지 않는다.
실제 실행은 다음과 같은 연산들의 연결로 구성된다.
GEMM
→ Bias
→ Activation
→ Transpose
→ Reshape
→ Normalization
→ Memory Copy
→ 다음 Kernel Launch
각 연산이 개별적으로 최적화되어 있더라도, 연산과 연산 사이의 연결이 최적이라는 보장은 없다.
성능 저하는 오히려 다음과 같은 경계에서 발생할 수 있다.
- 불필요한 global memory 왕복
- 작은 커널의 반복 실행
- kernel launch overhead
- tensor layout 변환
- 동기화
- 중간 결과 저장
- register 사용량 증가
- 특정 shape에서의 비효율적인 커널 선택
기존 라이브러리는 잘 만들어진 부품을 제공하지만, 전체 실행 구조의 최적화까지 항상 책임지지는 않는다.
3. 최적화된 부품과 최적화된 시스템의 차이
개별 연산이 빠르다는 것과 전체 모델이 빠르다는 것은 서로 다른 문제다.
예를 들어 GEMM 자체는 최적의 라이브러리를 사용하더라도, 그 앞뒤에 bias, activation, transpose가 각각 별도의 커널로 실행된다면 다음과 같은 비용이 발생한다.
결과 저장
→ global memory 접근
→ 다음 커널 실행
→ 결과 다시 로드
→ 다시 저장
이 과정에서는 실제 계산보다 메모리 이동과 커널 실행 비용이 더 큰 비중을 차지할 수 있다.
따라서 전체 시스템 관점에서는 다음과 같은 판단이 필요하다.
- 연산을 fusion할 것인가
- 중간 텐서를 유지할 것인가
- transpose를 실제 복사로 수행할 것인가
- layout을 처음부터 다르게 설계할 것인가
- 작은 연산들을 하나의 커널로 합칠 것인가
- CUDA Graph capture 범위를 어디까지 설정할 것인가
- 라이브러리 호출과 custom kernel의 경계를 어디에 둘 것인가
이러한 결정은 cuBLAS나 cuDNN이 자동으로 모두 해결해 주지 않는다.
이 지점에서 컴파일러, 런타임, 프레임워크 개발자의 역할이 시작된다.
4. CUDA 소스만으로는 알 수 없는 것
CUDA 소스 코드는 개발자가 의도한 연산을 보여준다.
y = x * weight + bias;
그러나 하드웨어가 실제로 어떤 명령어를 실행했는지는 소스 코드만으로 알 수 없다.
컴파일 과정에서는 다음과 같은 변화가 발생할 수 있다.
- 여러 연산이 하나의 명령으로 결합됨
- load와 store가 벡터화됨
- FMA 명령으로 변환됨
- Tensor Core 명령이 사용됨
- 분기가 predication으로 변환됨
- register 부족으로 local memory spill이 발생함
- 일부 연산이 제거되거나 재배치됨
- 예상과 다른 데이터 이동 명령이 생성됨
따라서 CUDA 소스는 실행 의도를 설명하지만, 실제 하드웨어 동작을 직접 설명하지는 못한다.
이 간극을 확인하기 위해 SASS 분석이 필요하다.
5. SASS가 보여주는 것
SASS는 GPU가 실제로 실행하는 기계 명령어 수준의 표현이다.
SASS를 통해 다음과 같은 내용을 확인할 수 있다.
- 실제 load와 store 명령
- Tensor Core 명령 사용 여부
- 정수 및 부동소수점 연산 구성
- register 사용 방식
- predicate와 branch 처리
- shared memory 접근
- global memory 접근
- 명령어 배치와 dependency
- local memory spill 가능성
예를 들어 소스 코드에서는 단순한 배열 접근으로 보이더라도, 실제 SASS에서는 여러 주소 계산 명령과 load 명령이 생성될 수 있다.
또한 동일한 CUDA 코드라도 GPU 아키텍처와 컴파일 옵션에 따라 서로 다른 SASS가 생성될 수 있다.
따라서 SASS 분석은 다음 질문에 답하는 과정이다.
내가 작성한 코드가 실제로 어떤 명령어 구조로 변환되었는가?
6. Nsight Compute가 보여주는 것
SASS가 실행 명령어의 구조를 보여준다면, Nsight Compute는 그 명령어들이 실제로 어떤 성능 특성을 보였는지 알려준다.
Nsight Compute를 통해 다음과 같은 내용을 확인할 수 있다.
- Warp stall 원인
- Memory throughput
- Cache hit rate
- Occupancy
- Register usage
- Shared memory usage
- Branch efficiency
- Instruction throughput
- Tensor Core utilization
- Scheduler activity
- Memory dependency
- Compute와 memory의 병목 관계
이를 통해 단순히 커널이 느리다는 사실을 넘어, 왜 느린지를 구분할 수 있다.
예를 들어 동일하게 성능이 낮은 두 커널이라도 원인은 전혀 다를 수 있다.
커널 A:
global memory latency가 병목
커널 B:
register 사용 증가로 occupancy 감소
커널 C:
instruction dependency로 scheduler stall 발생
소스 코드만 보아서는 이러한 차이를 정확히 식별하기 어렵다.
SASS와 Nsight Compute를 함께 보면 다음 흐름이 성립한다.
소스 코드
→ 생성된 SASS
→ 실제 하드웨어 실행
→ 성능 지표
→ 병목 원인
7. 기업마다 고유한 실행 방식이 존재하기 때문에 더 중요하다
각 기업과 프레임워크는 서로 다른 실행 방식을 사용한다.
- 서로 다른 kernel fusion 전략
- 서로 다른 layout
- 서로 다른 kernel selection heuristic
- 서로 다른 workspace 정책
- 서로 다른 graph optimization
- 서로 다른 dynamic shape 처리
- 서로 다른 memory allocator
- 서로 다른 execution scheduling
하지만 이러한 내부 정책은 완전히 공개되지 않는 경우가 많다.
프레임워크가 최적화를 수행했다고 하더라도 사용자는 다음 내용을 직접 확인해야 한다.
- 어떤 최적화가 적용되었는가
- 현재 입력에서도 동일하게 적용되는가
- 어떤 커널이 선택되었는가
- fusion이 실제로 발생했는가
- 메모리 이동이 얼마나 발생했는가
- 현재 GPU에서도 그 선택이 효율적인가
즉, 기업마다 고유한 실행 방식이 존재한다는 사실은 저수준 분석이 필요 없다는 의미가 아니다.
오히려 내부가 보이지 않기 때문에 실제 실행 결과를 통해 시스템의 선택을 역으로 확인해야 한다.
8. 분석의 목적은 라이브러리를 이기는 것이 아니다
SASS와 Nsight Compute 분석을 시작할 때 가장 경계해야 할 생각은 다음과 같다.
직접 만든 커널이 기존 라이브러리보다 빨라야 의미가 있다.
이 기준을 적용하면 대부분의 표준 연산 최적화는 실패처럼 보일 수 있다.
그러나 실제 목적은 다르다.
목적은 다음을 설명할 수 있는 능력을 만드는 것이다.
- 왜 기존 구현이 빠른가
- 어떤 조건에서 빠른가
- 어떤 조건에서 성능이 떨어지는가
- 어떤 하드웨어 자원을 사용하고 있는가
- 특정 최적화가 실제로 적용되었는가
- 다른 연산과 결합되었을 때 어떤 문제가 생기는가
이러한 분석 능력은 라이브러리를 사용하는 능력과는 다른 종류의 역량이다.
라이브러리를 잘 사용하는 개발자는 적절한 API를 선택할 수 있다.
반면 실행을 분석할 수 있는 개발자는 그 API가 내부에서 어떤 선택을 했고, 그 선택이 현재 문제에 적합한지를 판단할 수 있다.
9. AI Compiler에서의 의미
AI Compiler는 단순히 모델 그래프를 CUDA 코드로 변환하는 도구가 아니다.
AI Compiler는 궁극적으로 다음과 같은 결정을 내려야 한다.
- 어떤 연산을 합칠 것인가
- 어떤 연산을 분리할 것인가
- 어떤 kernel을 사용할 것인가
- 어떤 layout을 선택할 것인가
- 중간 결과를 저장할 것인가
- 값을 재계산할 것인가
- 어떤 실행 순서를 사용할 것인가
- 어떤 메모리 공간을 사용할 것인가
이러한 결정을 자동화하려면 비용 모델이 필요하다.
비용 모델은 단순한 연산량 계산만으로 만들 수 없다.
예를 들어 fusion은 일반적으로 메모리 접근과 kernel launch를 줄이기 때문에 좋아 보인다.
Fusion의 장점
- global memory 접근 감소
- 중간 텐서 제거
- kernel launch 감소
하지만 동시에 다음과 같은 문제가 발생할 수 있다.
Fusion의 단점
- register 사용량 증가
- occupancy 감소
- instruction dependency 증가
- code size 증가
- scheduler 효율 저하
따라서 fusion이 항상 빠른 것은 아니다.
어떤 조건에서 fusion이 유리하고, 어떤 조건에서 분리가 유리한지는 실제 실행 결과를 확인해야 한다.
이 과정에서 SASS와 Nsight Compute 분석 결과는 컴파일러의 판단 근거가 된다.
관찰
→ 반복 실험
→ 패턴 발견
→ 비용 모델
→ 최적화 규칙
→ 자동 kernel selection
수동 분석이 반복되고 규칙화되면, 그것은 컴파일러 heuristic과 자동 최적화 정책으로 발전할 수 있다.
10. GPU Probing 관점에서의 의미
GPU Probing의 목적은 단순히 특정 커널을 몇 퍼센트 빠르게 만드는 것이 아니다.
보다 본질적인 목표는 다음 관계를 구축하는 데 있다.
입력 조건
→ 컴파일러의 선택
→ 생성된 SASS
→ 사용된 하드웨어 자원
→ 실제 성능
이 관계를 여러 실험에서 반복적으로 관찰하면 다음과 같은 규칙을 발견할 수 있다.
- 특정 shape에서 특정 명령어 조합이 선택된다.
- register 사용량이 일정 수준을 넘으면 spill이 발생한다.
- 특정 stride에서 cache 효율이 급격히 떨어진다.
- shared memory padding이 bank conflict를 제거한다.
- fusion이 memory traffic은 줄이지만 occupancy를 낮춘다.
- 동일한 커널이 GPU 아키텍처에 따라 다른 성능을 보인다.
- 특정 데이터 타입에서 Tensor Core 명령이 선택된다.
- 입력 크기에 따라 라이브러리가 서로 다른 kernel variant를 선택한다.
이러한 결과는 단순한 프로파일링 기록이 아니다.
하드웨어와 컴파일러의 반응을 실험을 통해 역으로 추론하는 과정이다.
이를 실행 기반 아키텍처 분석 또는 실행 기반 비용 모델 구축으로 정의할 수 있다.
11. 기존 최적화와 GPU Probing의 관계
기존 라이브러리와 GPU Probing은 경쟁 관계가 아니다.
기존 라이브러리:
잘 알려진 연산을 빠르게 실행한다.
GPU Probing:
왜 빠른지 분석한다.
언제 느려지는지 확인한다.
어떤 선택이 이루어졌는지 추론한다.
다른 연산과 결합될 때의 비용을 분석한다.
기존 라이브러리는 비교 대상이자 실험 대상이며, 동시에 최적화 구조를 학습할 수 있는 기준점이다.
예를 들어 cuBLAS 호출을 분석하면 다음 내용을 탐색할 수 있다.
- 입력 shape에 따른 kernel 선택 변화
- Tensor Core 사용 조건
- workspace 사용 여부
- 데이터 타입별 명령어 차이
- layout에 따른 메모리 접근 변화
- 작은 GEMM과 큰 GEMM의 실행 방식 차이
따라서 기존 라이브러리의 존재는 GPU Probing의 필요성을 약화시키는 것이 아니라, 오히려 더 정교한 분석 대상을 제공한다.
12. 모든 것을 직접 구현할 필요는 없다
SASS와 Nsight Compute 분석이 중요하다고 해서 모든 연산을 직접 구현해야 하는 것은 아니다.
표준 GEMM, convolution, attention과 같은 연산은 기존 라이브러리를 적극적으로 활용하는 것이 합리적이다.
직접 구현이 필요한 영역은 다음과 같다.
- custom operator
- 연산 fusion
- 특수한 tensor layout
- 작은 연산의 결합
- 비정형적인 데이터 접근
- 특정 모델에 특화된 kernel
- runtime scheduling
- memory movement 최적화
- dynamic execution path 처리
- library call 전후의 glue operation
핵심은 모든 것을 새로 만드는 것이 아니다.
기존 라이브러리와 custom kernel의 경계를 어디에 설정할지를 판단하는 것이다.
그 판단을 위해 실제 실행 분석이 필요하다.
13. 이 분석이 필요한 조건
다음 중 하나라도 목표에 포함된다면 SASS와 Nsight Compute 단계의 분석은 충분한 의미가 있다.
- AI Compiler 개발
- GPU Runtime 개발
- CUDA kernel 최적화
- custom operator 개발
- kernel fusion 설계
- memory movement 최소화
- CUDA Graph 기반 실행
- 성능 회귀 분석
- kernel selection system 구축
- GPU 아키텍처 특성 추론
- 하드웨어 비용 모델 구축
반대로 단순히 기존 모델을 실행하고 결과를 얻는 것이 목적이라면 저수준 분석까지 내려갈 필요는 없다.
따라서 분석의 필요성은 모든 GPU 사용자에게 동일하게 적용되는 것이 아니라, 목표에 따라 결정된다.
14. 작업의 명확한 정의
현재 수행하려는 작업은 다음과 같이 정의할 수 있다.
기존 GPU 라이브러리를 다시 구현하는 작업이 아니라, CUDA 소스 코드와 컴파일 결과, 하드웨어 실행 결과를 연결하여 GPU 실행의 실제 비용 구조를 관찰하고 모델링하는 작업이다.
조금 더 확장하면 다음과 같다.
이미 최적화된 실행 시스템을 사용하는 데서 멈추지 않고, 그 시스템이 어떤 조건에서 어떤 선택을 수행하며, 그 선택이 실제 하드웨어에서 어떤 결과를 만드는지 설명 가능한 형태로 구축하는 작업이다.
이 작업의 핵심 결과는 단순한 성능 수치가 아니다.
다음과 같은 추적 가능한 분석 구조를 만드는 것이 핵심이다.
고수준 연산
→ CUDA 코드
→ PTX
→ SASS
→ 하드웨어 자원 사용
→ 성능 지표
→ 병목 원인
→ 최적화 판단
15. 장기적인 방향
GPU 아키텍처와 AI 연산 구조는 계속 변한다.
새로운 데이터 타입, Tensor Core 명령, memory hierarchy, asynchronous execution 방식이 등장할 수 있으며, 기존 최적화 규칙이 새로운 하드웨어에서는 더 이상 유효하지 않을 수 있다.
이때 중요한 것은 특정 아키텍처의 최적화 기법을 외우는 것이 아니다.
새로운 환경에서도 다시 분석할 수 있는 방법을 갖추는 것이다.
실행한다
→ 측정한다
→ 명령어를 확인한다
→ 병목을 분해한다
→ 규칙을 추출한다
이 분석 체계가 구축되어 있다면 GPU와 컴파일러가 변하더라도 동일한 방식으로 새로운 비용 구조를 파악할 수 있다.
따라서 현재의 SASS와 Nsight Compute 분석은 특정 NVIDIA GPU에만 종속된 작업이 아니다.
새로운 연산과 새로운 하드웨어가 등장했을 때도 실행 결과를 근거로 최적화 방향을 도출할 수 있는 방법론을 만드는 과정이다.
16. 결론
이미 최적화된 GPU 라이브러리와 실행 시스템이 존재한다는 사실은 SASS와 Nsight Compute 분석의 필요성을 없애지 않는다.
기존 시스템은 높은 성능을 제공하지만 다음 내용을 항상 설명해 주지는 않는다.
- 왜 이 커널이 선택되었는가
- 어떤 최적화가 실제로 적용되었는가
- 현재 입력에서도 최적인가
- 병목이 계산인지 메모리인지
- fusion이 실제로 유리한가
- 하드웨어가 어떤 명령어를 실행했는가
SASS는 소스 코드가 실제 명령어로 어떻게 변환되었는지 보여준다.
Nsight Compute는 그 명령어들이 하드웨어에서 어떤 성능 특성을 보였는지 보여준다.
두 분석을 연결하면 다음 구조를 만들 수 있다.
소스의 의도
→ 컴파일러의 선택
→ 하드웨어의 실행
→ 실제 비용
따라서 지금 필요한 확신은 다음 질문에 대한 답에서 찾아야 한다.
내가 NVIDIA보다 더 좋은 범용 커널을 만들 수 있는가?
이 질문이 아니다.
더 중요한 질문은 다음이다.
이미 최적화된 시스템이 실제로 무엇을 하고 있는지 설명하고, 그 시스템이 예상하지 못한 조건에서 발생하는 병목을 찾아낼 수 있는가?
이 능력은 단순한 CUDA 사용 능력이 아니라, GPU Compiler, Runtime, Performance Engineering을 연결하는 핵심 역량이다.
'SASS_Probe' 카테고리의 다른 글
| 소스 코드에서 하드웨어 실행까지 이어지는 추적 가능한 분석 모델의 구축 (0) | 2026.07.17 |
|---|---|
| SASS 기반 GPU 하드웨어 프로빙 방법론 (0) | 2026.07.09 |
| SASS 만으로 명령어 latency 를 확정할 수 없는 이유 - ffma 실험 기준 (0) | 2026.07.09 |
| 공개되지 않은 하드웨어 특성을 실행으로 추론하는 방법 (0) | 2026.07.09 |
| SASSScope exp002 실험 과정 정리 (0) | 2026.07.07 |