1. 바뀐 구조의 확인
이전 방식 ( op 별 pyd 작성 )
- add.pyd, relu.pyd, gemm.pyd 처럼 op 마다 개별 pybind 모듈
- Python 쪽에서
- import aicf_cuda.add, import aicf_cuda.relu 와 같은 방식
문제점
- op가 늘어날수록
- CMake 타겟 증가
- pyd 파일 증가
- Python import 경로 복잡화
- 프레임 워크 관점에서 IR - op - kernel dispatch 흐름이 분산됨
- graph / planner / runtime 과의 통합이 어려움
2. Plan A 핵심 아이디어
CUDA 확장 모듈
aicf_cuda/
├─ __init__.py
└─ _C.pyd ← 모든 CUDA op의 단일 진입점
- pybind 모듈 이름 : _C
- Python 패키지 이름 : aicf_cuda
- Python 에서 사용하는 API
import aicf_cuda as aicf
aicf.op_call(
aicf.OpKind.EltwiseAdd,
inputs=[a, b],
outputs=[out],
attrs={}
)
3. 전체 호출 흐름 ( End - to - End )
Python
↓
aicf_cuda.op_call()
↓
bindings.cpp
- Tensor → TensorDesc
- dict → AttrPack
↓
dispatch_v0()
↓
KernelRegistry
↓
KernelVariant::supported()
↓
KernelVariant::launch()
↓
CUDA kernel
핵심 포인트
- Python 은 op 이름이나 커널을 모른다
- 오직 OpKind + TensorDesc + AttrPack 만 전달
- 커널 선택은 전부 C++ 레지스트리 내부 책임
4. 현재 관리해야 하는 중앙 축
(1) OpKind - 연산의 ID
enum class OpKind : int {
EltwiseAdd = 0,
EltwiseRelu = 1,
Gemm = 2,
_Count
};
- 새 op 추가 시 반드시 여기 추가
- Python 에서도 동일 enum 을 그대로 사용
(2) KernelVariant - 커널의 제약
struct KernelVariant {
const char* name;
Status (*launch)(...);
bool (*supported)(...);
size_t (*query_workspace)(...);
};
- 하나의 OpKind 는 여러 KernelVariant 를 가질 수 있음
- gemm_f32_naive, gemm_f16_tc, gemm_f32_tiled
- supported() 가 런타임 커널 선택 로직
(3) KernelRegistry - 전역 커널 테이블
R.register_kernel(Opkind::Gemm, make_gemm_f32_naive_variant());
- register_all.cpp 가 유일한 등록 지점
- Python 에서 import 시 딱 한 번 호출
(4) bindings.cpp - Python <-> C++ 경계
- Tensor 검증
- TensorDesc 생성
- AttrPack 생성
- dispatch_v0() 호출
bindings.cpp 에는
- kernel 코드 x
- op 분기 x
- shape inference x
5. 새 op 추가 시 해야 할 일
1단계 : OpKind 추가
enum class OpKind : int {
...
Conv2d = 3,
};
2단계 : 커널 구현
src/backends/cuda/ops/conv2d/
├─ kernels.cuh
└─ launcher.cu
- launcher.cu 안에서
- public API
- KernelVariant make_conv2d_f32_variant()
3 단계 : KernelVariant 정의
KernelVariant make_conv2d_f32_variant() {
KernelVariant v;
v.name = "conv2d_f32_naive";
v.supported = conv2d_supported;
v.launch = conv2d_launch;
v.query_workspace = conv2d_workspace;
return v;
}
4 단계 : register_all.cpp 에 등록
R.register_kernel(OpKind::Conv2d, make_conv2d_f32_variant());
할 필요 없는 것들
- 새 pybind 파일 추가
- CMake 에 pyd 타겟 추가
- Python import 수정
- bindings.cpp 수정 ( 대부분의 경우 )
6. 테스트 코드 변화
이전
import aicf_cuda.add
aicf_cuda.add.add_f32(...)
현재
import aicf_cuda as aicf
aicf.op_call(
aicf.OpKind.EltwiseAdd,
[a, b],
[out],
{}
)
장점
- 테스트 코드가 IR / graph executor 테스트 형태와 동일
- 프레임워크 테스트와 커널 테스트의 경계가 사라짐
7. 이 구조의 장기적 의미
- Python -> C++ -> CUDA 가 완전히 IR 중심 구조
- op 증가는 레지스트리 데이터 증가일 뿐
- graph capture / planner / fusino 로직이
- Python API 변경 없이
- kernel 레벨에서 진화 가능
이 구조는 이미 TVM / XLA / TorchInductor / TensorRT 스타일의 진입점과 동일
'AI Compiler framework' 카테고리의 다른 글
| 자동 생성 / 스캐폴딩을 위한 수정 내용 (Tensor Desc, ... ) (0) | 2025.12.20 |
|---|---|
| 현재 통일된 바인딩 코드 사용, 바인딩이 수정이 될 경우는? (0) | 2025.12.20 |
| Python <-> CUDA Ops Binding 설계 및 pyd 모듈 생성 과정 (0) | 2025.12.19 |
| CUDA Backend - kernel Registry 기반 빌드업 정리 (0) | 2025.12.18 |
| CUDA Elementwise 커널 구현 및 Nsight Compute 분석 문서화 (AICF - Torch 커널 대체의 첫 단계) (0) | 2025.12.18 |