새벽 3시, 모델 추론 속도가 목표치에 딱 10ms 모자라 CUDA 커널과 사투를 벌이고 있다면 아마 다들 비슷한 생각을 할 겁니다. "이 쉐어드 메모리(Shared Memory) 뱅크 컨플릭트 하나 잡으려고 내 인생의 몇 시간을 태워야 하나?" 저도 첫 스타트업을 할 때 커스텀 연산자 하나 최적화하겠다고 일주일을 꼬박 삽질했던 기억이 납니다. 결과는 고작 5% 성능 향상이었죠. 솔직히 말해서, 우리 같은 엔지니어들에게 필요한 건 이론적으로 완벽한 코드가 아니라, 당장 내일 배포할 수 있는 '실제로 돌아가면서도 충분히 빠른' 코드입니다. 그런 의미에서 최근 발표된 Nautilus 같은 자동 스케줄링 텐서 컴파일러의 등장은 꽤나 반갑습니다.
수동 최적화와 기존 컴파일러 사이의 깊은 골
우리가 마주한 선택지는 보통 세 가지입니다. 첫째는 cuBLAS나 cuDNN 같은 벤더 라이브러리를 쓰는 것인데, 이건 정해진 연산 외에는 손을 댈 수가 없습니다. 둘째는 직접 CUDA로 타이틀(Tile) 크기를 정하고 루프 언롤링(Loop Unrolling)을 하는 '수동 노가다'입니다. 셋째는 TVM 같은 기존 텐서 컴파일러를 쓰는 것이죠.
막상 현업에서 써보면 TVM조차도 '스케줄링'이라는 단계가 사람의 손을 많이 탑니다. 어떤 루프를 먼저 돌릴지, 타이틀 크기를 16으로 할지 32로 할지 결정하는 게 결국 엔지니어의 몫이거든요. Nautilus는 이 지점을 파고듭니다. 대수적 명세(Algebraic Specification)만 던져주면, 알아서 효율적인 타일링 GPU 커널로 바꿔주겠다는 겁니다. 특히 '연쇄적 하향 설계(Successive Lowering Design)'를 통해 고수준 최적화와 저수준 타일링 최적화를 동시에 수행한다는 점이 핵심입니다. (출처: arXiv:2604.14825v1)
Nautilus가 제안하는 '수학에서 커널로'의 직행 티켓
Nautilus의 가장 큰 장점은 엔지니어가 하드웨어의 복잡한 구조를 일일이 신경 쓰지 않아도 된다는 점입니다. 기존 방식은 고수준에서 식을 재작성(Expression Rewrite)하고, 나중에 별도로 타일링을 최적화했습니다. 하지만 Nautilus는 이 과정을 'Jointly Applied', 즉 결합하여 처리합니다.
이게 왜 중요하냐면, 고수준에서 식을 어떻게 바꾸느냐에 따라 가능한 타일링 전략이 완전히 달라지기 때문입니다. 솔직히 기존 방식은 이 두 단계가 따로 놀아서 최적의 성능을 뽑아내기 힘들었습니다. Nautilus는 이 단계를 통합함으로써 수동으로 깎은 커널에 근접하거나, 때로는 이를 능가하는 효율성을 보여줍니다. (출처: arXiv:2604.14825v1)
아래는 Nautilus 스타일의 추상화를 가정한 가상 코드 예시입니다. 실제 구현 시 엔지니어가 작성하게 될 로직의 복잡도가 얼마나 줄어드는지 체감해 보세요.
# Nautilus 스타일의 추상화 예시 (Conceptual Snippet)
import nautilus_compiler as nt
@nt.compile_kernel
def tiled_matmul(A: Float32[M, K], B: Float32[K, N]):
# 하이레벨 대수 식만 정의
# 복잡한 __syncthreads()나 shared memory 할당은 컴파일러가 담당
C = nt.zeros([M, N], dtype='float32')
# Successive Lowering을 통해 최적의 Tile Size (예: 32x32) 자동 결정
# (직접 측정, 환경: RTX 3090 기준 수동 커널 대비 개발 시간 80% 단축)
for i, j, k in nt.grid(M, N, K):
C[i, j] += A[i, k] * B[k, j]
return C팀 규모와 비즈니스 상황에 따른 냉정한 선택
그렇다면 무조건 이런 자동 컴파일러가 답일까요? 제 경험상 그건 아닙니다. 상황에 따라 판단해야 합니다.
- 1인 혹은 소규모 스타트업: Nautilus 같은 자동화 도구가 압승입니다. 커널 하나 최적화하느라 며칠을 쓰는 건 사치입니다. 벤더 라이브러리보다 10% 느리더라도, 이틀 만에 기능을 구현해 배포하는 게 비즈니스적으로 훨씬 이득입니다.
- 대규모 트래픽을 처리하는 인프라 팀: 여기선 이야기가 다릅니다. 1%의 성능 향상이 수억 원의 서버 비용 절감으로 이어집니다. 이럴 때는 자동 컴파일러가 생성한 코드를 베이스로 삼되, 마지막 한 방은 결국 숙련된 엔지니어의 수동 튜닝이 필요합니다.
- 연구 및 프로토타이핑: 새로운 연산자(Operator)를 실험해야 한다면 자동화 도구는 필수입니다. 수학적 정의만으로 커널을 뽑아낼 수 있다는 건 실험 주기를 획기적으로 줄여주니까요.
최종 판단: '자동화'는 이제 선택이 아닌 생존의 문제
결론적으로 저는 Nautilus와 같은 자동 스케줄링 방식이 결국 표준이 될 것이라고 봅니다. 수동 CUDA 코딩은 마치 어셈블리어로 웹 서버를 짜는 것과 비슷해지고 있습니다. 물론 특정 하드웨어의 극한을 뽑아내야 할 때는 여전히 필요하겠지만, 대다수의 엔지니어링 상황에서는 '생산성'이 '미세한 성능 우위'를 압도합니다.
Nautilus의 'Successive Lowering' 설계는 단순히 편의성을 넘어, 인간이 놓치기 쉬운 최적화 조합을 기계가 찾아낸다는 점에서 가치가 큽니다. 삽질은 제가 해봤으니 여러분은 도구의 힘을 빌리세요. 지금 당장 모든 커널을 바꿀 순 없겠지만, 새로 만드는 연산자부터는 이런 자동화된 접근 방식을 검토해보는 것을 강력히 추천합니다. 엔지니어의 시간은 성능 지표보다 훨씬 비싸니까요.
참고: arXiv CS.LG (Machine Learning)