PyTorch 프로파일링 (2부): nn.Linear에서 통합 MLP까지

발행: (2026년 6월 11일 AM 09:00 GMT+9)
7 분 소요

Source: Hugging Face Blog

기사 목록으로 돌아가기

Thumbnail of the blog post

첫 번째 파트 “Profiling in PyTorch”에서는 torch.add(torch.matmul(x, w), b)를 사용해 PyTorch 프로파일러 트레이스를 읽는 방법을 배웠습니다. 또한 CPU 디스패치 체인, 런치 오버헤드, 오버헤드에 제한된 영역과 연산에 제한된 영역의 차이, torch.compile의 내부 동작 등 여러 주제를 다뤘습니다.

두 번째 iteration(이번 블로그 포스트)에서는 한 단계 위로 올라갑니다. 손으로 구현한 matmul‑add 쌍을 bias=Truenn.Linear 로 교체합니다. 이는 모든 딥러닝 모델이 사용하는 기본 블록입니다. 그런 다음 예제에 맞게 세 개를 쌓고, 그 사이에 활성화 함수를 넣어 다층 퍼셉트론(MLP) 블록을 구성합니다.

이번 포스트에 사용된 스크립트는 여기에서 확인할 수 있습니다: 02_linear.py, 03_simple_mlp.py, 그리고 03_kernels_mlp.py. 이전과 마찬가지로 별도 탭에서 코드를 열어가며 읽는 것이 도움이 됩니다. 우리는 NVIDIA A100‑SXM4‑80GB GPU에서 스크립트를 실행합니다. Hugging Face 인프라에서 GPU를 손쉽게 설정하고 Dev Mode with Spaces로 실험해볼 수 있습니다. 또한 Hugging Face Jobs 파이프라인으로 스크립트를 실행할 수도 있습니다.

시작하기 전에, 앞으로 반복해서 사용할 두 가지 개념을 간단히 정리합니다:

  • GPU 커널은 GPU의 수많은 스레드에서 병렬로 실행되는 프로그램입니다.
  • CPU는 이러한 커널을 스케줄링하고 런치합니다. 프로파일러 트레이스에서 보이는 대부분의 PyTorch 오버헤드는 이 스케줄링 작업입니다.

From matmul-add to Linear

nn.Linear파트 1에서 이미 프로파일링한 행렬 곱셈과 덧셈을 감싸는 모듈 래퍼입니다. 차이점은 가중치와 편향을 파라미터로 소유하고, PyTorch 사용자에게 친숙한 forward 메서드를 제공한다는 점입니다.

# bias=True 로 하면 파트 1에서 본 곱셈·덧셈 연산을 정확히 흉내낼 수 있습니다.
linear_layer = nn.Linear(in_dim, out_dim, bias=True)
y = linear_layer(x)

우리가 다루는 연산은 다음과 같이 표현됩니다:

y = x @ w.T + b

여기서 x는 입력, w는 가중치, b는 편향입니다. 이제 02_linear.py를 실행하고 프로파일을 확인해 보겠습니다.

uv run 02_linear.py --batch 1024 --in_dim 32 --out_dim 64
uvx trace-util traces -b traces

trace-util은 트레이스를 Hugging Face 버킷(Hugging Face storage)에 동기화하고, 터미널에 Perfetto URL을 출력해 주는 유틸리티입니다.

nn.Linear forward pass의 PyTorch 프로파일러 트레이스: CPU 레인에 세 개의 짧은 Profile Step과 linear_fwd 어노테이션, GPU 레인에 작은 커널, 그리고 마지막에 긴 cudaDeviceSynchronize 바가 표시됨

Figure 1. nn.Linear의 프로파일러 트레이스

Figure 1은 Linear 레이어의 forward 호출에 대한 프로파일 트레이스를 보여줍니다. 우리는 이전 트레이스와 동일한 schedule 설정(wait=1, warmup=1, active=3)으로 Linear 레이어의 forward를 트레이스했습니다. 그래서 CPU와 GPU 레인에 각각 세 개의 Profile Step이 나타납니다.


What is the transpose doing?

CPU 디스패치 체인 확대: aten::linear 내부에서 aten::addmm 앞에 중첩된 aten::t 전치 연산이 보이며, GPU 레인에는 대응되는 활동이 없음

Figure 2. 전치 연산을 담당하는 CPU 행

Figure 2처럼 트레이스를 확대하면 aten::addmm(곱셈·덧셈) 앞에 aten::t(전치) 연산이 있는 것을 확인할 수 있습니다. 이는 nn.Linear가 가중치 파라미터를 전치한 뒤 입력과 곱한다는 뜻이며, aten::t 연산이 보이는 이유이기도 합니다.

중요한 점은 aten::t가 실제 데이터를 복사하거나 재배열하지 않는다는 것입니다. CPU에서 텐서 메타데이터(형태와 stride)만 바꿔 전치된 행렬을 표현합니다. GPU에서 커널을 런치하지도 않습니다. 이를 확인하는 방법은 두 가지입니다: 트레이스의 GPU 레인을 살펴보거나, 프로파일러 테이블에서 aten::t 행이 차지한 CUDA 시간을 확인하면 됩니다.


Why are there no separate mul and add kernels?

디스패치 체인에 강조 표시된 Linear 레이어의 프로파일 트레이스: aten::linear, aten::t, aten::addmm은 보이지만 별도의 aten::add는 없음

Figure 3. Linear 레이어 프로파일에 aten::add가 없는 이유

Figure 3에서 볼 수 있듯이 Linear 레이어의 디스패치 체인에는 aten::add(편향 추가) 연산이 없습니다. 이는 편향 추가가 에필로그(epilogue) 라는 기법으로 행렬 곱셈 커널에 접합(folded) 되었기 때문입니다.

에필로그란 GEMM(General Matrix Multiply) 커널이 결과를 HBM(High Bandwidth Memory, GPU 메인 메모리)에 기록하기 직전에 수행

0 조회
Back to Blog

관련 글

더 보기 »

OpenAI, Ona 인수

Today we’re announcing that OpenAI will acquire Ona⁠opens in a new windowhttp://ona.com/, bringing its secure cloud execution and orchestration technology into...