<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> <channel> <title> blank </title> <description>SK텔레콤 LLM Research Engineer 장원범입니다. LLM 학습, 평가, 데이터 설계에 대해 공부하고 공유합니다. </description> <link>https://www.wonbeomjang.kr/</link> <atom:link href="https://www.wonbeomjang.kr/feed.xml" rel="self" type="application/rss+xml"/> <pubDate>Sat, 11 Apr 2026 10:52:25 +0000</pubDate> <lastBuildDate>Sat, 11 Apr 2026 10:52:25 +0000</lastBuildDate> <generator>Jekyll v4.4.1</generator> <item> <title>LLM 엔지니어가 알아야 할 GPU 아키텍처: Ampere → Hopper → Blackwell</title> <description>&lt;h1 id=&quot;왜-gpu-아키텍처를-알아야-하는가&quot;&gt;왜 GPU 아키텍처를 알아야 하는가&lt;/h1&gt; &lt;p&gt;LLM 엔지니어에게 GPU는 “빠른 연산기” 이상의 의미를 가진다. 모델 크기, 배치 사이즈, 시퀀스 길이, 학습/추론 전략 등 거의 &lt;strong&gt;모든 엔지니어링 결정이 GPU의 제약에 의해 결정&lt;/strong&gt;되기 때문이다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;“70B 모델을 학습하려면 GPU 몇 장이 필요한가?” → &lt;strong&gt;메모리 용량&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;“KV cache가 얼마나 들어가는가?” → &lt;strong&gt;메모리 대역폭&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;“FP8로 학습하면 속도가 얼마나 빨라지는가?” → &lt;strong&gt;정밀도별 연산 성능&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;“FlashAttention을 쓰면 왜 빨라지는가?” → &lt;strong&gt;메모리 계층과 IO 병목&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;“H100에서 A100 대비 실제로 얼마나 빠른가?” → &lt;strong&gt;실제 활용률과 병목 분석&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;이 글에서는 현재 LLM 엔지니어가 주로 사용하는 세 가지 GPU — &lt;strong&gt;A100 (Ampere)&lt;/strong&gt;, &lt;strong&gt;H100 (Hopper)&lt;/strong&gt;, &lt;strong&gt;B200 (Blackwell)&lt;/strong&gt; — 를 LLM 학습과 추론의 관점에서 비교한다.&lt;/p&gt; &lt;hr /&gt; &lt;h1 id=&quot;1-전체-스펙-비교&quot;&gt;1. 전체 스펙 비교&lt;/h1&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;&lt;strong&gt;A100 SXM&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;H100 SXM&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;B200 SXM&lt;/strong&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;아키텍처&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;Ampere (2020)&lt;/td&gt; &lt;td&gt;Hopper (2022)&lt;/td&gt; &lt;td&gt;Blackwell (2025)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;공정&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;TSMC 7nm&lt;/td&gt; &lt;td&gt;TSMC 4N&lt;/td&gt; &lt;td&gt;TSMC 4NP&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;트랜지스터&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;54.2B&lt;/td&gt; &lt;td&gt;80B&lt;/td&gt; &lt;td&gt;208B (듀얼 다이)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;SM 수&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;108&lt;/td&gt; &lt;td&gt;132&lt;/td&gt; &lt;td&gt;~148 × 2 다이&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Tensor Core&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;3세대&lt;/td&gt; &lt;td&gt;4세대&lt;/td&gt; &lt;td&gt;5세대&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;메모리&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;80GB HBM2e&lt;/td&gt; &lt;td&gt;80GB HBM3&lt;/td&gt; &lt;td&gt;192GB HBM3e&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;메모리 대역폭&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;2.0 TB/s&lt;/td&gt; &lt;td&gt;3.35 TB/s&lt;/td&gt; &lt;td&gt;8.0 TB/s&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;NVLink&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;600 GB/s&lt;/td&gt; &lt;td&gt;900 GB/s&lt;/td&gt; &lt;td&gt;1.8 TB/s&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;TDP&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;400W&lt;/td&gt; &lt;td&gt;700W&lt;/td&gt; &lt;td&gt;1,000W&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h1 id=&quot;2-llm-학습-관점-얼마나-빨리-학습할-수-있는가&quot;&gt;2. LLM 학습 관점: “얼마나 빨리 학습할 수 있는가”&lt;/h1&gt; &lt;h2 id=&quot;21-tensor-core-성능-정밀도가-핵심&quot;&gt;2.1 Tensor Core 성능: 정밀도가 핵심&lt;/h2&gt; &lt;p&gt;LLM 학습에서 가장 많은 시간을 차지하는 연산은 &lt;strong&gt;행렬곱(MatMul)&lt;/strong&gt;이다. Linear layer, Attention의 \(QK^\top\)와 \(PV\) 모두 matmul이다. Tensor Core의 정밀도별 처리량이 곧 학습 속도를 결정한다.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;정밀도&lt;/th&gt; &lt;th&gt;&lt;strong&gt;A100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;H100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;B200&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;용도&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;FP32&lt;/td&gt; &lt;td&gt;19.5 TF&lt;/td&gt; &lt;td&gt;67 TF&lt;/td&gt; &lt;td&gt;80 TF&lt;/td&gt; &lt;td&gt;디버깅, 정밀 연산&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;TF32&lt;/td&gt; &lt;td&gt;156 TF&lt;/td&gt; &lt;td&gt;495 TF&lt;/td&gt; &lt;td&gt;~1,000 TF&lt;/td&gt; &lt;td&gt;PyTorch 기본 학습&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;BF16&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;312 TF&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;989 TF&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;2,250 TF&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;LLM 학습 표준&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;FP8&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1,979 TF&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;4,500 TF&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;대규모 학습 가속&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FP4&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;9,000 TF&lt;/td&gt; &lt;td&gt;추론 최적화&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h3 id=&quot;실무-포인트&quot;&gt;실무 포인트&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;BF16이 LLM 학습의 사실상 표준&lt;/strong&gt;이다. FP16 대비 dynamic range가 넓어서 loss scaling 없이도 안정적이다. A100의 312 TFLOPS → H100의 989 TFLOPS → B200의 2,250 TFLOPS로, 세대마다 &lt;strong&gt;약 2-3배씩&lt;/strong&gt; 빨라진다.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;FP8 학습&lt;/strong&gt;은 H100부터 지원된다. Transformer Engine이 layer별로 FP8/BF16을 자동 전환하여, BF16 대비 &lt;strong&gt;약 1.5-2배&lt;/strong&gt; 추가 speedup을 제공한다. 다만 학습 안정성이 모델과 데이터에 따라 달라서, 아직 모든 상황에서 사용하지는 않는다.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;FP4는 주로 추론용&lt;/strong&gt;이다. 학습에 사용하기에는 정밀도가 부족하지만, 양자화된 모델의 추론 속도를 극대화할 수 있다.&lt;/p&gt; &lt;h2 id=&quot;22-실제-활용률-이론-vs-현실&quot;&gt;2.2 실제 활용률: 이론 vs 현실&lt;/h2&gt; &lt;p&gt;이론 성능은 좋지만, 실제로 달성하는 &lt;strong&gt;Model FLOPs Utilization (MFU)&lt;/strong&gt;은 다르다.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;&lt;strong&gt;A100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;H100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;B200&lt;/strong&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;이론 BF16&lt;/td&gt; &lt;td&gt;312 TF&lt;/td&gt; &lt;td&gt;989 TF&lt;/td&gt; &lt;td&gt;2,250 TF&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;일반적인 MFU&lt;/td&gt; &lt;td&gt;40-55%&lt;/td&gt; &lt;td&gt;35-50%&lt;/td&gt; &lt;td&gt;30-45% (추정)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;실제 학습 처리량&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;~130-170 TF&lt;/td&gt; &lt;td&gt;~350-500 TF&lt;/td&gt; &lt;td&gt;~700-1000 TF&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;MFU가 세대가 올라갈수록 &lt;strong&gt;약간 낮아지는 경향&lt;/strong&gt;이 있다. 이는 Tensor Core가 빨라지는 속도를 메모리 대역폭과 interconnect가 따라가지 못하기 때문이다. 이것이 바로 FlashAttention 같은 &lt;strong&gt;IO-aware 알고리즘&lt;/strong&gt;이 점점 더 중요해지는 이유다.&lt;/p&gt; &lt;h2 id=&quot;23-메모리-대역폭-tensor-core를-먹여살릴-수-있는가&quot;&gt;2.3 메모리 대역폭: “Tensor Core를 먹여살릴 수 있는가”&lt;/h2&gt; &lt;p&gt;행렬곱을 수행하려면 Tensor Core에 데이터를 &lt;strong&gt;공급&lt;/strong&gt;해야 한다. 아무리 Tensor Core가 빨라도 데이터가 도착하지 않으면 놀게 된다.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Arithmetic Intensity&lt;/strong&gt; (연산 강도)를 계산해보면:&lt;/p&gt; \[\text{AI} = \frac{\text{FLOPs}}{\text{Bytes}} = \frac{2mnk}{2(mk + kn + mn)} \approx \frac{mnk}{mk + kn + mn}\] &lt;p&gt;LLM의 대표적 연산인 \([B \times S, H] \times [H, 4H]\) (FFN의 첫 번째 linear):&lt;/p&gt; &lt;ul&gt; &lt;li&gt;\(B \times S = 2048\) (batch × seq), \(H = 4096\), \(4H = 16384\)&lt;/li&gt; &lt;li&gt;AI ≈ \(\frac{2048 \times 4096 \times 16384}{2 \times (2048 \times 16384 + 16384 \times 4096 + 2048 \times 4096)} \approx 1365\)&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;이 정도의 AI면 &lt;strong&gt;모든 GPU에서 compute-bound&lt;/strong&gt;이므로, 큰 MatMul은 Tensor Core 성능에 비례한다.&lt;/p&gt; &lt;p&gt;하지만 &lt;strong&gt;Attention&lt;/strong&gt;은 다르다. \(QK^\top\)는 \([B \times H, S, d] \times [B \times H, d, S]\)로, \(d = 128\)이면 AI가 약 64로 낮다. 시퀀스가 짧으면 &lt;strong&gt;memory-bound&lt;/strong&gt;가 될 수 있다. 이때 메모리 대역폭이 중요하다.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;&lt;strong&gt;A100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;H100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;B200&lt;/strong&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;BF16 Tensor Core&lt;/td&gt; &lt;td&gt;312 TF&lt;/td&gt; &lt;td&gt;989 TF&lt;/td&gt; &lt;td&gt;2,250 TF&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;메모리 대역폭&lt;/td&gt; &lt;td&gt;2.0 TB/s&lt;/td&gt; &lt;td&gt;3.35 TB/s&lt;/td&gt; &lt;td&gt;8.0 TB/s&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Ops:Byte&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;156&lt;/td&gt; &lt;td&gt;&lt;strong&gt;295&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;281&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Ops:Byte가 높을수록 memory-bound 연산에서 &lt;strong&gt;Tensor Core가 놀 확률이 높다&lt;/strong&gt;. H100과 B200은 A100 대비 이 비율이 2배 가까이 높아서, &lt;strong&gt;FlashAttention 같은 IO 최적화가 더욱 중요&lt;/strong&gt;하다.&lt;/p&gt; &lt;hr /&gt; &lt;h1 id=&quot;3-llm-추론-관점-얼마나-빨리-토큰을-생성하는가&quot;&gt;3. LLM 추론 관점: “얼마나 빨리 토큰을 생성하는가”&lt;/h1&gt; &lt;p&gt;LLM 추론은 학습과 전혀 다른 특성을 가진다.&lt;/p&gt; &lt;h2 id=&quot;31-prefill-vs-decode&quot;&gt;3.1 Prefill vs Decode&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;단계&lt;/th&gt; &lt;th&gt;특성&lt;/th&gt; &lt;th&gt;병목&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Prefill&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;프롬프트 전체를 한 번에 처리, 큰 MatMul&lt;/td&gt; &lt;td&gt;&lt;strong&gt;Compute-bound&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Decode&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;토큰 하나씩 생성, 배치 1의 작은 MatMul&lt;/td&gt; &lt;td&gt;&lt;strong&gt;Memory bandwidth-bound&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;&lt;strong&gt;Decode가 추론의 대부분 시간을 차지&lt;/strong&gt;한다. Decode에서는 weight 전체를 읽어서 하나의 토큰만 생성하므로, AI가 매우 낮다 (≈ 1). 이때 성능은 &lt;strong&gt;순수하게 메모리 대역폭에 의해 결정&lt;/strong&gt;된다.&lt;/p&gt; &lt;h2 id=&quot;32-decode-처리량-계산&quot;&gt;3.2 Decode 처리량 계산&lt;/h2&gt; \[\text{Tokens/s} = \frac{\text{Memory Bandwidth}}{2 \times \text{Model Parameters}} \text{ (BF16 기준)}\] &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;모델&lt;/th&gt; &lt;th&gt;&lt;strong&gt;A100 (2 TB/s)&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;H100 (3.35 TB/s)&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;B200 (8 TB/s)&lt;/strong&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Llama-7B (14 GB)&lt;/td&gt; &lt;td&gt;143 tok/s&lt;/td&gt; &lt;td&gt;239 tok/s&lt;/td&gt; &lt;td&gt;&lt;strong&gt;571 tok/s&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Llama-70B (140 GB)&lt;/td&gt; &lt;td&gt;14.3 tok/s&lt;/td&gt; &lt;td&gt;23.9 tok/s&lt;/td&gt; &lt;td&gt;&lt;strong&gt;57.1 tok/s&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Llama-405B (810 GB)&lt;/td&gt; &lt;td&gt;OOM&lt;/td&gt; &lt;td&gt;OOM&lt;/td&gt; &lt;td&gt;OOM (멀티 GPU)&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;이 계산은 &lt;strong&gt;단일 GPU, 배치 1, BF16&lt;/strong&gt; 기준이다. 실제로는:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;양자화&lt;/strong&gt; (INT8, FP8, INT4)를 적용하면 모델 크기가 줄어 처리량 증가&lt;/li&gt; &lt;li&gt;&lt;strong&gt;배치 크기&lt;/strong&gt;를 키우면 compute-bound 영역으로 이동하여 Tensor Core 활용 가능&lt;/li&gt; &lt;li&gt;&lt;strong&gt;KV cache&lt;/strong&gt;가 추가 메모리를 소비&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;33-kv-cache-시퀀스가-길면-메모리가-부족하다&quot;&gt;3.3 KV Cache: “시퀀스가 길면 메모리가 부족하다”&lt;/h2&gt; &lt;p&gt;Autoregressive 생성에서 이전 토큰의 Key, Value를 캐싱한다. KV cache 크기:&lt;/p&gt; \[\text{KV Cache} = 2 \times L \times 2 \times n_h \times d \times S \times B \text{ bytes (BF16)}\] &lt;p&gt;Llama-70B (\(L=80, n_h=8 \text{ (GQA)}, d=128\)) 기준:&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;시퀀스 길이&lt;/th&gt; &lt;th&gt;Batch 1&lt;/th&gt; &lt;th&gt;Batch 32&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;4K&lt;/td&gt; &lt;td&gt;0.16 GB&lt;/td&gt; &lt;td&gt;5.2 GB&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;32K&lt;/td&gt; &lt;td&gt;1.3 GB&lt;/td&gt; &lt;td&gt;41.6 GB&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;128K&lt;/td&gt; &lt;td&gt;5.2 GB&lt;/td&gt; &lt;td&gt;&lt;strong&gt;166.4 GB&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;A100 80GB에서는 128K 시퀀스 + 배치 32가 &lt;strong&gt;불가능&lt;/strong&gt;하다 (모델 140GB + KV 166GB &amp;gt; 80GB). H100도 마찬가지. &lt;strong&gt;B200의 192GB&lt;/strong&gt;에서야 가능해지며, 이것이 메모리 용량 증가의 실질적 의미다.&lt;/p&gt; &lt;h2 id=&quot;34-정밀도와-양자화&quot;&gt;3.4 정밀도와 양자화&lt;/h2&gt; &lt;p&gt;추론에서는 학습보다 낮은 정밀도를 사용할 수 있다.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;정밀도&lt;/th&gt; &lt;th&gt;모델 크기 (70B)&lt;/th&gt; &lt;th&gt;지원 GPU&lt;/th&gt; &lt;th&gt;품질 영향&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;BF16&lt;/td&gt; &lt;td&gt;140 GB&lt;/td&gt; &lt;td&gt;A100+&lt;/td&gt; &lt;td&gt;기준&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FP8&lt;/td&gt; &lt;td&gt;70 GB&lt;/td&gt; &lt;td&gt;H100+&lt;/td&gt; &lt;td&gt;거의 없음&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;INT8 (W8A8)&lt;/td&gt; &lt;td&gt;70 GB&lt;/td&gt; &lt;td&gt;A100+&lt;/td&gt; &lt;td&gt;미미&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;INT4 (GPTQ/AWQ)&lt;/td&gt; &lt;td&gt;35 GB&lt;/td&gt; &lt;td&gt;A100+&lt;/td&gt; &lt;td&gt;약간&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FP4&lt;/td&gt; &lt;td&gt;35 GB&lt;/td&gt; &lt;td&gt;&lt;strong&gt;B200만&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;모델 의존적&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;&lt;strong&gt;FP8이 현재 가장 실용적인 선택&lt;/strong&gt;이다. 모델 크기가 절반이 되어 메모리 대역폭 2배 + Tensor Core 2배로, BF16 대비 이론적으로 &lt;strong&gt;4배&lt;/strong&gt; 빠르다. H100의 Transformer Engine이 FP8을 자동 관리해준다.&lt;/p&gt; &lt;p&gt;B200에서 추가된 &lt;strong&gt;FP4는 이론적으로 BF16 대비 8배&lt;/strong&gt; 빠르지만, 품질 저하 없이 사용하려면 정교한 양자화 기법(GPTQ, AWQ, SqueezeLLM 등)이 필요하다.&lt;/p&gt; &lt;hr /&gt; &lt;h1 id=&quot;4-멀티-gpu-스케일링-gpu를-더-쓰면-비례해서-빨라지는가&quot;&gt;4. 멀티 GPU 스케일링: “GPU를 더 쓰면 비례해서 빨라지는가”&lt;/h1&gt; &lt;h2 id=&quot;41-nvlink-gpu-간-통신&quot;&gt;4.1 NVLink: GPU 간 통신&lt;/h2&gt; &lt;p&gt;LLM은 단일 GPU에 올라가지 않으므로, &lt;strong&gt;Tensor Parallelism (TP)&lt;/strong&gt;, &lt;strong&gt;Pipeline Parallelism (PP)&lt;/strong&gt;, &lt;strong&gt;Data Parallelism (DP)&lt;/strong&gt;을 조합한다. 이때 GPU 간 통신 대역폭이 스케일링을 결정한다.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;&lt;strong&gt;A100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;H100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;B200&lt;/strong&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;NVLink&lt;/td&gt; &lt;td&gt;600 GB/s&lt;/td&gt; &lt;td&gt;900 GB/s&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1,800 GB/s&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;GPU 간 대역폭 / 메모리 대역폭&lt;/td&gt; &lt;td&gt;30%&lt;/td&gt; &lt;td&gt;27%&lt;/td&gt; &lt;td&gt;&lt;strong&gt;23%&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;NVLink 대역폭이 절대적으로는 증가하지만, &lt;strong&gt;메모리 대역폭 대비 비율은 오히려 감소&lt;/strong&gt;하고 있다. 이는 TP를 많이 쓸수록 통신 overhead가 상대적으로 커진다는 의미다.&lt;/p&gt; &lt;h3 id=&quot;실무-가이드라인&quot;&gt;실무 가이드라인&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;TP 8 이하&lt;/strong&gt;를 유지하는 것이 효율적. TP 16 이상은 all-reduce 비용이 급증&lt;/li&gt; &lt;li&gt;70B 모델: TP=8이면 GPU당 약 9-10GB로 적절&lt;/li&gt; &lt;li&gt;405B 모델: TP=8 + PP=4 또는 TP=8 + PP=8이 일반적&lt;/li&gt; &lt;li&gt;&lt;strong&gt;DeepSpeed ZeRO-3&lt;/strong&gt; + FSDP: DP 방향으로 메모리 분산, 통신은 gradient 동기화만&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;42-dgx-시스템-비교&quot;&gt;4.2 DGX 시스템 비교&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;&lt;strong&gt;DGX A100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;DGX H100&lt;/strong&gt;&lt;/th&gt; &lt;th&gt;&lt;strong&gt;DGX B200&lt;/strong&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;GPU 수&lt;/td&gt; &lt;td&gt;8 × A100&lt;/td&gt; &lt;td&gt;8 × H100&lt;/td&gt; &lt;td&gt;8 × B200&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;총 메모리&lt;/td&gt; &lt;td&gt;640 GB&lt;/td&gt; &lt;td&gt;640 GB&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1,536 GB&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;총 BF16 성능&lt;/td&gt; &lt;td&gt;2.5 PF&lt;/td&gt; &lt;td&gt;7.9 PF&lt;/td&gt; &lt;td&gt;&lt;strong&gt;18 PF&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;총 NVLink BW&lt;/td&gt; &lt;td&gt;4.8 TB/s&lt;/td&gt; &lt;td&gt;7.2 TB/s&lt;/td&gt; &lt;td&gt;&lt;strong&gt;14.4 TB/s&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;가격&lt;/td&gt; &lt;td&gt;~$200K&lt;/td&gt; &lt;td&gt;~$300K&lt;/td&gt; &lt;td&gt;~$500K&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;DGX B200 한 대에서 &lt;strong&gt;1.5TB 메모리&lt;/strong&gt;를 사용할 수 있어, 405B 모델(BF16 810GB)도 단일 노드에서 추론이 가능하다.&lt;/p&gt; &lt;hr /&gt; &lt;h1 id=&quot;5-아키텍처별-핵심-신기능과-llm-영향&quot;&gt;5. 아키텍처별 핵심 신기능과 LLM 영향&lt;/h1&gt; &lt;h2 id=&quot;ampere-a100&quot;&gt;Ampere (A100)&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;기능&lt;/th&gt; &lt;th&gt;LLM 영향&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;BF16/TF32&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;혼합 정밀도 학습의 표준 확립&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;MIG&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;하나의 A100을 최대 7개로 분할 → 소형 모델 추론 효율화&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;3세대 NVLink&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;8-GPU TP 가능&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h2 id=&quot;hopper-h100&quot;&gt;Hopper (H100)&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;기능&lt;/th&gt; &lt;th&gt;LLM 영향&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;FP8 + Transformer Engine&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;학습/추론 모두 FP8 자동 적용 → 2배 speedup&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;TMA (Tensor Memory Accelerator)&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;FlashAttention-3의 핵심 — HBM↔SMEM 전송 자동화&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;WGMMA (비동기 MMA)&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;연산과 데이터 전송을 겹쳐 실행 → GPU 활용률 향상&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Warp Specialization&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;Producer/Consumer warp 분리로 파이프라인 최적화&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h2 id=&quot;blackwell-b200&quot;&gt;Blackwell (B200)&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;기능&lt;/th&gt; &lt;th&gt;LLM 영향&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;FP4 네이티브&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;INT4 양자화 추론이 하드웨어 수준에서 가속&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;192GB HBM3e&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;70B 모델을 양자화 없이 단일 GPU에 탑재 가능&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;8 TB/s 대역폭&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;Decode 처리량 2.4배 향상 → 추론 latency 대폭 감소&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Tensor Memory (256KB/SM)&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;MMA accumulator 전용 메모리 → register 압력 해소&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;2-CTA MMA&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;큰 타일의 MMA를 2개 CTA가 협력 실행 → SMEM 트래픽 절반&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;듀얼 다이&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;2개 다이를 10 TB/s로 연결 → 사실상 하나의 거대 GPU&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h1 id=&quot;6-비대칭-스케일링-왜-알고리즘이-중요한가&quot;&gt;6. 비대칭 스케일링: 왜 알고리즘이 중요한가&lt;/h1&gt; &lt;p&gt;세대별로 하드웨어 발전 속도가 &lt;strong&gt;균일하지 않다&lt;/strong&gt;는 것이 핵심이다.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;하드웨어&lt;/th&gt; &lt;th&gt;A100 → H100&lt;/th&gt; &lt;th&gt;H100 → B200&lt;/th&gt; &lt;th&gt;2세대 합산&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Tensor Core (BF16)&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;3.2×&lt;/td&gt; &lt;td&gt;2.3×&lt;/td&gt; &lt;td&gt;&lt;strong&gt;7.2×&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;메모리 대역폭&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;1.7×&lt;/td&gt; &lt;td&gt;2.4×&lt;/td&gt; &lt;td&gt;&lt;strong&gt;4.0×&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;MUFU (exp 등)&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;~1×&lt;/td&gt; &lt;td&gt;~1×&lt;/td&gt; &lt;td&gt;&lt;strong&gt;~1×&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;SMEM 대역폭&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;~1×&lt;/td&gt; &lt;td&gt;~1×&lt;/td&gt; &lt;td&gt;&lt;strong&gt;~1×&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Tensor Core는 2세대에 걸쳐 &lt;strong&gt;7배&lt;/strong&gt; 빨라졌지만, softmax의 exp를 계산하는 MUFU는 &lt;strong&gt;그대로&lt;/strong&gt;이다. 이것이 의미하는 바:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;단순히 GPU를 바꾸는 것만으로는 성능이 비례 증가하지 않는다.&lt;/strong&gt; Tensor Core가 7배 빨라져도, exp가 병목이면 전체 속도는 제한된다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;FlashAttention 같은 IO/compute-aware 알고리즘이 점점 더 중요해진다.&lt;/strong&gt; 하드웨어의 비대칭을 소프트웨어로 보상해야 한다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;세대별로 다른 최적화 전략이 필요하다.&lt;/strong&gt; FA1/FA2는 HBM IO를 줄이고, FA3는 GEMM과 softmax를 겹치고, FA4는 exp를 소프트웨어로 에뮬레이션한다.&lt;/li&gt; &lt;/ol&gt; &lt;hr /&gt; &lt;h1 id=&quot;7-실무-가이드라인&quot;&gt;7. 실무 가이드라인&lt;/h1&gt; &lt;h2 id=&quot;gpu-선택&quot;&gt;GPU 선택&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;상황&lt;/th&gt; &lt;th&gt;추천&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;7B 모델 fine-tuning&lt;/td&gt; &lt;td&gt;A100 80GB 1장이면 충분&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;70B 모델 학습&lt;/td&gt; &lt;td&gt;H100 8장 (DGX H100) 이상&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;70B 모델 추론 (저비용)&lt;/td&gt; &lt;td&gt;A100 + INT4 양자화&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;70B 모델 추론 (고성능)&lt;/td&gt; &lt;td&gt;H100 + FP8&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;405B 모델 학습&lt;/td&gt; &lt;td&gt;H100/B200 수백 장&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;128K+ long context 추론&lt;/td&gt; &lt;td&gt;&lt;strong&gt;B200&lt;/strong&gt; (192GB 메모리 필수)&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h2 id=&quot;정밀도-선택&quot;&gt;정밀도 선택&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;정밀도&lt;/th&gt; &lt;th&gt;학습&lt;/th&gt; &lt;th&gt;추론&lt;/th&gt; &lt;th&gt;주의사항&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;BF16&lt;/td&gt; &lt;td&gt;✅ 기본&lt;/td&gt; &lt;td&gt;✅ 안정적&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FP8&lt;/td&gt; &lt;td&gt;⚠️ H100+&lt;/td&gt; &lt;td&gt;✅ 추천&lt;/td&gt; &lt;td&gt;Transformer Engine 필요&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;INT8&lt;/td&gt; &lt;td&gt;❌&lt;/td&gt; &lt;td&gt;✅&lt;/td&gt; &lt;td&gt;GPTQ/AWQ 양자화 필요&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;INT4&lt;/td&gt; &lt;td&gt;❌&lt;/td&gt; &lt;td&gt;✅ 비용 효율&lt;/td&gt; &lt;td&gt;품질 저하 모니터링 필요&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FP4&lt;/td&gt; &lt;td&gt;❌&lt;/td&gt; &lt;td&gt;⚠️ B200만&lt;/td&gt; &lt;td&gt;아직 초기 단계&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h2 id=&quot;flashattention-버전-선택&quot;&gt;FlashAttention 버전 선택&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;GPU&lt;/th&gt; &lt;th&gt;추천 FA 버전&lt;/th&gt; &lt;th&gt;이유&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;A100&lt;/td&gt; &lt;td&gt;FA2&lt;/td&gt; &lt;td&gt;A100에 최적화된 알고리즘&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;H100&lt;/td&gt; &lt;td&gt;FA3 (또는 FA2)&lt;/td&gt; &lt;td&gt;WGMMA/TMA 활용, FP8 지원&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;B200&lt;/td&gt; &lt;td&gt;FA4&lt;/td&gt; &lt;td&gt;Blackwell 전용 파이프라인, SW exp&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h1 id=&quot;마치며&quot;&gt;마치며&lt;/h1&gt; &lt;p&gt;GPU 아키텍처를 이해하면 “왜 이 설정에서 성능이 안 나오는지”, “왜 이 최적화가 효과적인지”를 &lt;strong&gt;근본적으로&lt;/strong&gt; 이해할 수 있다. LLM 엔지니어에게 가장 중요한 insight는:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;LLM 학습은 BF16 Tensor Core 성능에 의해 결정&lt;/strong&gt;되지만, 실제 활용률은 메모리 대역폭과 IO 패턴에 의해 제한된다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;LLM 추론(decode)은 거의 순수하게 메모리 대역폭에 의해 결정&lt;/strong&gt;된다. Tensor Core 성능은 decode에서 거의 의미가 없다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;하드웨어가 비대칭적으로 발전&lt;/strong&gt;하면서, 소프트웨어 최적화(FlashAttention, 양자화, KV cache 관리)의 중요성이 점점 커지고 있다.&lt;/li&gt; &lt;/ol&gt; &lt;blockquote&gt; &lt;p&gt;GPU별 최적화 사례가 궁금하다면: &lt;a href=&quot;/blog/2023/fastattention/&quot;&gt;FlashAttention (A100)&lt;/a&gt;, &lt;a href=&quot;/blog/2023/flashattention-2/&quot;&gt;FlashAttention-2 (A100)&lt;/a&gt;, &lt;a href=&quot;/blog/2026/flashattention-3/&quot;&gt;FlashAttention-3 (H100)&lt;/a&gt;, &lt;a href=&quot;/blog/2026/flashattention-4/&quot;&gt;FlashAttention-4 (B200)&lt;/a&gt;를 참고하자.&lt;/p&gt; &lt;/blockquote&gt; &lt;hr /&gt; &lt;h1 id=&quot;참고-문헌&quot;&gt;참고 문헌&lt;/h1&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://www.exxactcorp.com/blog/hpc/comparing-nvidia-tensor-core-gpus&quot;&gt;Comparing Blackwell vs Hopper — Exxact&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://bizon-tech.com/blog/nvidia-b200-b100-h200-h100-a100-comparison&quot;&gt;NVIDIA B200 B100 H200 H100 A100 Comparison — BIZON&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://intuitionlabs.ai/articles/nvidia-data-center-gpu-specs&quot;&gt;NVIDIA Data Center GPU Specs — IntuitionLabs&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://intuitionlabs.ai/articles/blackwell-vs-hopper-gpu-architecture-comparison&quot;&gt;Blackwell vs Hopper GPU Architecture Comparison — IntuitionLabs&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://www.faceofit.com/nvidia-b200-vs-h100-vs-a100/&quot;&gt;NVIDIA B200 vs H100 vs A100 — FaceOfIT&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://modal.com/blog/h100-and-h200-vs-b100-and-b200&quot;&gt;NVIDIA H100s and H200s vs B100s and B200s — Modal&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://nvdam.widen.net/s/xqt56dflgh/nvidia-blackwell-architecture-technical-brief&quot;&gt;NVIDIA Blackwell Architecture Technical Brief&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://resources.nvidia.com/en-us-tensor-core&quot;&gt;NVIDIA H100 Tensor Core GPU Architecture&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html&quot;&gt;NVIDIA CUDA Programming Guide&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; </description> <pubDate>Sat, 11 Apr 2026 09:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2026/gpu-architecture-for-llm-engineers/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2026/gpu-architecture-for-llm-engineers/</guid> <category>gpu</category> <category>llm</category> <category>hardware-optimization</category> <category>dev</category> </item> <item> <title>FlashAttention-4: Algorithm and Kernel Pipelining Co-Design for Asymmetric Hardware Scaling</title> <description>&lt;blockquote&gt; &lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2603.05451&quot;&gt;FlashAttention-4: Algorithm and Kernel Pipelining Co-Design for Asymmetric Hardware Scaling&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt; &lt;p&gt;&lt;a href=&quot;/blog/2026/flashattention-3/&quot;&gt;FlashAttention-3&lt;/a&gt;는 Hopper GPU(H100)의 비동기 실행과 FP8을 활용하여 GPU 이론 성능의 75%를 달성했다. 하지만 FA3는 H100에 특화되어 있으며, AI 산업은 이미 Blackwell 기반 시스템(B200, GB200)으로 빠르게 전환하고 있다.&lt;/p&gt; &lt;p&gt;문제는 Blackwell이 Hopper와 &lt;strong&gt;근본적으로 다른 성능 특성&lt;/strong&gt;을 가진다는 점이다. Tensor Core 처리량은 2배로 증가했지만, shared memory 대역폭과 지수함수(exponential) 유닛은 거의 그대로이다. 이런 &lt;strong&gt;비대칭적 스케일링(asymmetric scaling)&lt;/strong&gt; 때문에, FA3의 알고리즘을 Blackwell에 그대로 이식하면 성능이 크게 제한된다.&lt;/p&gt; &lt;p&gt;저자는 이 비대칭 하드웨어 스케일링을 정면으로 다루는 &lt;strong&gt;FlashAttention-4&lt;/strong&gt;를 제안한다. 핵심 기법은 다음과 같다.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;파이프라인 재설계&lt;/strong&gt;: Blackwell의 완전 비동기 MMA와 더 큰 타일 크기를 활용하는 새로운 소프트웨어 파이프라인&lt;/li&gt; &lt;li&gt;&lt;strong&gt;지수함수 병목 완화&lt;/strong&gt;: 다항식 근사를 통한 소프트웨어 에뮬레이션 + 불필요한 softmax rescaling을 건너뛰는 조건부 rescaling&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Shared memory 트래픽 감소&lt;/strong&gt;: Tensor Memory 활용과 backward pass에서의 2-CTA MMA 모드&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;FlashAttention-4는 B200 GPU에서 BF16 기준 cuDNN 9.13 대비 &lt;strong&gt;1.3배&lt;/strong&gt;, Triton 대비 &lt;strong&gt;2.7배&lt;/strong&gt; 빠르며, 최대 &lt;strong&gt;1613 TFLOPS/s (71% utilization)&lt;/strong&gt;를 달성한다.&lt;/p&gt; &lt;p&gt;또한 FA4는 &lt;strong&gt;CuTe-DSL&lt;/strong&gt;(Python 기반)로 전체 구현하여, 기존 C++ 템플릿 대비 컴파일 시간을 &lt;strong&gt;20-30배&lt;/strong&gt; 단축했다.&lt;/p&gt; &lt;hr /&gt; &lt;h1 id=&quot;background-blackwell-gpu의-비대칭-스케일링&quot;&gt;Background: Blackwell GPU의 비대칭 스케일링&lt;/h1&gt; &lt;h2 id=&quot;hopper-vs-blackwell-하드웨어-비교&quot;&gt;Hopper vs Blackwell 하드웨어 비교&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;하드웨어&lt;/th&gt; &lt;th&gt;Hopper (H100)&lt;/th&gt; &lt;th&gt;Blackwell (B200)&lt;/th&gt; &lt;th&gt;스케일링&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Tensor Core (BF16)&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;1 PFLOPS&lt;/td&gt; &lt;td&gt;&lt;strong&gt;2.25 PFLOPS&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;2.25×&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;MMA 타일 크기&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;64 × N&lt;/td&gt; &lt;td&gt;&lt;strong&gt;128 × N&lt;/strong&gt; (또는 256)&lt;/td&gt; &lt;td&gt;&lt;strong&gt;2×&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;MUFU (exp 등)&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;16 ops/clock/SM&lt;/td&gt; &lt;td&gt;&lt;strong&gt;16 ops/clock/SM&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1× (동일!)&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;SMEM 대역폭&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;128 bytes/clock&lt;/td&gt; &lt;td&gt;&lt;strong&gt;128 bytes/clock&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1× (동일!)&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Tensor Memory&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;없음&lt;/td&gt; &lt;td&gt;&lt;strong&gt;256 KB/SM&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;신규&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;MMA 비동기성&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;register writeback 필요&lt;/td&gt; &lt;td&gt;&lt;strong&gt;TMEM에 직접 쓰기&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;더 높은 비동기성&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;핵심 관찰: &lt;strong&gt;Tensor Core가 2배 이상 빨라졌지만, exp를 계산하는 MUFU와 SMEM 대역폭은 그대로&lt;/strong&gt;이다. FA3에서는 ping-pong 스케줄링으로 softmax를 GEMM 뒤에 숨길 수 있었는데, Blackwell에서는 GEMM이 2배 빨라져서 &lt;strong&gt;softmax를 숨길 시간이 부족&lt;/strong&gt;하다.&lt;/p&gt; &lt;h2 id=&quot;roofline-분석-forward-pass&quot;&gt;Roofline 분석: Forward Pass&lt;/h2&gt; &lt;p&gt;타일 크기 \(M \times N\)과 head dimension \(d\)에 대해, forward pass의 각 리소스별 소요 사이클:&lt;/p&gt; \[T_{\text{MMA}} = \frac{4MNd}{8192} \text{ cycles}\] \[T_{\text{smem}} = \frac{3MNd}{8192} \text{ cycles (대략적)}\] \[T_{\text{exp}} = \frac{MN}{16} \text{ cycles}\] &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Resource&lt;/th&gt; &lt;th&gt;\(128^3\)&lt;/th&gt; &lt;th&gt;\(256 \times 128^2\)&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;MMA compute&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1024&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;2048&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Shared memory&lt;/td&gt; &lt;td&gt;768&lt;/td&gt; &lt;td&gt;1536&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Exponential unit&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1024&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;2048&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;&lt;strong&gt;MMA와 exponential이 동시에 병목&lt;/strong&gt;이다. 즉, exp를 다른 하드웨어에서 실행하지 않으면 Tensor Core가 아무리 빨라도 성능이 제한된다.&lt;/p&gt; &lt;p&gt;이 분석에서 FA4의 설계 원칙이 도출된다.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;큰 타일 크기&lt;/strong&gt;를 사용하여 MMA와 softmax의 overlap을 극대화&lt;/li&gt; &lt;li&gt;&lt;strong&gt;exp의 처리량을 높이기&lt;/strong&gt; 위해 FMA 유닛으로 소프트웨어 에뮬레이션&lt;/li&gt; &lt;li&gt;&lt;strong&gt;불필요한 non-matmul 연산을 줄이기&lt;/strong&gt; — 조건부 rescaling&lt;/li&gt; &lt;/ol&gt; &lt;hr /&gt; &lt;h1 id=&quot;forward-pass-파이프라인-재설계&quot;&gt;Forward Pass: 파이프라인 재설계&lt;/h1&gt; &lt;h2 id=&quot;fa3-vs-fa4-파이프라인-비교&quot;&gt;FA3 vs FA4 파이프라인 비교&lt;/h2&gt; &lt;p&gt;FA3는 2개 warpgroup의 ping-pong 스케줄링을 사용했다. FA4는 이를 확장하여 &lt;strong&gt;역할별 전문화된 warp 그룹&lt;/strong&gt;을 사용한다.&lt;/p&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention4/fa4_fwd_pipeline.png&quot; width=&quot;100%&quot; /&gt; &lt;/p&gt; &lt;h3 id=&quot;blackwell에서의-핵심-변화&quot;&gt;Blackwell에서의 핵심 변화&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;1. Accumulator가 Tensor Memory에 저장&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Hopper에서는 MMA의 accumulator가 register에 저장되어, softmax warpgroup이 register에서 값을 읽어야 했다. Blackwell에서는 MMA가 &lt;strong&gt;Tensor Memory(TMEM)&lt;/strong&gt;에 직접 accumulator를 쓴다. 이 덕분에:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Softmax warpgroup이 &lt;strong&gt;TMEM에서 직접&lt;/strong&gt; 값을 읽을 수 있다&lt;/li&gt; &lt;li&gt;Rescaling을 별도의 &lt;strong&gt;correction warpgroup&lt;/strong&gt;으로 분리 가능 — critical path에서 제거&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;&lt;strong&gt;2. 타일 크기 128 × 128&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Hopper의 64 × 128 대비 2배 큰 타일. 한 번의 MMA에 더 많은 연산을 수행하므로, MMA와 softmax의 overlap 기회가 늘어난다.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;3. Warp 역할 분배&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;각 thread당 하나의 행(row)을 담당하여 128개 원소를 register에 로드한다. 구체적으로:&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;역할&lt;/th&gt; &lt;th&gt;수량&lt;/th&gt; &lt;th&gt;기능&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;MMA warpgroup&lt;/td&gt; &lt;td&gt;1&lt;/td&gt; &lt;td&gt;Tensor Core 연산 (\(QK^\top\), \(PV\))&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Softmax warpgroup&lt;/td&gt; &lt;td&gt;2&lt;/td&gt; &lt;td&gt;max, exp, rowsum 계산&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Correction warpgroup&lt;/td&gt; &lt;td&gt;1&lt;/td&gt; &lt;td&gt;Rescaling (\(e^{m_{\text{old}} - m_{\text{new}}}\)로 보정)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;TMA (producer)&lt;/td&gt; &lt;td&gt;-&lt;/td&gt; &lt;td&gt;HBM → SMEM 데이터 로드&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Softmax warpgroup과 correction warpgroup을 분리한 것이 FA3와의 핵심 차이다. Correction은 critical path 밖에서 실행된다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;지수함수-소프트웨어-에뮬레이션&quot;&gt;지수함수 소프트웨어 에뮬레이션&lt;/h2&gt; &lt;h3 id=&quot;문제-mufu-병목&quot;&gt;문제: MUFU 병목&lt;/h3&gt; &lt;p&gt;Blackwell에서 MUFU(Multi-Function Unit)는 clock당 SM당 16개 연산만 처리한다. Tensor Core가 clock당 8192 FLOPs를 처리하는 것과 비교하면 &lt;strong&gt;512배 느리다&lt;/strong&gt;. Head dimension 128 기준으로 forward에서 matmul FLOPs는 exp 연산 대비 512배 많지만, MUFU가 512배 느리므로 &lt;strong&gt;exp가 matmul과 동일한 시간&lt;/strong&gt;을 소비한다.&lt;/p&gt; &lt;h3 id=&quot;해결-다항식-근사&quot;&gt;해결: 다항식 근사&lt;/h3&gt; &lt;p&gt;FMA(Fused Multiply-Add) 유닛은 MUFU와 &lt;strong&gt;독립적으로 병렬 실행&lt;/strong&gt;될 수 있다. 저자는 지수함수를 FMA 기반 다항식으로 근사한다.&lt;/p&gt; &lt;p&gt;핵심 분해:&lt;/p&gt; \[2^x = 2^{\lfloor x \rfloor} \cdot 2^{x_{\text{frac}}}\] &lt;p&gt;여기서 \(x_{\text{frac}} = x - \lfloor x \rfloor \in [0, 1)\)이다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;정수 부분&lt;/strong&gt; \(2^{\lfloor x \rfloor}\): IEEE 754 부동소수점의 exponent 필드를 직접 조작 (integer ALU 명령어)&lt;/li&gt; &lt;li&gt;&lt;strong&gt;소수 부분&lt;/strong&gt; \(2^{x_{\text{frac}}}\): 다항식 근사&lt;/li&gt; &lt;/ul&gt; \[2^{x_{\text{frac}}} \approx \sum_{i=0}^{n} p_i \cdot x_{\text{frac}}^i\] &lt;p&gt;Degree 3 다항식의 경우 3번의 FMA 명령어로 계산 가능하며, BF16 정밀도에서 하드웨어 MUFU와 &lt;strong&gt;거의 구분 불가능&lt;/strong&gt;한 오차를 보인다.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Method&lt;/th&gt; &lt;th&gt;FP32 Max Rel Err&lt;/th&gt; &lt;th&gt;BF16 Max Rel Err&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Hardware MUFU.EX2&lt;/td&gt; &lt;td&gt;\(1.41 \times 10^{-7}\)&lt;/td&gt; &lt;td&gt;\(3.89 \times 10^{-3}\)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Degree 3 polynomial&lt;/td&gt; &lt;td&gt;\(8.77 \times 10^{-5}\)&lt;/td&gt; &lt;td&gt;\(3.90 \times 10^{-3}\)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Degree 5 polynomial&lt;/td&gt; &lt;td&gt;\(1.44 \times 10^{-7}\)&lt;/td&gt; &lt;td&gt;\(3.89 \times 10^{-3}\)&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;FP32 수준에서는 degree 3가 MUFU보다 약 600배 부정확하지만, &lt;strong&gt;BF16으로 반올림하면 양자화 오차가 지배적&lt;/strong&gt;이어서 차이가 사라진다. Degree 3 이상에서 BF16 오차는 모두 \(\sim 3.9 \times 10^{-3}\)으로 동일하다.&lt;/p&gt; &lt;h3 id=&quot;partial-emulation&quot;&gt;Partial Emulation&lt;/h3&gt; &lt;p&gt;모든 exp를 다항식으로 대체하면 register 압력이 증가하고 대역폭이 늘어난다. 따라서 &lt;strong&gt;softmax row의 일부(10-25%)&lt;/strong&gt;에만 선택적으로 적용한다. 나머지는 하드웨어 MUFU를 사용한다. 적용 비율은 MMA와 exp의 처리량 비율에 따라 경험적으로 튜닝한다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;조건부-softmax-rescaling&quot;&gt;조건부 Softmax Rescaling&lt;/h2&gt; &lt;h3 id=&quot;기존-방식의-비효율&quot;&gt;기존 방식의 비효율&lt;/h3&gt; &lt;p&gt;FlashAttention의 online softmax에서, 새로운 블록을 처리할 때마다 max가 바뀌면 이전 결과를 보정해야 한다:&lt;/p&gt; \[m_j = \max(m_{j-1}, \text{rowmax}(S_j))\] \[O_j = e^{m_{j-1} - m_j} O_{j-1} + e^{S_j - m_j} V_j\] &lt;p&gt;이 rescaling (\(e^{m_{j-1} - m_j} O_{j-1}\)) 은 매 블록마다 발생한다. 하지만 실제로 max가 크게 변하지 않으면 \(e^{m_{j-1} - m_j} \approx 1\)이 되어 &lt;strong&gt;불필요한 연산&lt;/strong&gt;이다.&lt;/p&gt; &lt;h3 id=&quot;fa4의-개선-threshold-tau&quot;&gt;FA4의 개선: threshold \(\tau\)&lt;/h3&gt; &lt;p&gt;저자는 threshold \(\tau\)를 도입하여, max의 변화가 충분히 클 때만 rescaling한다:&lt;/p&gt; \[O_j = \begin{cases} e^{m_{j-1} - m_j} O_{j-1} + e^{S_j - m_j} V_j &amp;amp; \text{if } m_j - m_{j-1} &amp;gt; \tau \\ O_{j-1} + e^{S_j - m_{j-1}} V_j &amp;amp; \text{otherwise} \end{cases}\] &lt;p&gt;\(\tau\)가 \(m_{j-1}\)와 \(m_j\)의 차이보다 크면, 이전 max(\(m_{j-1}\))를 그대로 사용한다. 마지막에 true max \(m_{\text{final}}\)과 true normalizer \(\ell_{\text{final}}\)로 한 번만 보정한다.&lt;/p&gt; \[\text{Output} = \frac{1}{\ell_{\text{final}}} O_{\text{final}}\] &lt;p&gt;실용적으로 \(\tau = \log_2(256) = 8.0\)으로 설정한다. 이렇게 하면 rescaling 횟수가 약 &lt;strong&gt;10배 감소&lt;/strong&gt;한다.&lt;/p&gt; &lt;hr /&gt; &lt;h1 id=&quot;backward-pass&quot;&gt;Backward Pass&lt;/h1&gt; &lt;h2 id=&quot;roofline-분석&quot;&gt;Roofline 분석&lt;/h2&gt; &lt;p&gt;Backward pass는 5개의 MMA를 수행한다: \(S^\top = KQ^\top\), \(dP^\top = VdO^\top\), \(dV = P^\top dO\), \(dK = dS^\top Q\), \(dQ = dS \cdot K\).&lt;/p&gt; &lt;p&gt;\(M = N = d = 128\) 기준:&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Resource&lt;/th&gt; &lt;th&gt;1-CTA (\(M=128\))&lt;/th&gt; &lt;th&gt;2-CTA (\(M=256\))&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;MMA compute&lt;/td&gt; &lt;td&gt;2560&lt;/td&gt; &lt;td&gt;2560&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Total shared memory&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;3328&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;2688&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Exponential unit&lt;/td&gt; &lt;td&gt;1024&lt;/td&gt; &lt;td&gt;1024&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;&lt;strong&gt;Shared memory 트래픽이 MMA compute보다 30% 더 크다.&lt;/strong&gt; Forward와 달리 backward에서는 shared memory가 주요 병목이다.&lt;/p&gt; &lt;h2 id=&quot;2-cta-mma-모드&quot;&gt;2-CTA MMA 모드&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention4/fa4_2cta_figure.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;Blackwell은 &lt;strong&gt;2-CTA tensor core MMA 모드&lt;/strong&gt;를 지원한다. 같은 thread block cluster 내의 2개 CTA가 협력하여 하나의 큰 MMA를 실행한다. \(M = 256\), \(N = K = 128\) 크기의 타일을 사용하면:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;각 CTA는 operand B의 &lt;strong&gt;절반만&lt;/strong&gt; shared memory에 로드&lt;/li&gt; &lt;li&gt;나머지 절반은 peer CTA의 shared memory에서 읽음&lt;/li&gt; &lt;li&gt;→ Shared memory 트래픽 &lt;strong&gt;약 절반으로 감소&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;또한 \(dQ\)의 accumulation에서 atomic add가 필요한데, 2-CTA 모드에서는 각 CTA가 \(dQ\) 타일의 절반만 쓰므로 &lt;strong&gt;global atomic reduction 횟수도 절반&lt;/strong&gt;이 된다.&lt;/p&gt; &lt;h2 id=&quot;backward-computation-graph&quot;&gt;Backward Computation Graph&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention4/fa4_bwd_graph.png&quot; width=&quot;100%&quot; /&gt; &lt;/p&gt; &lt;p&gt;FA4의 backward pass는 Prologue → Main Loop → Tail의 3단계로 구성된다. 5개 MMA + 2개 elementwise 연산이 파이프라인으로 실행된다. FA3 대비 핵심 개선:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;TMEM에 accumulator를 저장하여 MMA와 softmax gradient 계산을 overlap&lt;/li&gt; &lt;li&gt;\(dQ\)와 \(dK\)의 MMA를 이전 iteration의 softmax 계산과 병렬 실행&lt;/li&gt; &lt;li&gt;2-CTA 모드로 shared memory 병목 완화&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;deterministic-backward-pass&quot;&gt;Deterministic Backward Pass&lt;/h2&gt; &lt;p&gt;GPU의 atomic reduction은 비결정적(nondeterministic)이다. 강화학습 등 재현 가능한 학습이 필요한 경우를 위해, &lt;strong&gt;deterministic mode&lt;/strong&gt;도 제공한다. Semaphore lock으로 \(dQ\) reduction 순서를 고정하며, CTA swizzling으로 stall을 최소화한다. Nondeterministic 대비 약 &lt;strong&gt;75%의 속도&lt;/strong&gt;를 달성한다.&lt;/p&gt; &lt;hr /&gt; &lt;h1 id=&quot;scheduling-lpt와-causal-masking&quot;&gt;Scheduling: LPT와 Causal Masking&lt;/h1&gt; &lt;h2 id=&quot;longest-processing-time-first-lpt&quot;&gt;Longest-Processing-Time First (LPT)&lt;/h2&gt; &lt;p&gt;Causal masking이나 variable sequence length 상황에서 SM 간 load imbalance가 발생한다. FA4는 &lt;strong&gt;LPT 스케줄링&lt;/strong&gt;을 도입한다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;SM들을 L2 cache를 공유하는 section으로 나누고&lt;/li&gt; &lt;li&gt;각 section 내에서 worktile을 실행 시간이 긴 순서대로 배치&lt;/li&gt; &lt;li&gt;Causal masking에서는 대각선 근처의 긴 worktile을 먼저 처리&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;이 스케줄링은 Hopper에서도 적용 가능하며, BF16 hdim 128 기준 MHA에서 &lt;strong&gt;4-8% FLOPS 향상&lt;/strong&gt;, MQA 8에서 &lt;strong&gt;7-14% 향상&lt;/strong&gt;을 보인다.&lt;/p&gt; &lt;hr /&gt; &lt;h1 id=&quot;language-cute-dsl&quot;&gt;Language: CuTe-DSL&lt;/h1&gt; &lt;p&gt;FA4는 CUDA C++이 아닌 &lt;strong&gt;CuTe-DSL&lt;/strong&gt;(Python 기반)로 전체 구현했다. CuTe-DSL은 CUTLASS의 일부로, Python 코드를 PTX → SASS로 컴파일한다.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;FA3 (C++)&lt;/th&gt; &lt;th&gt;FA4 (CuTe-DSL)&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Forward 컴파일&lt;/td&gt; &lt;td&gt;55s&lt;/td&gt; &lt;td&gt;&lt;strong&gt;2.5s&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Backward 컴파일&lt;/td&gt; &lt;td&gt;45s&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1.4s&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Speedup&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;&lt;strong&gt;22-32×&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;C++ 템플릿 메타프로그래밍의 복잡한 컴파일 과정 없이, Python의 JIT 컴파일을 활용하여 빠른 iteration이 가능하다. FA2, FA3는 수백 개의 커널을 미리 컴파일해야 했지만, FA4는 필요할 때 JIT로 컴파일한다.&lt;/p&gt; &lt;p&gt;이 접근의 장점:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;개발 생산성&lt;/strong&gt;: C++ 템플릿 전문 지식 없이도 GPU 커널 개발 가능&lt;/li&gt; &lt;li&gt;&lt;strong&gt;모듈성&lt;/strong&gt;: Block-sparse, FlexAttention, variable sequence length 등을 독립적인 primitive로 구현하여 자유롭게 조합&lt;/li&gt; &lt;li&gt;&lt;strong&gt;PTX escape hatch&lt;/strong&gt;: CuTe-DSL API에 아직 없는 기능은 직접 PTX를 삽입 가능&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h1 id=&quot;empirical-evaluation&quot;&gt;Empirical Evaluation&lt;/h1&gt; &lt;p&gt;B200 GPU에서 BF16 입력으로 벤치마크를 수행했다. Hidden dimension 2048, head dimension 64 또는 128, 시퀀스 길이 1K-32K.&lt;/p&gt; &lt;h2 id=&quot;forward-pass&quot;&gt;Forward Pass&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention4/fa4_fwd_noncausal_hdim128.png&quot; width=&quot;49%&quot; /&gt; &lt;img src=&quot;/assets/post/image/flashattention4/fa4_fwd_causal_hdim128.png&quot; width=&quot;49%&quot; /&gt; &lt;/p&gt; &lt;p&gt;Head dim 128 기준:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Non-causal&lt;/strong&gt;: FA4가 cuDNN 9.13.0 대비 &lt;strong&gt;1.1-1.3×&lt;/strong&gt; 빠르고, Triton 대비 &lt;strong&gt;2.1-2.7×&lt;/strong&gt; 빠르다&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Causal&lt;/strong&gt;: LPT 스케줄링 덕분에 특히 긴 시퀀스에서 이점이 크다&lt;/li&gt; &lt;li&gt;최대 &lt;strong&gt;1613 TFLOPS/s&lt;/strong&gt; (이론 최대 2250 TFLOPS의 약 71%)&lt;/li&gt; &lt;/ul&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention4/fa4_fwd_causal_hdim192128.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;DeepSeek V3에서 사용하는 head dim (192, 128) 설정에서도 cuDNN 대비 일관되게 우수한 성능을 보인다.&lt;/p&gt; &lt;h2 id=&quot;backward-pass-1&quot;&gt;Backward Pass&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention4/fa4_bwd_noncausal_hdim128.png&quot; width=&quot;49%&quot; /&gt; &lt;img src=&quot;/assets/post/image/flashattention4/fa4_bwd_causal_hdim128.png&quot; width=&quot;49%&quot; /&gt; &lt;/p&gt; &lt;p&gt;Backward에서도 cuDNN 대비 일관된 speedup을 달성한다. 2-CTA 모드가 shared memory 병목을 완화하여 특히 긴 시퀀스에서 효과적이다.&lt;/p&gt; &lt;h2 id=&quot;deterministic-backward-ablation&quot;&gt;Deterministic Backward Ablation&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention4/fa4_bwd_det_causal.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;Deterministic backward의 스케줄링 전략 비교:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;SPT (Shortest-Processing-Time first)&lt;/strong&gt;: Causal에서 최적&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Reverse mblock LPT&lt;/strong&gt;: 차선&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Naive&lt;/strong&gt;: 스케줄링 없이는 성능이 크게 떨어짐&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;CTA swizzling과 LPT 스케줄링 조합이 deterministic 모드에서도 nondeterministic 대비 &lt;strong&gt;75%의 성능&lt;/strong&gt;을 유지하게 한다.&lt;/p&gt; &lt;hr /&gt; &lt;h1 id=&quot;flashattention-시리즈-비교&quot;&gt;FlashAttention 시리즈 비교&lt;/h1&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;FA1&lt;/th&gt; &lt;th&gt;FA2&lt;/th&gt; &lt;th&gt;FA3&lt;/th&gt; &lt;th&gt;&lt;strong&gt;FA4&lt;/strong&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;GPU&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;A100&lt;/td&gt; &lt;td&gt;A100&lt;/td&gt; &lt;td&gt;H100&lt;/td&gt; &lt;td&gt;&lt;strong&gt;B200&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;핵심 아이디어&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;Tiling + Recomputation&lt;/td&gt; &lt;td&gt;non-matmul 감소, split-Q&lt;/td&gt; &lt;td&gt;Ping-pong, FP8&lt;/td&gt; &lt;td&gt;&lt;strong&gt;SW exp, conditional rescale, 2-CTA&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;병목&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;HBM IO&lt;/td&gt; &lt;td&gt;non-matmul FLOPs&lt;/td&gt; &lt;td&gt;GEMM vs softmax&lt;/td&gt; &lt;td&gt;&lt;strong&gt;exp + SMEM&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;MMA 명령&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;mma.sync&lt;/td&gt; &lt;td&gt;mma.sync&lt;/td&gt; &lt;td&gt;WGMMA&lt;/td&gt; &lt;td&gt;&lt;strong&gt;tcgen05.mma&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Accumulator&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;Register&lt;/td&gt; &lt;td&gt;Register&lt;/td&gt; &lt;td&gt;Register&lt;/td&gt; &lt;td&gt;&lt;strong&gt;Tensor Memory&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;BF16 성능&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;~230 TFLOPS&lt;/td&gt; &lt;td&gt;~740 TFLOPS&lt;/td&gt; &lt;td&gt;&lt;strong&gt;~1613 TFLOPS&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;GPU 활용률&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;50-73%&lt;/td&gt; &lt;td&gt;75%&lt;/td&gt; &lt;td&gt;&lt;strong&gt;71%&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;구현 언어&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;CUDA C++&lt;/td&gt; &lt;td&gt;CUDA C++&lt;/td&gt; &lt;td&gt;CUDA C++&lt;/td&gt; &lt;td&gt;&lt;strong&gt;CuTe-DSL (Python)&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;컴파일 시간&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;~55s&lt;/td&gt; &lt;td&gt;~55s&lt;/td&gt; &lt;td&gt;&lt;strong&gt;~2.5s&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h1 id=&quot;discussion-and-conclusion&quot;&gt;Discussion and Conclusion&lt;/h1&gt; &lt;p&gt;FlashAttention-4는 &lt;strong&gt;비대칭 하드웨어 스케일링&lt;/strong&gt;이라는 현대 가속기의 근본적 추세를 정면으로 다룬 논문이다. Tensor Core가 다른 유닛보다 훨씬 빠르게 발전하면서, 병목이 matmul에서 &lt;strong&gt;shared memory 트래픽과 지수함수 처리량&lt;/strong&gt;으로 이동했다. FA4는 이를 세 가지 방향에서 해결한다.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;연산 분산&lt;/strong&gt;: exp를 MUFU에서만 하지 않고 FMA 유닛에도 분산하여 처리량 증가&lt;/li&gt; &lt;li&gt;&lt;strong&gt;불필요한 연산 제거&lt;/strong&gt;: 조건부 rescaling으로 보정 횟수 10배 감소&lt;/li&gt; &lt;li&gt;&lt;strong&gt;메모리 효율&lt;/strong&gt;: TMEM과 2-CTA 모드로 shared memory 병목 완화&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;또한 CuTe-DSL로의 전환은 단순한 구현 선택이 아니라, &lt;strong&gt;attention 커널 개발의 접근성을 크게 높인&lt;/strong&gt; 결정이다. FlexAttention, block-sparse attention 등 다양한 attention variant를 FA4 프레임워크 위에 빠르게 구현할 수 있다.&lt;/p&gt; &lt;p&gt;FA1이 “IO를 줄이자”, FA2가 “non-matmul을 줄이자”, FA3가 “GEMM과 softmax를 겹치자”였다면, FA4는 &lt;strong&gt;“하드웨어의 비대칭을 소프트웨어로 보상하자”&lt;/strong&gt;라는 메시지를 던진다. 가속기가 더 빠르게, 더 비대칭적으로 발전할수록, 이런 하드웨어-소프트웨어 co-design의 중요성은 더 커질 것이다.&lt;/p&gt; &lt;hr /&gt; &lt;blockquote&gt; &lt;p&gt;FlashAttention의 원리가 궁금하다면 &lt;a href=&quot;/blog/2023/fastattention/&quot;&gt;FlashAttention 논문 리뷰&lt;/a&gt;를, 개선점이 궁금하다면 &lt;a href=&quot;/blog/2023/flashattention-2/&quot;&gt;FlashAttention-2 논문 리뷰&lt;/a&gt;를, Hopper GPU 최적화가 궁금하다면 &lt;a href=&quot;/blog/2026/flashattention-3/&quot;&gt;FlashAttention-3 논문 리뷰&lt;/a&gt;를, Triton으로 직접 구현하고 싶다면 &lt;a href=&quot;/blog/2026/triton-05-flash-attention/&quot;&gt;Triton 05: Flash Attention&lt;/a&gt;을 참고하자.&lt;/p&gt; &lt;/blockquote&gt; &lt;hr /&gt; &lt;h1 id=&quot;참고-문헌&quot;&gt;참고 문헌&lt;/h1&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2603.05451&quot;&gt;FlashAttention-4: Algorithm and Kernel Pipelining Co-Design for Asymmetric Hardware Scaling (arXiv)&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://modal.com/blog/reverse-engineer-flash-attention-4&quot;&gt;We reverse-engineered Flash Attention 4 — Modal Blog&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/Dao-AILab/flash-attention&quot;&gt;FlashAttention GitHub — Dao-AILab&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/Dao-AILab/flash-attention/tree/main/flash_attn/cute&quot;&gt;FlashAttention-4 CuTe-DSL Implementation&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://nvdam.widen.net/s/xqt56dflgh/nvidia-blackwell-architecture-technical-brief&quot;&gt;NVIDIA Blackwell Architecture Technical Brief&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://docs.nvidia.com/cutlass/media/docs/pythonDSL/cute_dsl_general/dsl_introduction.html&quot;&gt;NVIDIA CuTe-DSL Documentation&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://docs.nvidia.com/deeplearning/cudnn/backend/latest/release-notes.html&quot;&gt;NVIDIA cuDNN Release Notes&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; </description> <pubDate>Sat, 11 Apr 2026 03:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2026/flashattention-4/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2026/flashattention-4/</guid> <category>attention</category> <category>hardware-optimization</category> <category>paper</category> <category>flash-attention</category> <category>optimization</category> </item> <item> <title>FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision</title> <description>&lt;blockquote&gt; &lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2407.08608&quot;&gt;FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt; &lt;p&gt;Transformer의 핵심인 attention은 시퀀스 길이에 대해 \(O(N^2)\)의 연산량을 가지고, LLM과 long-context 애플리케이션에서 가장 큰 병목이다. &lt;a href=&quot;/blog/2023/fastattention/&quot;&gt;FlashAttention&lt;/a&gt;은 tiling과 kernel fusion으로 HBM IO를 줄여서 이를 해결했고, &lt;a href=&quot;/blog/2023/flashattention-2/&quot;&gt;FlashAttention-2&lt;/a&gt;는 non-matmul FLOPs 감소와 warp partitioning 개선으로 한 단계 더 발전했다.&lt;/p&gt; &lt;p&gt;하지만 FlashAttention-2는 H100 GPU에서 이론 성능의 &lt;strong&gt;35%&lt;/strong&gt;밖에 활용하지 못한다. 최적화된 GEMM 커널이 80~90%를 달성하는 것과 비교하면 매우 낮은 수치다. 그 이유는 FlashAttention-2가 동기적(synchronous) 모델을 사용하고, Hopper 아키텍처의 새로운 하드웨어 기능을 활용하지 않기 때문이다.&lt;/p&gt; &lt;p&gt;근본적으로, FlashAttention-2의 알고리즘은 단순화된 동기 모델을 따르며 비동기성과 저정밀도를 명시적으로 활용하지 않는다. 비동기성은 하드웨어 전문화의 결과다. 행렬곱을 수행하는 Tensor Core, 메모리 로드를 담당하는 TMA 등 서로 독립적인 하드웨어 유닛이 존재하기 때문에, 이들을 동시에 활용하려면 소프트웨어도 이에 맞춰 설계해야 한다.&lt;/p&gt; &lt;p&gt;저자는 이 문제를 해결하기 위해 세 가지 기법을 제안한다.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Producer-Consumer 비동기 처리&lt;/strong&gt;: Warp specialization으로 데이터 전송과 연산을 분리하고, ping-pong 스케줄링으로 GEMM과 softmax를 겹쳐서 실행한다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Intra-warpgroup 파이프라이닝&lt;/strong&gt;: 하나의 warpgroup 안에서도 WGMMA의 비동기 특성을 이용하여 GEMM과 softmax를 겹친다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;FP8 저정밀도 + Incoherent Processing&lt;/strong&gt;: 블록 양자화와 Hadamard 변환으로 FP8의 정확도 손실을 최소화한다.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;결과적으로 FlashAttention-3는 H100에서 FP16 기준 &lt;strong&gt;740 TFLOPS/s (75% 활용률)&lt;/strong&gt;, FP8 기준 &lt;strong&gt;1.2 PFLOPS/s&lt;/strong&gt;를 달성하며, FlashAttention-2 대비 &lt;strong&gt;1.5~2.0배&lt;/strong&gt; 빠르다.&lt;/p&gt; &lt;h1 id=&quot;background&quot;&gt;Background&lt;/h1&gt; &lt;h2 id=&quot;multi-head-attention&quot;&gt;Multi-Head Attention&lt;/h2&gt; &lt;p&gt;Query, Key, Value \(Q, K, V \in \mathbb{R}^{N \times d}\)에 대해 attention output \(O \in \mathbb{R}^{N \times d}\)는 다음과 같이 계산된다.&lt;/p&gt; \[S = \alpha QK^\top \in \mathbb{R}^{N \times N}, \quad P = \text{softmax}(S) \in \mathbb{R}^{N \times N}, \quad O = PV \in \mathbb{R}^{N \times d}\] &lt;p&gt;여기서 \(\alpha = 1/\sqrt{d}\)이고, softmax는 row-wise로 적용된다. 실제로는 수치 안정성을 위해 \(S\)에서 \(\text{rowmax}(S)\)를 빼고 exponential을 취한다.&lt;/p&gt; &lt;p&gt;Multi-head attention(MHA)에서는 각 head가 자기만의 \(Q, K, V\)를 가지며, 여러 head와 batch에 대해 독립적으로 병렬 처리된다.&lt;/p&gt; &lt;h3 id=&quot;backward-pass&quot;&gt;Backward Pass&lt;/h3&gt; &lt;p&gt;손실 함수를 \(\phi\), 그 gradient를 \(d(\cdot) = \partial \phi / \partial (\cdot)\)라고 하자. Output gradient \(dO \in \mathbb{R}^{N \times d}\)가 주어지면 chain rule에 따라 \(dQ, dK, dV\)를 다음과 같이 계산한다.&lt;/p&gt; \[dV = P^\top dO \in \mathbb{R}^{N \times d}\] \[dP = dOV^\top \in \mathbb{R}^{N \times N}\] \[dS = \text{dsoftmax}(dP) \in \mathbb{R}^{N \times N}\] \[dQ = \alpha \cdot dS \cdot K \in \mathbb{R}^{N \times d}\] \[dK = \alpha \cdot dS^\top Q \in \mathbb{R}^{N \times d}\] &lt;p&gt;여기서 \(ds = (\text{diag}(p) - pp^\top)dp\), 즉 \(p = \text{softmax}(s)\)에 대해 row-wise로 적용된다. 이를 \(\text{dsoftmax}(dP)\)라고 쓴다. Forward pass에서 2개의 matmul(\(QK^\top, PV\))이 필요했다면, backward pass에서는 5개의 matmul이 필요하다. 이 때문에 backward의 FLOPs는 forward의 2.5배이다.&lt;/p&gt; &lt;h2 id=&quot;gpu-하드웨어-특성과-실행-모델&quot;&gt;GPU 하드웨어 특성과 실행 모델&lt;/h2&gt; &lt;p&gt;FlashAttention-3를 이해하려면 Hopper GPU의 메모리 계층과 실행 모델을 알아야 한다.&lt;/p&gt; &lt;h3 id=&quot;메모리-계층&quot;&gt;메모리 계층&lt;/h3&gt; &lt;p&gt;H100 SXM5 GPU의 메모리 계층은 다음과 같다.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;하드웨어 레벨&lt;/th&gt; &lt;th&gt;병렬 단위&lt;/th&gt; &lt;th&gt;메모리&lt;/th&gt; &lt;th&gt;용량 &amp;amp; 대역폭&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Chip&lt;/td&gt; &lt;td&gt;Grid&lt;/td&gt; &lt;td&gt;GMEM (HBM)&lt;/td&gt; &lt;td&gt;80 GiB @ 3.35 TB/s&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;GPC&lt;/td&gt; &lt;td&gt;Threadblock Cluster&lt;/td&gt; &lt;td&gt;L2&lt;/td&gt; &lt;td&gt;50 MiB @ 12 TB/s&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;SM&lt;/td&gt; &lt;td&gt;Threadblock (CTA)&lt;/td&gt; &lt;td&gt;SMEM&lt;/td&gt; &lt;td&gt;228 KiB per SM, 31TB/s per GPU&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Thread&lt;/td&gt; &lt;td&gt;Thread&lt;/td&gt; &lt;td&gt;RMEM (Register)&lt;/td&gt; &lt;td&gt;256 KiB per SM&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;아래로 갈수록 빠르고 작다. FlashAttention의 핵심 아이디어는 GMEM(HBM) 접근을 최소화하고 SMEM과 RMEM에서 최대한 연산을 수행하는 것이었다.&lt;/p&gt; &lt;h3 id=&quot;쓰레드-계층&quot;&gt;쓰레드 계층&lt;/h3&gt; &lt;p&gt;GPU의 실행 단위도 계층적이다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Thread&lt;/strong&gt;: 가장 작은 실행 단위&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Warp&lt;/strong&gt;: 32개 thread 묶음, SIMT(Single Instruction Multiple Thread) 방식으로 동시 실행&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Warpgroup&lt;/strong&gt;: 4개 warp (128 threads), Hopper에서 새로 도입된 단위. WGMMA의 실행 단위이다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Threadblock (CTA)&lt;/strong&gt;: 같은 SM에서 실행되는 warpgroup들의 묶음. SMEM을 공유한다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Threadblock Cluster&lt;/strong&gt;: 같은 GPC에서 실행되는 threadblock 묶음. L2를 공유한다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Grid&lt;/strong&gt;: 전체 커널의 모든 threadblock&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;비동기-실행과-warp-specialization&quot;&gt;비동기 실행과 Warp Specialization&lt;/h3&gt; &lt;p&gt;Hopper에는 두 가지 핵심 하드웨어 기능이 있다.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;TMA (Tensor Memory Accelerator)&lt;/strong&gt;: HBM과 SMEM 사이의 데이터 전송을 전담하는 하드웨어 유닛이다. 기존에는 warp 내의 모든 thread가 인덱스 계산, 범위 검사, 메모리 복사를 직접 수행해야 했다. TMA는 이 모든 것을 &lt;strong&gt;하드웨어가 자동으로&lt;/strong&gt; 처리한다. 덕분에 warp은 데이터 전송에서 완전히 해방되어 연산에만 집중할 수 있다.&lt;/p&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/h100_tma.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;WGMMA (Warpgroup Matrix Multiply-Accumulate)&lt;/strong&gt;: 기존 Ampere의 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mma.sync&lt;/code&gt;를 대체하는 명령어다. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mma.sync&lt;/code&gt;는 이름 그대로 &lt;strong&gt;동기적&lt;/strong&gt;이어서, 명령을 발행하면 결과가 나올 때까지 해당 warp이 대기해야 했다. 반면 WGMMA는 &lt;strong&gt;비동기적&lt;/strong&gt;이다. WGMMA 명령을 발행한 후 결과를 기다리지 않고 다른 연산(softmax의 exp, max 등)을 수행할 수 있다. 또한 WGMMA는 SMEM에서 직접 operand를 읽을 수 있어서, 레지스터로 복사하는 단계를 생략할 수 있다.&lt;/p&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/h100_wgmma.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;이 두 기능 덕분에 Hopper에서는 warp을 역할별로 나눌 수 있다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Producer warp&lt;/strong&gt;: TMA로 HBM → SMEM 데이터 로드만 담당. 레지스터를 거의 사용하지 않으므로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setmaxnreg&lt;/code&gt;로 레지스터를 반환한다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Consumer warp&lt;/strong&gt;: WGMMA로 행렬 연산만 담당. Producer가 반환한 레지스터를 가져와 더 큰 타일로 연산할 수 있다.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;이 구조를 &lt;strong&gt;warp specialization&lt;/strong&gt;이라고 하며, 데이터 전송과 연산이 &lt;strong&gt;동시에&lt;/strong&gt; 일어나게 된다. Producer가 다음 블록의 \(K, V\)를 로드하는 동안 consumer는 현재 블록으로 행렬곱을 수행한다.&lt;/p&gt; &lt;h3 id=&quot;저정밀도-연산&quot;&gt;저정밀도 연산&lt;/h3&gt; &lt;p&gt;Hopper의 WGMMA는 FP8(e4m3)을 지원한다. FP8 Tensor Core의 처리량은 FP16 대비 &lt;strong&gt;2배&lt;/strong&gt;이다 (989 TFLOPS → 약 1978 TFLOPS). 하지만 FP8은 mantissa 3bit, exponent 4bit으로 정밀도가 낮아서 단순히 적용하면 정확도가 크게 떨어진다.&lt;/p&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/h100_wgmma_fp8.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;또한 FP8 WGMMA에는 레이아웃 제약이 있다. FP16 WGMMA는 mn-major와 k-major 입력 모두 지원하지만, FP8 WGMMA는 &lt;strong&gt;k-major만&lt;/strong&gt; 지원한다. 이 제약은 attention처럼 연속된 두 GEMM(\(S = QK^\top\), \(O = PV\))을 수행할 때 FP32 accumulator와 FP8 operand의 레이아웃이 충돌하는 문제를 야기한다.&lt;/p&gt; &lt;h2 id=&quot;standard-attention과-flashattention-복습&quot;&gt;Standard Attention과 FlashAttention 복습&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/flash_recap_diagram.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Standard attention&lt;/strong&gt;은 중간 행렬 \(S\)와 \(P\)를 HBM에 저장한다. 이로 인해 \(O(N^2)\)의 메모리가 필요하고, HBM 읽기/쓰기가 병목이 된다.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;FlashAttention&lt;/strong&gt;의 핵심은 \(S\)와 \(P\)를 HBM에 쓰지 않는 것이다. Tiling을 통해 \(Q, K, V\)를 블록 단위로 SMEM에 올리고, on-chip에서 local softmax를 계산한 뒤 결과를 점진적으로 누적한다. 이때 online softmax 알고리즘을 사용하여 전체 row를 보지 않고도 softmax를 정확히 계산한다. Backward에서는 \((m, l)\) 통계량만 저장해두고 \(S, P\)를 recomputation한다.&lt;/p&gt; &lt;p&gt;FlashAttention-3도 이 기본 구조는 동일하다. 차이점은 &lt;strong&gt;어떻게 실행하느냐&lt;/strong&gt;에 있다.&lt;/p&gt; &lt;h1 id=&quot;flashattention-3-algorithm&quot;&gt;FlashAttention-3: Algorithm&lt;/h1&gt; &lt;h2 id=&quot;기본-구조-warp-specialization-적용&quot;&gt;기본 구조: Warp Specialization 적용&lt;/h2&gt; &lt;p&gt;FlashAttention-3의 forward pass는 batch, head, query sequence length에 대해 embarrassingly parallel하다. 각 CTA는 query의 한 타일 \(Q_i \in \mathbb{R}^{B_r \times d}\)를 담당하여 output 타일 \(O_i\)를 계산한다. \(Q\)를 \(T_r = \lceil N/B_r \rceil\)개의 블록으로, \(K, V\)를 \(T_c = \lceil N/B_c \rceil\)개의 블록으로 나눈다.&lt;/p&gt; &lt;p&gt;Producer warp과 consumer warp이 &lt;strong&gt;\(s\)-stage circular SMEM buffer&lt;/strong&gt;를 통해 협력한다. 이 버퍼는 \(K, V\) 블록을 \(s\)개까지 미리 로드해둘 수 있는 원형 큐이다.&lt;/p&gt; &lt;h3 id=&quot;algorithm-1-forward-pass-warp-specialization-without-intra-consumer-overlapping&quot;&gt;Algorithm 1: Forward Pass (Warp Specialization, without intra-consumer overlapping)&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;Producer warpgroup:&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setmaxnreg&lt;/code&gt;로 레지스터를 반환한다 (producer는 레지스터가 거의 필요 없다).&lt;/li&gt; &lt;li&gt;\(Q_i\)를 HBM에서 SMEM으로 로드하고, consumer에게 알린다.&lt;/li&gt; &lt;li&gt;\(j = 0, \ldots, T_c - 1\)에 대해: &lt;ul&gt; &lt;li&gt;버퍼의 \((j \bmod s)\)번째 스테이지가 소비될 때까지 대기한다.&lt;/li&gt; &lt;li&gt;\(K_j, V_j\)를 HBM에서 SMEM의 해당 스테이지에 로드한다.&lt;/li&gt; &lt;li&gt;로드 완료를 consumer에게 알린다.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ol&gt; &lt;p&gt;TMA 덕분에 로드 명령은 비동기적이며, 처음 \(s\)번의 이터레이션까지는 대기 없이 연속으로 발행할 수 있다.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Consumer warpgroup:&lt;/strong&gt;&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setmaxnreg&lt;/code&gt;로 추가 레지스터를 확보한다.&lt;/li&gt; &lt;li&gt;On-chip에서 초기화: \(O_i = 0 \in \mathbb{R}^{B_r \times d}\), \(l_i = 0 \in \mathbb{R}^{B_r}\), \(m_i = -\infty \in \mathbb{R}^{B_r}\)&lt;/li&gt; &lt;li&gt;\(Q_i\)가 SMEM에 로드되기를 기다린다.&lt;/li&gt; &lt;li&gt;\(j = 0, \ldots, T_c - 1\)에 대해: &lt;ul&gt; &lt;li&gt;\(K_j\)가 SMEM에 로드되기를 기다린다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;SS-GEMM&lt;/strong&gt;: \(S_i^{(j)} = Q_i K_j^\top \in \mathbb{R}^{B_r \times B_c}\). Commit and wait.&lt;/li&gt; &lt;li&gt;\(m_i^{\text{old}} = m_i\)를 저장한다.&lt;/li&gt; &lt;li&gt; \[m_i = \max(m_i^{\text{old}}, \text{rowmax}(S_i^{(j)}))\] &lt;/li&gt; &lt;li&gt; \[\tilde{P}_i^{(j)} = \exp(S_i^{(j)} - m_i) \in \mathbb{R}^{B_r \times B_c}\] &lt;/li&gt; &lt;li&gt; \[l_i = \exp(m_i^{\text{old}} - m_i) \cdot l_i + \text{rowsum}(\tilde{P}_i^{(j)})\] &lt;/li&gt; &lt;li&gt;\(V_j\)가 SMEM에 로드되기를 기다린다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;이전 결과 보정&lt;/strong&gt;: \(O_i = \text{diag}(\exp(m_i^{\text{old}} - m_i))^{-1} \cdot O_i\)&lt;/li&gt; &lt;li&gt;&lt;strong&gt;RS-GEMM&lt;/strong&gt;: \(O_i = O_i + \tilde{P}_i^{(j)} V_j\). Commit and wait.&lt;/li&gt; &lt;li&gt;버퍼의 \((j \bmod s)\)번째 스테이지를 producer에게 반환한다.&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt;최종 정규화: \(O_i = \text{diag}(l_i)^{-1} O_i\)&lt;/li&gt; &lt;li&gt;\(L_i = m_i + \log(l_i)\)를 계산한다 (backward용 logsumexp 저장).&lt;/li&gt; &lt;li&gt;\(O_i\)와 \(L_i\)를 HBM에 기록한다.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;여기서 &lt;strong&gt;SS-GEMM&lt;/strong&gt;은 두 operand 모두 SMEM에서 오는 GEMM이고, &lt;strong&gt;RS-GEMM&lt;/strong&gt;은 한쪽이 Register(accumulator)에서 오는 GEMM이다.&lt;/p&gt; &lt;p&gt;이 구조만으로도 FlashAttention-2의 350 TFLOPS에서 &lt;strong&gt;540~570 TFLOPS&lt;/strong&gt;로 성능이 향상된다. Producer가 다음 \(K_{j+1}, V_{j+1}\)을 로드하는 동안 consumer가 현재 \(K_j, V_j\)로 연산하기 때문이다.&lt;/p&gt; &lt;h2 id=&quot;ping-pong-스케줄링-gemm과-softmax-겹치기&quot;&gt;Ping-Pong 스케줄링: GEMM과 Softmax 겹치기&lt;/h2&gt; &lt;p&gt;여기까지만 해도 빨라졌지만, 아직 해결되지 않은 문제가 있다. Consumer warpgroup 안에서 GEMM과 softmax가 &lt;strong&gt;순차적으로&lt;/strong&gt; 실행된다는 점이다.&lt;/p&gt; &lt;p&gt;왜 이게 문제일까? H100의 처리량을 보자.&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;연산&lt;/th&gt; &lt;th&gt;처리량 (FP16)&lt;/th&gt; &lt;th&gt;비고&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;행렬곱 (WGMMA)&lt;/td&gt; &lt;td&gt;989 TFLOPS&lt;/td&gt; &lt;td&gt;Tensor Core&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;지수함수 (exp 등)&lt;/td&gt; &lt;td&gt;3.9 TFLOPS&lt;/td&gt; &lt;td&gt;Multi-function Unit&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;행렬곱이 지수함수보다 &lt;strong&gt;약 256배&lt;/strong&gt; 빠르다. 그런데 attention forward pass에서 matmul FLOPs와 exponential FLOPs의 비율을 계산해보면, head dimension \(d = 128\) 기준:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;matmul FLOPs: \(4 \times N^2 \times d\) (\(QK^\top\)와 \(PV\), 각각 \(2N^2d\))&lt;/li&gt; &lt;li&gt;exponential FLOPs: \(\sim N^2\) (softmax의 exp 연산)&lt;/li&gt; &lt;li&gt;비율: \(4d = 512\)&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;matmul이 512배 더 많지만, exponential이 256배 느리니까, softmax가 전체 사이클에서 차지하는 비중은:&lt;/p&gt; \[\frac{1}{1 + 512/256} = \frac{1}{3} \approx 33\%\] &lt;p&gt;즉, 아무리 GEMM을 빠르게 해도 softmax 때문에 전체 시간의 &lt;strong&gt;약 1/3은 Tensor Core가 놀게 된다&lt;/strong&gt;. FP8에서는 더 심각하다. GEMM 처리량이 2배로 늘어나지만 exp 속도는 그대로이므로, softmax 비중이 더 커진다.&lt;/p&gt; &lt;p&gt;해결책은 softmax를 GEMM 뒤에 &lt;strong&gt;숨기는 것&lt;/strong&gt;이다. WGMMA가 Tensor Core에서 실행되고, exp는 Multi-function Unit(SFU)에서 실행되므로, 둘은 서로 다른 하드웨어 유닛이다. 동시에 실행할 수 있다면 softmax의 비용이 사라진다.&lt;/p&gt; &lt;p&gt;저자는 &lt;strong&gt;2개의 consumer warpgroup&lt;/strong&gt;을 사용하는 ping-pong 스케줄링을 제안한다. 동기화 배리어(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bar.sync&lt;/code&gt;)를 사용하여 다음과 같이 강제한다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Warpgroup 1의 GEMM1(\(PV\))과 다음 iteration의 GEMM0(\(QK^\top\))이 먼저 스케줄링된다.&lt;/li&gt; &lt;li&gt;이 GEMM들이 Warpgroup 2의 GEMM들 &lt;strong&gt;이전에&lt;/strong&gt; 스케줄링되도록 배리어로 순서를 강제한다.&lt;/li&gt; &lt;li&gt;결과적으로 Warpgroup 2가 GEMM을 하는 동안, Warpgroup 1은 softmax를 실행한다.&lt;/li&gt; &lt;li&gt;그 다음에는 역할이 뒤바뀐다.&lt;/li&gt; &lt;/ul&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/pingpong_pipelining.png&quot; width=&quot;100%&quot; /&gt; &lt;/p&gt; &lt;p&gt;그림에서 같은 색은 같은 iteration을 의미한다. 핵심은 한 warpgroup이 softmax를 하는 시간이 다른 warpgroup의 GEMM 시간에 완전히 &lt;strong&gt;가려진다&lt;/strong&gt;는 것이다.&lt;/p&gt; &lt;p&gt;이 기법으로 570 TFLOPS → &lt;strong&gt;620~640 TFLOPS&lt;/strong&gt;로 성능이 향상된다 (FP16, hdim=128, seqlen=8192 기준).&lt;/p&gt; &lt;h2 id=&quot;intra-warpgroup-2-stage-파이프라이닝&quot;&gt;Intra-Warpgroup 2-Stage 파이프라이닝&lt;/h2&gt; &lt;p&gt;Ping-pong이 warpgroup &lt;strong&gt;사이의&lt;/strong&gt; 겹침이었다면, 2-stage 파이프라이닝은 하나의 warpgroup &lt;strong&gt;안에서의&lt;/strong&gt; 겹침이다.&lt;/p&gt; &lt;p&gt;Algorithm 1의 consumer 내부 루프를 보면, 한 iteration 안에서 다음과 같은 의존성이 있다.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;GEMM0&lt;/strong&gt; (\(S = QK^\top\)) → 결과 \(S\)가 나와야&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Softmax&lt;/strong&gt; (\(\tilde{P} = \exp(S - m)\) 등) → 결과 \(\tilde{P}\)가 나와야&lt;/li&gt; &lt;li&gt;&lt;strong&gt;GEMM1&lt;/strong&gt; (\(O += \tilde{P}V\)) → 다음 iteration 시작&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;이 세 단계가 순차적으로 실행되기 때문에, WGMMA가 비동기적이더라도 wait가 필요하다.&lt;/p&gt; &lt;p&gt;하지만 iteration을 넘나들면 겹칠 수 있다. 핵심 관찰은 이것이다.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Iteration \(j\)의 GEMM0 결과(\(S_{\text{next}}\))는 iteration \(j\)의 softmax에만 필요하다. 한편 iteration \(j-1\)의 GEMM1(\(\tilde{P}_{\text{cur}} V_{j-1}\))은 iteration \(j-1\)의 softmax 결과에만 의존한다.&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;따라서 다음과 같이 겹칠 수 있다.&lt;/p&gt; &lt;h3 id=&quot;algorithm-2-consumer-warpgroup-forward-pass-2-stage&quot;&gt;Algorithm 2: Consumer Warpgroup Forward Pass (2-Stage)&lt;/h3&gt; &lt;ol&gt; &lt;li&gt;\(O_i = 0, l_i = 0, m_i = -\infty\) 초기화&lt;/li&gt; &lt;li&gt;\(Q_i\)와 \(K_0\)가 로드되기를 기다린다.&lt;/li&gt; &lt;li&gt;\(S_{\text{cur}} = Q_i K_0^\top\) (WGMMA). Commit and wait.&lt;/li&gt; &lt;li&gt;\(S_{\text{cur}}\)에서 \(m_i, \tilde{P}_{\text{cur}}, l_i\) 계산하고 \(O_i\) rescale.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;\(j = 1, \ldots, T_c - 1\)에 대해:&lt;/strong&gt; &lt;ul&gt; &lt;li&gt;\(K_j\)가 로드되기를 기다린다.&lt;/li&gt; &lt;li&gt;\(S_{\text{next}} = Q_i K_j^\top\) (WGMMA). &lt;strong&gt;Commit but do not wait.&lt;/strong&gt; ← 핵심!&lt;/li&gt; &lt;li&gt;\(V_{j-1}\)이 로드되기를 기다린다.&lt;/li&gt; &lt;li&gt;\(O_i = O_i + \tilde{P}_{\text{cur}} V_{j-1}\) (WGMMA). &lt;strong&gt;Commit but do not wait.&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;\(S_{\text{next}}\)의 WGMMA를 기다린다.&lt;/li&gt; &lt;li&gt;\(S_{\text{next}}\)에서 \(m_i, \tilde{P}_{\text{next}}, l_i\) 계산하고 \(O_i\) rescale.&lt;/li&gt; &lt;li&gt;\(\tilde{P}_{\text{cur}} V_{j-1}\)의 WGMMA를 기다린다.&lt;/li&gt; &lt;li&gt;\(O_i\) rescale 반영.&lt;/li&gt; &lt;li&gt; \[S_{\text{next}} \to S_{\text{cur}}, \tilde{P}_{\text{next}} \to \tilde{P}_{\text{cur}}\] &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt;마지막 \(V_{T_c-1}\) 처리&lt;/li&gt; &lt;li&gt;최종 정규화: \(O_i = \text{diag}(l_i)^{-1} O_i\)&lt;/li&gt; &lt;/ol&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/2_stage_pipelining.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;그림에서 같은 색은 같은 iteration을 나타낸다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;WGMMA0&lt;/strong&gt; (\(QK^\top\))이 iteration 1을 계산하는 동안&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Softmax&lt;/strong&gt;는 iteration 0의 \(S\)를 처리하고&lt;/li&gt; &lt;li&gt;&lt;strong&gt;WGMMA1&lt;/strong&gt; (\(\tilde{P}V\))은 iteration 0의 softmax 결과를 사용한다.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;5번 단계에서 \(S_{\text{next}}\)와 \(\tilde{P}_{\text{cur}} V_{j-1}\)을 &lt;strong&gt;동시에&lt;/strong&gt; 발행하고 나중에 각각 기다리는 것이 핵심이다. 두 WGMMA가 동시에 실행되는 동안 softmax 연산이 끼어든다.&lt;/p&gt; &lt;h3 id=&quot;트레이드오프-레지스터-압력&quot;&gt;트레이드오프: 레지스터 압력&lt;/h3&gt; &lt;p&gt;이 기법의 대가는 \(S\)를 두 개 동시에 보관해야 한다는 것이다 (\(S_{\text{cur}}\)와 \(S_{\text{next}}\)). 추가 레지스터 사용량은 \(B_r \times B_c \times \text{sizeof(float)}\)이다. 블록 크기를 키우면 memory IO는 줄지만 레지스터 압력이 커져서, GPU마다 최적 블록 크기를 조정해야 한다.&lt;/p&gt; &lt;h3 id=&quot;컴파일러-주의사항&quot;&gt;컴파일러 주의사항&lt;/h3&gt; &lt;p&gt;이 의사코드는 이상적인 실행 순서를 나타내지만, NVCC 컴파일러가 최적화를 위해 WGMMA 명령의 순서를 재배치할 수 있다. 이 경우 의도한 파이프라이닝이 깨질 수 있으므로, SASS 코드를 확인하여 컴파일러가 올바른 순서를 생성하는지 검증해야 한다.&lt;/p&gt; &lt;p&gt;이 기법으로 620 TFLOPS → &lt;strong&gt;640~661 TFLOPS&lt;/strong&gt;까지 향상된다.&lt;/p&gt; &lt;h2 id=&quot;backward-pass-1&quot;&gt;Backward Pass&lt;/h2&gt; &lt;p&gt;Backward pass도 forward와 유사한 warp specialization 구조를 사용한다. 다만 한 가지 추가 역할이 필요하다. Forward에서는 각 CTA가 \(Q_i\)를 담당하여 \(O_i\)를 독립적으로 계산했지만, backward에서는 \(dQ\)의 누적이 필요하다. 여러 CTA가 같은 \(dQ_i\)에 값을 더해야 하므로, 메모리 경합(contention)이 발생한다.&lt;/p&gt; &lt;p&gt;이를 해결하기 위해 &lt;strong&gt;dQ-writer warp&lt;/strong&gt;이라는 세 번째 역할을 추가한다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Producer warp&lt;/strong&gt;: \(K_j, V_j, Q_i, dO_i\) 등을 HBM에서 SMEM으로 로드&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Consumer warp&lt;/strong&gt;: WGMMA로 \(dV_j, dK_j, dQ_i^{(\text{local})}\) 계산 후 SMEM에 기록&lt;/li&gt; &lt;li&gt;&lt;strong&gt;dQ-writer warp&lt;/strong&gt;: \(dQ_i^{(\text{local})}\)를 SMEM에서 읽어 HBM의 \(dQ_i\)에 semaphore를 이용하여 원자적으로 누적&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;이 구조 덕분에 dQ 누적의 메모리 경합이 consumer의 연산을 블로킹하지 않는다.&lt;/p&gt; &lt;h1 id=&quot;low-precision-flashattention-fp8&quot;&gt;Low-precision FlashAttention: FP8&lt;/h1&gt; &lt;h2 id=&quot;fp8-wgmma의-레이아웃-문제&quot;&gt;FP8 WGMMA의 레이아웃 문제&lt;/h2&gt; &lt;p&gt;FP8로 FlashAttention-3를 구현할 때 가장 큰 기술적 난관은 &lt;strong&gt;레이아웃 충돌&lt;/strong&gt;이다.&lt;/p&gt; &lt;p&gt;GEMM에서 행렬 \(A \times B\)를 계산할 때, \(A\)나 \(B\)가 &lt;strong&gt;mn-major&lt;/strong&gt; (outer dimension이 연속)인지 &lt;strong&gt;k-major&lt;/strong&gt; (inner dimension이 연속)인지에 따라 WGMMA 명령이 달라진다. FP16에서는 둘 다 지원하지만, FP8에서는 &lt;strong&gt;k-major만&lt;/strong&gt; 지원한다.&lt;/p&gt; &lt;p&gt;Attention에서는 두 개의 연속된 GEMM이 있다.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;GEMM0&lt;/strong&gt;: \(S = QK^\top\) → \(S\)는 FP32 accumulator에 저장됨&lt;/li&gt; &lt;li&gt;&lt;strong&gt;GEMM1&lt;/strong&gt;: \(O = \tilde{P}V\) → \(\tilde{P}\)는 \(S\)에서 softmax를 취한 결과&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;문제는 FP32 accumulator의 레지스터 레이아웃(아래 그림)이 FP8 operand A의 레이아웃(아래 그림)과 &lt;strong&gt;다르다&lt;/strong&gt;는 것이다.&lt;/p&gt; &lt;p&gt;FP16에서는 이 레이아웃 차이를 mn-major 모드로 우회할 수 있었지만, FP8에서는 k-major만 지원하므로 불가능하다.&lt;/p&gt; &lt;p&gt;저자는 두 가지 방법으로 이를 해결한다.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;V의 in-kernel transpose&lt;/strong&gt;: LDSM(Load Shared Memory to Register)과 STSM(Store Register to Shared Memory) 명령어를 이용하여 \(V\) 타일을 SMEM에서 읽어 transpose한 후 다시 SMEM에 쓴다. 이 과정은 producer warp에서 실행되며, 다음 \(V\) 타일을 TMA로 로드하는 시간에 숨길 수 있다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;\(\tilde{P}\)의 byte permute&lt;/strong&gt;: Accumulator의 레이아웃을 FP8 operand A 형식에 맞추기 위해 byte permute 명령어를 사용한다. 구체적으로 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{d0 d1 d4 d5 d2 d3 d6 d7}&lt;/code&gt; 순서로 재배열한다.&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;이 두 변환 모두 다른 연산에 숨길 수 있어서 추가 비용이 거의 없다.&lt;/p&gt; &lt;h2 id=&quot;block-quantization&quot;&gt;Block Quantization&lt;/h2&gt; &lt;p&gt;전체 텐서에 하나의 스케일링 값을 사용하는 &lt;strong&gt;per-tensor scaling&lt;/strong&gt;은 이상치 하나가 전체 텐서의 양자화 범위를 지배할 수 있다. 이를 완화하기 위해 &lt;strong&gt;블록 단위 양자화&lt;/strong&gt;를 사용한다.&lt;/p&gt; &lt;p&gt;\(Q, K, V\) 각각을 \(B_r \times d\) 또는 \(B_c \times d\) 크기의 블록으로 나누고, 블록별로 하나의 스케일링 값을 유지한다.&lt;/p&gt; \[s_Q = \frac{\max(|Q_{\text{block}}|)}{448}, \quad Q_{\text{fp8}} = \text{round}\left(\frac{Q_{\text{block}}}{s_Q}\right)\] &lt;p&gt;여기서 448은 FP8 e4m3의 최대 표현값이다.&lt;/p&gt; &lt;p&gt;FlashAttention-3는 이미 블록 단위로 \(Q, K, V\)를 처리하므로, 각 블록의 \(S\)에 대해 \(s_Q \cdot s_K\)를 곱해주면 된다. 이 양자화는 rotary embedding 같은 memory-bound 연산에 fuse할 수 있어서 추가 slowdown이 없다 (rotary embedding 자체가 memory-bandwidth bounded이므로).&lt;/p&gt; &lt;h2 id=&quot;incoherent-processing&quot;&gt;Incoherent Processing&lt;/h2&gt; &lt;p&gt;블록 양자화만으로는 부족하다. LLM의 활성화값에는 &lt;strong&gt;이상치(outlier)&lt;/strong&gt;가 존재하는데, 전체 원소의 0.1% 정도가 나머지보다 매우 큰 값을 가진다. 이 소수의 큰 값이 블록의 양자화 범위를 지배하면 나머지 99.9%의 값들이 좁은 범위에 몰려서 정밀도가 크게 떨어진다.&lt;/p&gt; &lt;p&gt;저자는 양자화 문헌에서 사용되는 &lt;strong&gt;incoherent processing&lt;/strong&gt; 기법을 도입한다. 아이디어는 양자화 전에 \(Q\)와 \(K\)에 랜덤 직교행렬 \(M\)을 곱해서 이상치를 분산시키는 것이다.&lt;/p&gt; &lt;p&gt;\(M\)이 직교행렬이므로 \(MM^\top = I\)이고:&lt;/p&gt; \[(QM)(KM)^\top = QMM^\top K^\top = QK^\top\] &lt;p&gt;따라서 attention 결과는 &lt;strong&gt;전혀 변하지 않지만&lt;/strong&gt;, \(QM\)의 각 원소는 원래 \(Q\)의 원소들의 &lt;strong&gt;가중합&lt;/strong&gt;이 된다. 이상치의 영향이 여러 원소로 분산되어, 각 원소의 크기가 균등해진다.&lt;/p&gt; &lt;h3 id=&quot;왜-분산될까&quot;&gt;왜 분산될까?&lt;/h3&gt; &lt;p&gt;직관적으로 이해하면, \(Q\)의 한 행 \(q = [1, 1, 1, 100, 1, \ldots]\)처럼 이상치가 있다고 하자. \(M\)을 곱하면 \(qM\)은 \(q\)의 모든 원소를 섞은 값이 된다. 100이라는 큰 값이 다른 원소들과 합쳐져서 각 원소가 대략 비슷한 크기가 된다.&lt;/p&gt; &lt;h3 id=&quot;실제-구현&quot;&gt;실제 구현&lt;/h3&gt; &lt;p&gt;실제로 \(M\)을 \(N \times N\) 임의의 직교행렬로 사용하면 \(O(d^2)\)의 연산이 필요하다. 대신 \(M = HD\)로 구성한다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;\(D\): \(\pm 1\)을 원소로 가지는 랜덤 대각행렬 (\(O(d)\))&lt;/li&gt; &lt;li&gt;\(H\): Hadamard 행렬 (\(O(d \log d)\), Fast Walsh-Hadamard Transform)&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;\(HD\)의 곱은 \(O(d \log d)\)에 계산 가능하다. 또한 이 변환은 rotary embedding과 같은 memory-bound 연산에 &lt;strong&gt;fuse할 수 있어서 사실상 공짜&lt;/strong&gt;다. Rotary embedding은 어차피 HBM에서 \(Q, K\)를 읽고 써야 하므로 memory-bandwidth bounded인데, Hadamard 변환을 추가해도 메모리 대역폭은 변하지 않기 때문이다.&lt;/p&gt; &lt;h3 id=&quot;수치-오차-검증&quot;&gt;수치 오차 검증&lt;/h3&gt; &lt;p&gt;이상치가 있는 분포에서 수치 오차를 비교한다. \(Q, K, V\)의 원소를 다음과 같이 생성한다.&lt;/p&gt; \[\mathcal{N}(0, 1) + \mathcal{N}(0, 100) \cdot \text{Bernoulli}(0.001)\] &lt;p&gt;즉, 대부분은 표준정규분포이지만 0.1%의 원소에 표준편차 10의 이상치가 추가된다. FP64 구현을 기준(ground truth)으로 RMSE를 측정한 결과:&lt;/p&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/flash3_numerical_error.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;방법&lt;/th&gt; &lt;th&gt;RMSE&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Baseline FP16 (standard attention)&lt;/td&gt; &lt;td&gt;3.2e-4&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FlashAttention-2 FP16&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1.9e-4&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FlashAttention-3 FP16&lt;/td&gt; &lt;td&gt;&lt;strong&gt;1.9e-4&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Baseline FP8 (per-tensor scaling)&lt;/td&gt; &lt;td&gt;2.4e-2&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FlashAttention-3 FP8 (block quant + incoherent)&lt;/td&gt; &lt;td&gt;&lt;strong&gt;9.1e-3&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FlashAttention-3 FP8 (block quant만, no incoherent)&lt;/td&gt; &lt;td&gt;9.3e-3&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;FlashAttention-3 FP8 (no block quant)&lt;/td&gt; &lt;td&gt;2.4e-2&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;주목할 점:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;FP16에서 FlashAttention-2/3는 standard attention보다 &lt;strong&gt;1.7배 더 정확&lt;/strong&gt;하다. 중간 결과(softmax)를 FP32로 유지하기 때문이다.&lt;/li&gt; &lt;li&gt;FP8에서 block quantization + incoherent processing을 적용하면 baseline 대비 &lt;strong&gt;2.6배&lt;/strong&gt; 오차가 줄어든다.&lt;/li&gt; &lt;li&gt;Block quantization만으로도 대부분의 개선이 이루어지고, incoherent processing이 추가적인 개선을 제공한다.&lt;/li&gt; &lt;/ul&gt; &lt;h1 id=&quot;empirical-validation&quot;&gt;Empirical Validation&lt;/h1&gt; &lt;p&gt;H100 80GB SXM5 GPU에서 벤치마크를 수행했다. Hidden dimension은 2048, 시퀀스 길이는 512~16K, head dimension은 64, 128, 256으로, total token 수가 16K가 되도록 batch size를 조절했다. Forward FLOPs는 다음과 같이 계산한다.&lt;/p&gt; \[\text{FLOPs} = 4 \times \text{seqlen}^2 \times \text{head\_dim} \times \text{num\_heads}\] &lt;p&gt;Causal masking이 있으면 약 절반만 계산하므로 2로 나눈다. Backward FLOPs는 forward의 2.5배이다 (forward에 matmul 2개, backward에 5개).&lt;/p&gt; &lt;h2 id=&quot;fp16-forward-pass&quot;&gt;FP16 Forward Pass&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/flash3_fp16_fwd.png&quot; width=&quot;100%&quot; /&gt; &lt;/p&gt; &lt;p&gt;FlashAttention-3는 FlashAttention-2 대비 &lt;strong&gt;1.5~2.0배&lt;/strong&gt; 빠르다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Head dim 64&lt;/strong&gt;: Non-causal, seqlen 16K 기준 — Standard 73, FA-2 332, cuDNN 412, &lt;strong&gt;FA-3 497&lt;/strong&gt; TFLOPS&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Head dim 128&lt;/strong&gt;: Non-causal, seqlen 8K 기준 — Standard 133, FA-2 370, cuDNN 610, &lt;strong&gt;FA-3 649&lt;/strong&gt; TFLOPS&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Head dim 256&lt;/strong&gt;: Non-causal, seqlen 8K 기준 — FA-2 581, cuDNN 581, &lt;strong&gt;FA-3 746&lt;/strong&gt; TFLOPS (최대)&lt;/li&gt; &lt;li&gt;Standard attention 대비 &lt;strong&gt;3~16배&lt;/strong&gt; 빠르다&lt;/li&gt; &lt;li&gt;&lt;strong&gt;cuDNN&lt;/strong&gt;(NVIDIA 자체 최적화 라이브러리)보다도 대부분의 설정에서 빠르거나 비슷한 성능을 보인다&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Head dimension이 클수록 matmul 비중이 커져서 GPU 활용률이 높아진다. Head dim 256에서 최대 756 TFLOPS, 이론 최대 989 TFLOPS의 약 &lt;strong&gt;75%&lt;/strong&gt;에 도달한다.&lt;/p&gt; &lt;h2 id=&quot;fp16-backward-pass&quot;&gt;FP16 Backward Pass&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/flash3_fp16_bwd.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;Backward pass에서도 FlashAttention-2 대비 &lt;strong&gt;1.5~1.75배&lt;/strong&gt; 빠르다. Forward보다 speedup이 약간 낮은데, backward에는 5개의 matmul과 더 복잡한 데이터 의존성이 있어서 파이프라이닝 효과가 상대적으로 줄어들기 때문이다.&lt;/p&gt; &lt;h2 id=&quot;fp8-forward-pass&quot;&gt;FP8 Forward Pass&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt; &lt;img src=&quot;/assets/post/image/flashattention3/flash3_fp8_fwd.png&quot; width=&quot;80%&quot; /&gt; &lt;/p&gt; &lt;p&gt;FP8에서는 head dim 256, non-causal, seqlen 16K 기준 최대 &lt;strong&gt;1,171 TFLOPS&lt;/strong&gt;에 달하며, &lt;strong&gt;1.2 PFLOPS/s&lt;/strong&gt;에 근접한다. FP16 대비 약 1.5~2배의 추가 speedup이다.&lt;/p&gt; &lt;p&gt;다만 짧은 시퀀스와 causal masking 조합에서는 FP8 cuDNN이 더 빠른 경우도 있다. 이는 FP16 FlashAttention-3가 persistent kernel과 load balancing 전략을 사용하는 반면 FP8 버전은 아직 이를 적용하지 않았기 때문이다.&lt;/p&gt; &lt;h2 id=&quot;ablation-파이프라이닝-효과&quot;&gt;Ablation: 파이프라이닝 효과&lt;/h2&gt; &lt;p&gt;Non-causal FlashAttention-3 (FP16, batch=4, seqlen=8448, nheads=16, hdim=128) 기준:&lt;/p&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Configuration&lt;/th&gt; &lt;th&gt;Time&lt;/th&gt; &lt;th&gt;TFLOPs/s&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;FlashAttention-3 (full)&lt;/td&gt; &lt;td&gt;3.538 ms&lt;/td&gt; &lt;td&gt;&lt;strong&gt;661&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;No GEMM-Softmax pipelining, with warp specialization&lt;/td&gt; &lt;td&gt;4.021 ms&lt;/td&gt; &lt;td&gt;582&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;GEMM-Softmax pipelining, no warp specialization&lt;/td&gt; &lt;td&gt;4.105 ms&lt;/td&gt; &lt;td&gt;570&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;Warp specialization만으로 570 → 582 TFLOPS, GEMM-softmax pipelining을 추가하면 582 → 661 TFLOPS로, 각 기법이 단계적으로 성능을 끌어올리는 것을 확인할 수 있다.&lt;/p&gt; &lt;h1 id=&quot;flashattention-시리즈-비교&quot;&gt;FlashAttention 시리즈 비교&lt;/h1&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;FlashAttention&lt;/th&gt; &lt;th&gt;FlashAttention-2&lt;/th&gt; &lt;th&gt;FlashAttention-3&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;핵심 아이디어&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;Tiling + Recomputation&lt;/td&gt; &lt;td&gt;non-matmul FLOPs 감소, warp partitioning&lt;/td&gt; &lt;td&gt;비동기 실행, FP8&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;타겟 GPU&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;A100&lt;/td&gt; &lt;td&gt;A100&lt;/td&gt; &lt;td&gt;H100 (Hopper)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;주요 명령어&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;mma.sync&lt;/td&gt; &lt;td&gt;mma.sync&lt;/td&gt; &lt;td&gt;WGMMA + TMA&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;정밀도&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;FP16&lt;/td&gt; &lt;td&gt;FP16&lt;/td&gt; &lt;td&gt;FP16 + FP8&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;GPU 활용률&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;35% (H100 기준)&lt;/td&gt; &lt;td&gt;&lt;strong&gt;75% (H100)&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;FP16 성능&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;~370 TFLOPS&lt;/td&gt; &lt;td&gt;&lt;strong&gt;~740 TFLOPS&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;FP8 성능&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;&lt;strong&gt;~1.2 PFLOPS&lt;/strong&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Softmax 처리&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;순차 실행&lt;/td&gt; &lt;td&gt;순차 실행&lt;/td&gt; &lt;td&gt;GEMM과 겹침&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Backward 특이점&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;—&lt;/td&gt; &lt;td&gt;outer loop을 K, V로 변경&lt;/td&gt; &lt;td&gt;dQ-writer warp 추가&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h1 id=&quot;discussion-limitations-conclusion&quot;&gt;Discussion, Limitations, Conclusion&lt;/h1&gt; &lt;p&gt;FlashAttention-3는 Hopper GPU의 하드웨어 특성을 적극적으로 활용하여 attention 성능을 크게 향상시켰다. 특히 WGMMA의 비동기 특성을 이용한 ping-pong 스케줄링과, FP8의 정확도 문제를 해결하는 incoherent processing이 인상적이다.&lt;/p&gt; &lt;p&gt;FlashAttention-2가 “어떤 GPU에서든 작동하는 범용 최적화”였다면, FlashAttention-3는 “Hopper의 능력을 극한까지 끌어내는 하드웨어 특화 최적화”라고 할 수 있다. GPU 아키텍처가 발전할수록 소프트웨어도 이에 맞춰 진화해야 한다는 것을 보여주는 좋은 사례다.&lt;/p&gt; &lt;h3 id=&quot;limitations&quot;&gt;Limitations&lt;/h3&gt; &lt;p&gt;저자가 언급한 한계는 다음과 같다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Hopper 전용&lt;/strong&gt;: 현재 구현은 H100에 특화되어 있다. 다만 비동기 실행과 warp specialization이라는 개념 자체는 비슷한 하드웨어 특성을 가진 다른 GPU에도 적용 가능하다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;FP8 커널 설계의 복잡성&lt;/strong&gt;: Persistent kernel과 FP8을 통합하는 것이 아직 남은 과제이다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;FP8 학습의 불확실성&lt;/strong&gt;: 추론에서 FP8의 효과는 검증되었지만, 학습에서 저정밀도가 안정적인지는 추가 연구가 필요하다.&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;related-work&quot;&gt;Related Work&lt;/h3&gt; &lt;p&gt;FlashAttention-3와 관련된 연구 방향들도 간략히 정리한다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Distributed attention&lt;/strong&gt;: Ring Attention 등은 FlashAttention을 여러 GPU로 확장하여 최대 100만 토큰까지 처리할 수 있다. FlashAttention-3의 개선은 이런 분산 attention 방법에도 그대로 적용된다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Alternative architectures&lt;/strong&gt;: Mamba, RWKV, RetNet 등 linear attention 계열 모델이 등장하고 있지만, 대형 모델(Jamba, Zamba 등)에서도 여전히 attention layer를 포함하고 있어 FlashAttention의 최적화가 유효하다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;KV cache quantization&lt;/strong&gt;: QuIP, KIVI 등은 KV cache를 4bit, 2bit까지 양자화하여 추론 효율을 높인다. FlashAttention-3의 incoherent processing 기법은 이런 양자화 연구에서 영감을 받았다.&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;blockquote&gt; &lt;p&gt;FlashAttention의 원리가 궁금하다면 &lt;a href=&quot;/blog/2023/fastattention/&quot;&gt;FlashAttention 논문 리뷰&lt;/a&gt;를, 개선점이 궁금하다면 &lt;a href=&quot;/blog/2023/flashattention-2/&quot;&gt;FlashAttention-2 논문 리뷰&lt;/a&gt;를, Blackwell GPU에서의 최적화가 궁금하다면 &lt;a href=&quot;/blog/2026/flashattention-4/&quot;&gt;FlashAttention-4 논문 리뷰&lt;/a&gt;를, Triton으로 직접 구현하고 싶다면 &lt;a href=&quot;/blog/2026/triton-05-flash-attention/&quot;&gt;Triton 05: Flash Attention&lt;/a&gt;을 참고하자.&lt;/p&gt; &lt;/blockquote&gt; &lt;hr /&gt; &lt;h1 id=&quot;참고-문헌&quot;&gt;참고 문헌&lt;/h1&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;https://arxiv.org/abs/2407.08608&quot;&gt;FlashAttention-3: Fast and Accurate Attention with Asynchrony and Low-precision (arXiv)&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://tridao.me/publications/flash3/flash3.pdf&quot;&gt;FlashAttention-3 PDF&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://tridao.me/blog/2024/flash3/&quot;&gt;Tri Dao Blog — FlashAttention-3&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://ai.meta.com/research/publications/flashattention-3-fast-and-accurate-attention-with-asynchrony-and-low-precision/&quot;&gt;AI at Meta — FlashAttention-3&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://resources.nvidia.com/en-us-tensor-core&quot;&gt;NVIDIA H100 Tensor Core GPU Architecture&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html&quot;&gt;NVIDIA CUDA Programming Guide&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;https://github.com/NVIDIA/cutlass&quot;&gt;CUTLASS — NVIDIA&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; </description> <pubDate>Thu, 09 Apr 2026 15:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2026/flashattention-3/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2026/flashattention-3/</guid> <category>attention</category> <category>hardware-optimization</category> <category>paper</category> <category>flash-attention</category> <category>optimization</category> </item> <item> <title>Triton 05: Flash Attention — 종합 프로젝트</title> <description>&lt;h2 id=&quot;개요&quot;&gt;개요&lt;/h2&gt; &lt;p&gt;지금까지 배운 모든 기법을 종합하여 Flash Attention을 구현합니다. LLM 추론/학습에서 가장 중요한 최적화 기법 중 하나입니다.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;Flash Attention의 원리와 논문 내용이 궁금하다면 &lt;a href=&quot;/blog/2023/fastattention/&quot;&gt;FlashAttention 논문 리뷰&lt;/a&gt;를 먼저 읽어보는 것을 추천한다.&lt;/p&gt; &lt;/blockquote&gt; &lt;hr /&gt; &lt;h2 id=&quot;핵심-개념&quot;&gt;핵심 개념&lt;/h2&gt; &lt;h3 id=&quot;attention-수식&quot;&gt;Attention 수식&lt;/h3&gt; \[O = \text{softmax}\!\left(\frac{Q \cdot K^T}{\sqrt{d}}\right) \cdot V\] &lt;ul&gt; &lt;li&gt;\(Q, K, V\): Query, Key, Value 행렬 (각각 \(N \times d\))&lt;/li&gt; &lt;li&gt;\(\sqrt{d}\): head dimension의 제곱근으로 나눠서 스케일링&lt;/li&gt; &lt;li&gt;\(\text{softmax}\): 행(row) 단위로 적용 → 확률 분포로 변환&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;standard-attention의-문제&quot;&gt;Standard Attention의 문제&lt;/h3&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=05_flash_attention_snippet01_Standard_Attention%EC%9D%98_%EB%AC%B8%EC%A0%9C.py&quot;&gt;&lt;/script&gt; &lt;p&gt;시퀀스 길이 N=4096, float16이면:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;S 행렬 크기: 4096 × 4096 × 2 bytes = &lt;strong&gt;32MB&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;N=16384이면: &lt;strong&gt;512MB&lt;/strong&gt; — 시퀀스가 길어질수록 VRAM 폭발&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;flash-attention의-핵심-아이디어&quot;&gt;Flash Attention의 핵심 아이디어&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;S 행렬을 전체 생성하지 않는다!&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;타일 단위로 Q, K, V를 처리하면서 결과를 점진적으로 누적합니다. 이를 위해 &lt;strong&gt;Online Softmax&lt;/strong&gt; 알고리즘이 필요합니다.&lt;/p&gt; &lt;h3 id=&quot;online-softmax&quot;&gt;Online Softmax&lt;/h3&gt; &lt;p&gt;데이터를 청크(블록) 단위로 받으면서 &lt;strong&gt;점진적으로 업데이트&lt;/strong&gt;합니다.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;청크 1 처리 후&lt;/strong&gt; (\(S_1\) = 첫 번째 K 블록과의 attention score):&lt;/p&gt; \[m^{(1)} = \max(S_1)\] \[l^{(1)} = \sum_j e^{S_{1,j} - m^{(1)}}\] \[O^{(1)} = \text{diag}(l^{(1)})^{-1} \cdot e^{S_1 - m^{(1)}} \cdot V_1\] &lt;p&gt;&lt;strong&gt;청크 2 처리 후&lt;/strong&gt; — 보정 계수 (핵심!):&lt;/p&gt; \[\alpha = e^{m^{(1)} - m^{(2)}}\] &lt;p&gt;이전 결과를 새로운 max 기준으로 보정:&lt;/p&gt; \[l^{(2)} = l^{(1)} \cdot \alpha + \sum_j e^{S_{2,j} - m^{(2)}}\] \[O^{(2)} = O^{(1)} \cdot \alpha + e^{S_2 - m^{(2)}} \cdot V_2\] &lt;h4 id=&quot;왜-보정-계수-alpha가-필요한가&quot;&gt;왜 보정 계수 \(\alpha\)가 필요한가?&lt;/h4&gt; &lt;p&gt;max가 바뀌면 이전에 계산한 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp&lt;/code&gt; 값들이 틀어집니다:&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;청크 1: max=5, exp(3-5) = exp(-2) = 0.135 청크 2: max=10, exp(3-5)는 틀림! exp(3-10) = exp(-7) = 0.0009여야 함 보정: 0.135 × exp(5-10) = 0.135 × exp(-5) ≈ 0.0009 ✓ α = exp(m_old - m_new) &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3 id=&quot;메모리-복잡도&quot;&gt;메모리 복잡도&lt;/h3&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;방식&lt;/th&gt; &lt;th&gt;메모리&lt;/th&gt; &lt;th&gt;RTX 4080 (16GB)에서 최대 seq_len&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Standard&lt;/td&gt; &lt;td&gt;O(N²)&lt;/td&gt; &lt;td&gt;~8K (float16)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Flash&lt;/td&gt; &lt;td&gt;O(N)&lt;/td&gt; &lt;td&gt;수십만+&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h2 id=&quot;커널-동작-원리&quot;&gt;커널 동작 원리&lt;/h2&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/05_flash_attention/flash_attention_flow-480.webp 480w,/assets/img/triton/05_flash_attention/flash_attention_flow-800.webp 800w,/assets/img/triton/05_flash_attention/flash_attention_flow-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/05_flash_attention/flash_attention_flow.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;단계별-의사코드&quot;&gt;단계별 의사코드&lt;/h3&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=05_flash_attention_snippet02_%EB%8B%A8%EA%B3%84%EB%B3%84_%EC%9D%98%EC%82%AC%EC%BD%94%EB%93%9C.py&quot;&gt;&lt;/script&gt; &lt;hr /&gt; &lt;h2 id=&quot;causal-masking&quot;&gt;Causal Masking&lt;/h2&gt; &lt;p&gt;Autoregressive 모델(GPT 등)에서는 미래 토큰을 볼 수 없습니다:&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/05_flash_attention/causal_mask-480.webp 480w,/assets/img/triton/05_flash_attention/causal_mask-800.webp 800w,/assets/img/triton/05_flash_attention/causal_mask-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/05_flash_attention/causal_mask.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=05_flash_attention_snippet03_Causal_Masking.py&quot;&gt;&lt;/script&gt; &lt;hr /&gt; &lt;h2 id=&quot;코드-라인별-설명&quot;&gt;코드 라인별 설명&lt;/h2&gt; &lt;h3 id=&quot;online-softmax-변수-초기화&quot;&gt;Online Softmax 변수 초기화&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_i&lt;/code&gt;: 행별 최대값 추적 (처음엔 -inf → 점점 커짐)&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;l_i&lt;/code&gt;: 행별 softmax 분모 추적 (처음엔 0 → 점점 커짐)&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acc&lt;/code&gt;: 최종 출력 누적기 (처음엔 0 → P@V 결과가 점점 누적)&lt;/li&gt; &lt;li&gt;이 세 변수가 &lt;strong&gt;Online Softmax의 핵심&lt;/strong&gt; — 전체 S 행렬 없이 softmax 계산&lt;/li&gt; &lt;/ul&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=05_flash_attention_snippet04_Online_Softmax_%EB%B3%80%EC%88%98_%EC%B4%88%EA%B8%B0%ED%99%94.py&quot;&gt;&lt;/script&gt; &lt;h3 id=&quot;내부-루프--online-softmax-업데이트-핵심&quot;&gt;내부 루프 — Online Softmax 업데이트 (핵심!)&lt;/h3&gt; &lt;p&gt;각 K/V 블록에 대해 다음을 수행합니다:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;K 블록 로드&lt;/strong&gt; → &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;S = Q @ K^T * scale&lt;/code&gt; 계산 (attention score 타일)&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Causal mask 적용&lt;/strong&gt; → 미래 토큰 차단 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-inf&lt;/code&gt;로 마스킹)&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Online Softmax 업데이트&lt;/strong&gt;: &lt;ul&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m_new = max(m_old, max(S))&lt;/code&gt; — 전체 최대값 갱신&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alpha = exp(m_old - m_new)&lt;/code&gt; — &lt;strong&gt;이전 결과 보정 계수&lt;/strong&gt; (max가 바뀌면 이전 exp 값이 틀어지므로)&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;l_i = l_i * alpha + sum(exp(S - m_new))&lt;/code&gt; — 분모 업데이트&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acc = acc * alpha&lt;/code&gt; — 이전 출력 보정&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;li&gt;&lt;strong&gt;V 블록 로드&lt;/strong&gt; → &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acc += P @ V&lt;/code&gt; 누적&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p.to(v.dtype)&lt;/code&gt;: FP32 → FP16 변환 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.dot&lt;/code&gt;은 같은 타입 필요)&lt;/li&gt; &lt;/ol&gt; &lt;p&gt;매 반복마다 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;acc&lt;/code&gt;에 결과가 누적되므로 &lt;strong&gt;S 전체를 저장할 필요가 없습니다.&lt;/strong&gt;&lt;/p&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=05_flash_attention_snippet05_%EB%82%B4%EB%B6%80_%EB%A3%A8%ED%94%84___Online_Softmax_%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8__%ED%95%B5.py&quot;&gt;&lt;/script&gt; &lt;h3 id=&quot;최종-정규화&quot;&gt;최종 정규화&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;l_i&lt;/code&gt;: 각 행의 softmax 분모 (Σ exp) → 마지막에 한 번만 나눔&lt;/li&gt; &lt;li&gt;FP32 → FP16 변환 후 저장&lt;/li&gt; &lt;/ul&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=05_flash_attention_snippet06_%EC%B5%9C%EC%A2%85_%EC%A0%95%EA%B7%9C%ED%99%94.py&quot;&gt;&lt;/script&gt; &lt;hr /&gt; &lt;h2 id=&quot;전체-튜토리얼과의-연결&quot;&gt;전체 튜토리얼과의 연결&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;개념&lt;/th&gt; &lt;th&gt;어디서 배웠나&lt;/th&gt; &lt;th&gt;Flash Attention에서의 역할&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.load&lt;/code&gt;, mask&lt;/td&gt; &lt;td&gt;01 Vector Add&lt;/td&gt; &lt;td&gt;Q, K, V 블록 로드&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;reduction, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.exp&lt;/code&gt;&lt;/td&gt; &lt;td&gt;02 Softmax&lt;/td&gt; &lt;td&gt;Online Softmax의 max, sum, exp&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;stride, 다중 포인터&lt;/td&gt; &lt;td&gt;03 RMSNorm&lt;/td&gt; &lt;td&gt;batch, head, seq, dim 차원 접근&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.dot&lt;/code&gt;, 2D 타일링&lt;/td&gt; &lt;td&gt;04 MatMul&lt;/td&gt; &lt;td&gt;S = Q@K^T, O += P@V&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;K 차원 루프&lt;/td&gt; &lt;td&gt;04 MatMul&lt;/td&gt; &lt;td&gt;K/V 블록 순회 (내부 루프)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Online Softmax&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;&lt;strong&gt;신규&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;SRAM 제한 극복의 핵심&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h2 id=&quot;벤치마크-결과&quot;&gt;벤치마크 결과&lt;/h2&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/05_flash_attention/benchmark-480.webp 480w,/assets/img/triton/05_flash_attention/benchmark-800.webp 800w,/assets/img/triton/05_flash_attention/benchmark-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/05_flash_attention/benchmark.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;정확도&lt;/strong&gt;: PyTorch standard attention과 거의 동일한 결과&lt;/li&gt; &lt;li&gt;&lt;strong&gt;속도&lt;/strong&gt;: 시퀀스 길이가 길수록 (1024+) 큰 속도 향상&lt;/li&gt; &lt;li&gt;&lt;strong&gt;메모리&lt;/strong&gt;: O(N²) → O(N)으로 극적인 메모리 절약&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;전체-코드&quot;&gt;전체 코드&lt;/h2&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/0f4970e5dbed9af5037d796fa395727f.js?file=flash_attention.py&quot;&gt;&lt;/script&gt; </description> <pubDate>Wed, 01 Apr 2026 15:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2026/triton-05-flash-attention/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2026/triton-05-flash-attention/</guid> <category>triton</category> <category>gpu</category> <category>flash-attention</category> <category>llm</category> <category>attention</category> <category>triton</category> </item> <item> <title>Triton 04: Matrix Multiplication — 2D 타일링과 Autotune</title> <description>&lt;h2 id=&quot;개요&quot;&gt;개요&lt;/h2&gt; &lt;p&gt;딥러닝의 핵심 연산인 행렬 곱셈(GEMM)을 Triton으로 구현합니다. 2D 타일링, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.dot&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;triton.autotune&lt;/code&gt; 등 고급 기능을 학습합니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;핵심-개념&quot;&gt;핵심 개념&lt;/h2&gt; &lt;h3 id=&quot;행렬-곱셈이-왜-중요한가&quot;&gt;행렬 곱셈이 왜 중요한가&lt;/h3&gt; &lt;p&gt;딥러닝의 거의 모든 연산이 행렬 곱셈:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Linear layer: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;y = xW + b&lt;/code&gt;&lt;/li&gt; &lt;li&gt;Attention: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QK^T&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PV&lt;/code&gt;&lt;/li&gt; &lt;li&gt;MLP: 모든 Feed-Forward 블록&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;나이브-vs-타일링&quot;&gt;나이브 vs 타일링&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;나이브&lt;/strong&gt;: 출력의 각 원소마다 Global Memory에서 행/열 전체를 읽음 → 같은 데이터를 반복 로드&lt;/p&gt; &lt;p&gt;&lt;strong&gt;타일링&lt;/strong&gt;: 행렬을 작은 블록으로 나누어 SRAM에 올리고, 블록 단위로 계산&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/04_matmul/tiling-480.webp 480w,/assets/img/triton/04_matmul/tiling-800.webp 800w,/assets/img/triton/04_matmul/tiling-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/04_matmul/tiling.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;hr /&gt; &lt;h2 id=&quot;커널-동작-원리&quot;&gt;커널 동작 원리&lt;/h2&gt; &lt;h3 id=&quot;2d-그리드&quot;&gt;2D 그리드&lt;/h3&gt; &lt;p&gt;이전 튜토리얼은 1D 그리드(행 단위)였지만, MatMul은 2D 그리드를 사용합니다:&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/04_matmul/2d_grid-480.webp 480w,/assets/img/triton/04_matmul/2d_grid-800.webp 800w,/assets/img/triton/04_matmul/2d_grid-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/04_matmul/2d_grid.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;k-차원-루프&quot;&gt;K 차원 루프&lt;/h3&gt; &lt;p&gt;행렬 곱셈 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C = A × B&lt;/code&gt;에서 A(M×K), B(K×N)일 때, K가 크면 한 번에 SRAM에 못 올립니다. 그래서 K를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BLOCK_SIZE_K&lt;/code&gt;씩 잘라서 반복하며, 부분 결과를 누적합니다.&lt;/p&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=04_matmul_snippet01_K_%EC%B0%A8%EC%9B%90_%EB%A3%A8%ED%94%84.py&quot;&gt;&lt;/script&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/04_matmul/k_loop_pointer-480.webp 480w,/assets/img/triton/04_matmul/k_loop_pointer-800.webp 800w,/assets/img/triton/04_matmul/k_loop_pointer-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/04_matmul/k_loop_pointer.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;l2-캐시-최적화-swizzling&quot;&gt;L2 캐시 최적화 (Swizzling)&lt;/h3&gt; &lt;p&gt;&lt;strong&gt;Swizzling = “같은 B 블록을 쓰는 프로그램들을 묶어서 실행”&lt;/strong&gt;&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/04_matmul/group_concept-480.webp 480w,/assets/img/triton/04_matmul/group_concept-800.webp 800w,/assets/img/triton/04_matmul/group_concept-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/04_matmul/group_concept.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/04_matmul/swizzling_detail-480.webp 480w,/assets/img/triton/04_matmul/swizzling_detail-800.webp 800w,/assets/img/triton/04_matmul/swizzling_detail-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/04_matmul/swizzling_detail.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/04_matmul/swizzling-480.webp 480w,/assets/img/triton/04_matmul/swizzling-800.webp 800w,/assets/img/triton/04_matmul/swizzling-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/04_matmul/swizzling.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;tritonautotune-이란&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;triton.autotune&lt;/code&gt; 이란?&lt;/h3&gt; &lt;p&gt;블록 크기에 따라 성능이 크게 달라집니다. Autotune은 여러 설정을 실행해보고 가장 빠른 것을 선택합니다:&lt;/p&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=04_matmul_snippet02_triton_autotune__%EC%9D%B4%EB%9E%80.py&quot;&gt;&lt;/script&gt; &lt;hr /&gt; &lt;h2 id=&quot;코드-라인별-설명&quot;&gt;코드 라인별 설명&lt;/h2&gt; &lt;h3 id=&quot;k-차원-루프-핵심&quot;&gt;K 차원 루프 (핵심)&lt;/h3&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=04_matmul_snippet03_K_%EC%B0%A8%EC%9B%90_%EB%A3%A8%ED%94%84__%ED%95%B5%EC%8B%AC.py&quot;&gt;&lt;/script&gt; &lt;h3 id=&quot;이전-튜토리얼과의-차이점&quot;&gt;이전 튜토리얼과의 차이점&lt;/h3&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;01~03&lt;/th&gt; &lt;th&gt;04 MatMul&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;그리드&lt;/td&gt; &lt;td&gt;1D (행 수)&lt;/td&gt; &lt;td&gt;1D (M타일 × N타일)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;데이터&lt;/td&gt; &lt;td&gt;1D 벡터/행&lt;/td&gt; &lt;td&gt;2D 블록 (타일)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;루프&lt;/td&gt; &lt;td&gt;없음&lt;/td&gt; &lt;td&gt;K 차원 루프&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;핵심 연산&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum&lt;/code&gt;&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.dot&lt;/code&gt; (텐서 코어)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;파라미터 튜닝&lt;/td&gt; &lt;td&gt;수동 BLOCK_SIZE&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;triton.autotune&lt;/code&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h2 id=&quot;벤치마크-결과&quot;&gt;벤치마크 결과&lt;/h2&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/04_matmul/benchmark-480.webp 480w,/assets/img/triton/04_matmul/benchmark-800.webp 800w,/assets/img/triton/04_matmul/benchmark-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/04_matmul/benchmark.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;p&gt;cuBLAS(&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;torch.matmul&lt;/code&gt;)는 수십 년간 최적화된 라이브러리입니다. Triton으로 cuBLAS의 &lt;strong&gt;80~90%&lt;/strong&gt; 성능에 도달하는 것이 목표입니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;전체-코드&quot;&gt;전체 코드&lt;/h2&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/0f4970e5dbed9af5037d796fa395727f.js?file=matmul.py&quot;&gt;&lt;/script&gt; </description> <pubDate>Wed, 01 Apr 2026 15:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2026/triton-04-matmul/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2026/triton-04-matmul/</guid> <category>triton</category> <category>gpu</category> <category>matmul</category> <category>tensor-core</category> <category>triton</category> </item> <item> <title>Triton 03: RMSNorm — LLM에서 쓰이는 실전 커널</title> <description>&lt;h2 id=&quot;개요&quot;&gt;개요&lt;/h2&gt; &lt;p&gt;LLaMA, Mistral, Gemma 등 최신 LLM에서 사용하는 RMSNorm을 Triton으로 구현합니다. Softmax와 유사한 패턴이지만, 학습 가능한 가중치(gamma)가 추가됩니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;핵심-개념&quot;&gt;핵심 개념&lt;/h2&gt; &lt;h3 id=&quot;layernorm-vs-rmsnorm&quot;&gt;LayerNorm vs RMSNorm&lt;/h3&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;LayerNorm: y = (x - mean(x)) / sqrt(var(x) + ε) * γ + β RMSNorm: y = x / sqrt(mean(x²) + ε) * γ &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;RMSNorm이 LLM에서 선호되는 이유:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;mean 계산이 필요 없음 → 연산량 감소&lt;/li&gt; &lt;li&gt;bias(β) 없음 → 파라미터 수 감소&lt;/li&gt; &lt;li&gt;실험적으로 LayerNorm과 성능이 비슷&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;수식-분해&quot;&gt;수식 분해&lt;/h3&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1. 제곱합: sum_sq = Σ(x_i²) 2. RMS: rms = sqrt(sum_sq / n + ε) 3. 정규화: x_norm = x / rms 4. 스케일링: y = x_norm * γ &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;hr /&gt; &lt;h2 id=&quot;커널-동작-원리&quot;&gt;커널 동작 원리&lt;/h2&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/03_rmsnorm/rmsnorm_flow-480.webp 480w,/assets/img/triton/03_rmsnorm/rmsnorm_flow-800.webp 800w,/assets/img/triton/03_rmsnorm/rmsnorm_flow-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/03_rmsnorm/rmsnorm_flow.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;hr /&gt; &lt;h2 id=&quot;코드-라인별-설명&quot;&gt;코드 라인별 설명&lt;/h2&gt; &lt;h3 id=&quot;pytorch-참조-구현&quot;&gt;PyTorch 참조 구현&lt;/h3&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=03_rmsnorm_snippet01_PyTorch_%EC%B0%B8%EC%A1%B0_%EA%B5%AC%ED%98%84.py&quot;&gt;&lt;/script&gt; &lt;h3 id=&quot;커널-함수&quot;&gt;커널 함수&lt;/h3&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=03_rmsnorm_snippet02_%EC%BB%A4%EB%84%90_%ED%95%A8%EC%88%98.py&quot;&gt;&lt;/script&gt; &lt;h3 id=&quot;래퍼-함수&quot;&gt;래퍼 함수&lt;/h3&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=03_rmsnorm_snippet03_%EB%9E%98%ED%8D%BC_%ED%95%A8%EC%88%98.py&quot;&gt;&lt;/script&gt; &lt;hr /&gt; &lt;h2 id=&quot;02-fused-softmax와의-차이점&quot;&gt;02 Fused Softmax와의 차이점&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;02 Softmax&lt;/th&gt; &lt;th&gt;03 RMSNorm&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;reduction&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum&lt;/code&gt; (2번)&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum&lt;/code&gt; (1번)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;수치 안정성&lt;/td&gt; &lt;td&gt;max 빼기&lt;/td&gt; &lt;td&gt;eps 더하기&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;범위 밖 채움&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-inf&lt;/code&gt;&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0&lt;/code&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;추가 입력&lt;/td&gt; &lt;td&gt;없음&lt;/td&gt; &lt;td&gt;가중치 γ&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;입력 shape&lt;/td&gt; &lt;td&gt;2D만&lt;/td&gt; &lt;td&gt;3D/4D → 2D 변환&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h2 id=&quot;벤치마크-결과&quot;&gt;벤치마크 결과&lt;/h2&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/03_rmsnorm/benchmark-480.webp 480w,/assets/img/triton/03_rmsnorm/benchmark-800.webp 800w,/assets/img/triton/03_rmsnorm/benchmark-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/03_rmsnorm/benchmark.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;p&gt;PyTorch의 수동 RMSNorm 구현 대비 커널 퓨전으로 인한 성능 향상이 나타납니다. hidden_size가 클수록(2048, 4096 등) 차이가 명확합니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;전체-코드&quot;&gt;전체 코드&lt;/h2&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/0f4970e5dbed9af5037d796fa395727f.js?file=rmsnorm.py&quot;&gt;&lt;/script&gt; </description> <pubDate>Wed, 01 Apr 2026 15:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2026/triton-03-rmsnorm/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2026/triton-03-rmsnorm/</guid> <category>triton</category> <category>gpu</category> <category>rmsnorm</category> <category>llm</category> <category>triton</category> </item> <item> <title>Triton 02: Fused Softmax — 커널 퓨전과 Reduction</title> <description>&lt;h2 id=&quot;개요&quot;&gt;개요&lt;/h2&gt; &lt;p&gt;Softmax를 하나의 커널로 퓨전(fusion)하여 메모리 접근을 최소화합니다. 커널 퓨전이 왜 중요한지, reduction 연산을 어떻게 처리하는지 학습합니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;핵심-개념&quot;&gt;핵심 개념&lt;/h2&gt; &lt;h3 id=&quot;softmax-수식&quot;&gt;Softmax 수식&lt;/h3&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;softmax(x_i) = exp(x_i - max(x)) / Σ exp(x_j - max(x)) &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max(x)&lt;/code&gt;를 빼는 이유: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exp&lt;/code&gt;는 큰 값에서 오버플로우가 발생합니다. 최대값을 빼면 모든 지수가 0 이하가 되어 안정적으로 계산됩니다.&lt;/p&gt; &lt;h3 id=&quot;왜-커널-퓨전인가&quot;&gt;왜 커널 퓨전인가?&lt;/h3&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/02_fused_softmax/kernel_fusion-480.webp 480w,/assets/img/triton/02_fused_softmax/kernel_fusion-800.webp 800w,/assets/img/triton/02_fused_softmax/kernel_fusion-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/02_fused_softmax/kernel_fusion.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;reduction-연산&quot;&gt;Reduction 연산&lt;/h3&gt; &lt;p&gt;전체 데이터에서 하나의 값을 계산하는 연산:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max&lt;/code&gt;: 최대값&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sum&lt;/code&gt;: 합계&lt;/li&gt; &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mean&lt;/code&gt;: 평균&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Triton에서는 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.max(x, axis=0)&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.sum(x, axis=0)&lt;/code&gt; 으로 간단하게 수행합니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;커널-동작-원리&quot;&gt;커널 동작 원리&lt;/h2&gt; &lt;p&gt;입력 행렬의 각 &lt;strong&gt;행(row)&lt;/strong&gt; 을 하나의 프로그램이 처리합니다.&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/02_fused_softmax/row_processing-480.webp 480w,/assets/img/triton/02_fused_softmax/row_processing-800.webp 800w,/assets/img/triton/02_fused_softmax/row_processing-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/02_fused_softmax/row_processing.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;hr /&gt; &lt;h2 id=&quot;코드-라인별-설명&quot;&gt;코드 라인별 설명&lt;/h2&gt; &lt;h3 id=&quot;커널-함수&quot;&gt;커널 함수&lt;/h3&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=02_fused_softmax_snippet01_%EC%BB%A4%EB%84%90_%ED%95%A8%EC%88%98.py&quot;&gt;&lt;/script&gt; &lt;p&gt;핵심: &lt;strong&gt;max → exp → sum → 나누기&lt;/strong&gt;를 전부 SRAM 안에서 처리. PyTorch는 이 4단계를 각각 별도 커널로 실행하므로 매번 Global Memory를 왕복합니다.&lt;/p&gt; &lt;h3 id=&quot;래퍼-함수&quot;&gt;래퍼 함수&lt;/h3&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=02_fused_softmax_snippet02_%EB%9E%98%ED%8D%BC_%ED%95%A8%EC%88%98.py&quot;&gt;&lt;/script&gt; &lt;hr /&gt; &lt;h2 id=&quot;01-vector-add와의-차이점&quot;&gt;01 Vector Add와의 차이점&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt; &lt;/th&gt; &lt;th&gt;01 Vector Add&lt;/th&gt; &lt;th&gt;02 Fused Softmax&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;처리 단위&lt;/td&gt; &lt;td&gt;1D 벡터의 청크&lt;/td&gt; &lt;td&gt;2D 행렬의 행&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;프로그램당 연산&lt;/td&gt; &lt;td&gt;덧셈 1번&lt;/td&gt; &lt;td&gt;max+exp+sum+나누기&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;퓨전 효과&lt;/td&gt; &lt;td&gt;없음 (연산이 1개)&lt;/td&gt; &lt;td&gt;4개 연산을 1커널로&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;새로운 기능&lt;/td&gt; &lt;td&gt;-&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.max&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.sum&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.exp&lt;/code&gt;, stride&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h2 id=&quot;벤치마크-결과&quot;&gt;벤치마크 결과&lt;/h2&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/02_fused_softmax/benchmark-480.webp 480w,/assets/img/triton/02_fused_softmax/benchmark-800.webp 800w,/assets/img/triton/02_fused_softmax/benchmark-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/02_fused_softmax/benchmark.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;p&gt;커널 퓨전 덕분에 메모리 대역폭을 절약하여, 특히 열(column) 수가 클수록 PyTorch 대비 성능 향상이 눈에 띕니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;전체-코드&quot;&gt;전체 코드&lt;/h2&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/0f4970e5dbed9af5037d796fa395727f.js?file=fused_softmax.py&quot;&gt;&lt;/script&gt; </description> <pubDate>Wed, 01 Apr 2026 15:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2026/triton-02-fused-softmax/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2026/triton-02-fused-softmax/</guid> <category>triton</category> <category>gpu</category> <category>softmax</category> <category>kernel-fusion</category> <category>triton</category> </item> <item> <title>Triton 01: Vector Addition — Triton 커널 기초</title> <description>&lt;h2 id=&quot;개요&quot;&gt;개요&lt;/h2&gt; &lt;p&gt;가장 간단한 GPU 커널인 벡터 덧셈을 구현합니다. 이 튜토리얼에서 Triton의 핵심 개념을 모두 배울 수 있습니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;핵심-개념&quot;&gt;핵심 개념&lt;/h2&gt; &lt;h3 id=&quot;gpu-병렬-프로그래밍&quot;&gt;GPU 병렬 프로그래밍&lt;/h3&gt; &lt;p&gt;CPU는 순차적으로 빠르게 처리하고, GPU는 수천 개의 코어로 동시에 처리합니다.&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/01_vector_add/cpu_vs_gpu_parallel-480.webp 480w,/assets/img/triton/01_vector_add/cpu_vs_gpu_parallel-800.webp 800w,/assets/img/triton/01_vector_add/cpu_vs_gpu_parallel-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/01_vector_add/cpu_vs_gpu_parallel.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;cuda-vs-triton&quot;&gt;CUDA vs Triton&lt;/h3&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;구분&lt;/th&gt; &lt;th&gt;CUDA&lt;/th&gt; &lt;th&gt;Triton&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;언어&lt;/td&gt; &lt;td&gt;C/C++&lt;/td&gt; &lt;td&gt;Python&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;메모리 관리&lt;/td&gt; &lt;td&gt;수동 (shared memory 직접 관리)&lt;/td&gt; &lt;td&gt;자동 (컴파일러가 처리)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;스레드 관리&lt;/td&gt; &lt;td&gt;warp/thread 단위&lt;/td&gt; &lt;td&gt;block(프로그램) 단위&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;난이도&lt;/td&gt; &lt;td&gt;높음&lt;/td&gt; &lt;td&gt;낮음&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;성능&lt;/td&gt; &lt;td&gt;최고&lt;/td&gt; &lt;td&gt;CUDA의 90%+ 달성 가능&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;h3 id=&quot;triton-핵심-용어&quot;&gt;Triton 핵심 용어&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;커널(Kernel)&lt;/strong&gt;: GPU에서 실행되는 함수&lt;/li&gt; &lt;li&gt;&lt;strong&gt;프로그램(Program)&lt;/strong&gt;: 커널의 하나의 인스턴스 (CUDA의 thread block에 해당)&lt;/li&gt; &lt;li&gt;&lt;strong&gt;그리드(Grid)&lt;/strong&gt;: 프로그램 인스턴스의 총 개수&lt;/li&gt; &lt;li&gt;&lt;strong&gt;BLOCK_SIZE&lt;/strong&gt;: 각 프로그램이 처리하는 데이터 크기&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;커널-동작-원리&quot;&gt;커널 동작 원리&lt;/h2&gt; &lt;p&gt;길이 N인 벡터를 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BLOCK_SIZE&lt;/code&gt; 크기의 청크로 나누고, 각 프로그램이 하나의 청크를 담당합니다.&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/01_vector_add/vector_chunking-480.webp 480w,/assets/img/triton/01_vector_add/vector_chunking-800.webp 800w,/assets/img/triton/01_vector_add/vector_chunking-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/01_vector_add/vector_chunking.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;단계별-분석&quot;&gt;단계별 분석&lt;/h3&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=01_vector_add_snippet01_%EB%8B%A8%EA%B3%84%EB%B3%84_%EB%B6%84%EC%84%9D.py&quot;&gt;&lt;/script&gt; &lt;hr /&gt; &lt;h2 id=&quot;사용된-triton-기능&quot;&gt;사용된 Triton 기능&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;기능&lt;/th&gt; &lt;th&gt;설명&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@triton.jit&lt;/code&gt;&lt;/td&gt; &lt;td&gt;함수를 Triton 커널로 컴파일&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.program_id(axis)&lt;/code&gt;&lt;/td&gt; &lt;td&gt;현재 프로그램의 ID (어떤 청크를 처리할지 결정)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.arange(start, end)&lt;/code&gt;&lt;/td&gt; &lt;td&gt;연속 정수 벡터 생성 (numpy의 arange와 유사)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.load(ptr, mask)&lt;/code&gt;&lt;/td&gt; &lt;td&gt;Global Memory에서 데이터 읽기&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.store(ptr, value, mask)&lt;/code&gt;&lt;/td&gt; &lt;td&gt;Global Memory에 데이터 쓰기&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.constexpr&lt;/code&gt;&lt;/td&gt; &lt;td&gt;컴파일 타임 상수 (BLOCK_SIZE처럼 컴파일 시 결정되는 값)&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h2 id=&quot;마스크mask란&quot;&gt;마스크(Mask)란?&lt;/h2&gt; &lt;p&gt;벡터 길이가 BLOCK_SIZE의 배수가 아닐 때 경계 처리가 필요합니다.&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/01_vector_add/mask_explanation-480.webp 480w,/assets/img/triton/01_vector_add/mask_explanation-800.webp 800w,/assets/img/triton/01_vector_add/mask_explanation-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/01_vector_add/mask_explanation.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;hr /&gt; &lt;h2 id=&quot;그리드-설정&quot;&gt;그리드 설정&lt;/h2&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/01_vector_add/grid_launch-480.webp 480w,/assets/img/triton/01_vector_add/grid_launch-800.webp 800w,/assets/img/triton/01_vector_add/grid_launch-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/01_vector_add/grid_launch.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;hr /&gt; &lt;h2 id=&quot;래퍼-함수&quot;&gt;래퍼 함수&lt;/h2&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/42cd2b629a46d83e348bc15c5aa83a17.js?file=01_vector_add_snippet02_%EB%9E%98%ED%8D%BC_%ED%95%A8%EC%88%98.py&quot;&gt;&lt;/script&gt; &lt;hr /&gt; &lt;h2 id=&quot;포인터pointer란&quot;&gt;포인터(Pointer)란?&lt;/h2&gt; &lt;p&gt;C/CUDA 경험이 없으면 포인터가 낯설 수 있습니다:&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/01_vector_add/pointer_explanation-480.webp 480w,/assets/img/triton/01_vector_add/pointer_explanation-800.webp 800w,/assets/img/triton/01_vector_add/pointer_explanation-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/01_vector_add/pointer_explanation.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;hr /&gt; &lt;h2 id=&quot;벤치마크-결과&quot;&gt;벤치마크 결과&lt;/h2&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/01_vector_add/benchmark-480.webp 480w,/assets/img/triton/01_vector_add/benchmark-800.webp 800w,/assets/img/triton/01_vector_add/benchmark-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/01_vector_add/benchmark.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;p&gt;Vector Add는 &lt;strong&gt;메모리 대역폭 바운드(memory-bound)&lt;/strong&gt; 연산입니다. 연산량이 적고 데이터 이동이 대부분이라, Triton과 PyTorch의 성능 차이가 크지 않습니다. 하지만 이 패턴은 이후 모든 커널의 기초가 됩니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;전체-코드&quot;&gt;전체 코드&lt;/h2&gt; &lt;script src=&quot;https://gist.github.com/wonbeomjang/0f4970e5dbed9af5037d796fa395727f.js?file=vector_add.py&quot;&gt;&lt;/script&gt; </description> <pubDate>Wed, 01 Apr 2026 15:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2026/triton-01-vector-add/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2026/triton-01-vector-add/</guid> <category>triton</category> <category>gpu</category> <category>cuda</category> <category>deep-learning</category> <category>triton</category> </item> <item> <title>Triton 00: GPU 기초 — Triton을 시작하기 전에 알아야 할 것들</title> <description>&lt;h2 id=&quot;gpu란-무엇인가&quot;&gt;GPU란 무엇인가?&lt;/h2&gt; &lt;p&gt;GPU(Graphics Processing Unit)는 원래 그래픽 렌더링을 위해 만들어졌지만, 지금은 딥러닝과 과학 계산의 핵심 장치입니다.&lt;/p&gt; &lt;h3 id=&quot;cpu-vs-gpu&quot;&gt;CPU vs GPU&lt;/h3&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/00_gpu_basics/cpu_vs_gpu-480.webp 480w,/assets/img/triton/00_gpu_basics/cpu_vs_gpu-800.webp 800w,/assets/img/triton/00_gpu_basics/cpu_vs_gpu-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/00_gpu_basics/cpu_vs_gpu.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;p&gt;딥러닝은 “쉬운 연산(곱하기, 더하기)을 엄청나게 많이” 하는 작업이라 GPU가 유리합니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;rtx-4080-스펙-이해하기&quot;&gt;RTX 4080 스펙 이해하기&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;스펙&lt;/th&gt; &lt;th&gt;값&lt;/th&gt; &lt;th&gt;의미&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;CUDA 코어&lt;/td&gt; &lt;td&gt;9,728개&lt;/td&gt; &lt;td&gt;동시에 연산할 수 있는 유닛 수&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;SM (Streaming Multiprocessor)&lt;/td&gt; &lt;td&gt;76개&lt;/td&gt; &lt;td&gt;CUDA 코어들을 묶은 그룹 (128코어/SM)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;VRAM (Global Memory)&lt;/td&gt; &lt;td&gt;16GB GDDR6X&lt;/td&gt; &lt;td&gt;GPU 전용 메모리 (모델/데이터 저장)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;메모리 대역폭&lt;/td&gt; &lt;td&gt;~717 GB/s&lt;/td&gt; &lt;td&gt;1초에 읽을 수 있는 데이터 양&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;L2 캐시&lt;/td&gt; &lt;td&gt;64MB&lt;/td&gt; &lt;td&gt;자주 쓰는 데이터 임시 저장&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;SRAM (Shared Memory)&lt;/td&gt; &lt;td&gt;64KB / SM&lt;/td&gt; &lt;td&gt;각 SM 내부의 초고속 메모리&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;텐서 코어&lt;/td&gt; &lt;td&gt;4세대&lt;/td&gt; &lt;td&gt;행렬 곱셈 전용 하드웨어 (매우 빠름)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Compute Capability&lt;/td&gt; &lt;td&gt;8.9 (Ada Lovelace)&lt;/td&gt; &lt;td&gt;GPU 아키텍처 세대&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;hr /&gt; &lt;h2 id=&quot;gpu-메모리-계층-매우-중요&quot;&gt;GPU 메모리 계층 (매우 중요!)&lt;/h2&gt; &lt;p&gt;GPU 프로그래밍에서 &lt;strong&gt;가장 중요한 개념&lt;/strong&gt;이 메모리 계층입니다. 성능 최적화의 핵심은 “느린 메모리 접근을 줄이는 것”입니다.&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/00_gpu_basics/memory_hierarchy-480.webp 480w,/assets/img/triton/00_gpu_basics/memory_hierarchy-800.webp 800w,/assets/img/triton/00_gpu_basics/memory_hierarchy-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/00_gpu_basics/memory_hierarchy.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;속도-비교-대략적&quot;&gt;속도 비교 (대략적)&lt;/h3&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;메모리&lt;/th&gt; &lt;th&gt;대역폭&lt;/th&gt; &lt;th&gt;HBM 대비&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;HBM (Global Memory)&lt;/td&gt; &lt;td&gt;~717 GB/s&lt;/td&gt; &lt;td&gt;1x&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;L2 Cache&lt;/td&gt; &lt;td&gt;~3-5 TB/s&lt;/td&gt; &lt;td&gt;~5x&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;SRAM (Shared Memory)&lt;/td&gt; &lt;td&gt;~19 TB/s&lt;/td&gt; &lt;td&gt;~26x&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;레지스터&lt;/td&gt; &lt;td&gt;훨씬 빠름&lt;/td&gt; &lt;td&gt;~100x+&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;&lt;strong&gt;핵심&lt;/strong&gt;: HBM에서 데이터를 읽는 것이 &lt;strong&gt;병목&lt;/strong&gt;입니다. 데이터를 한 번 SRAM으로 올리면, 그 안에서 여러 연산을 하는 게 훨씬 빠릅니다. 이것이 &lt;strong&gt;커널 퓨전&lt;/strong&gt;의 핵심 원리입니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;sm-streaming-multiprocessor-이해하기&quot;&gt;SM (Streaming Multiprocessor) 이해하기&lt;/h2&gt; &lt;p&gt;SM은 GPU의 “미니 프로세서”입니다. RTX 4080에는 76개의 SM이 있습니다.&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/00_gpu_basics/sm_architecture-480.webp 480w,/assets/img/triton/00_gpu_basics/sm_architecture-800.webp 800w,/assets/img/triton/00_gpu_basics/sm_architecture-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/00_gpu_basics/sm_architecture.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;warp란&quot;&gt;Warp란?&lt;/h3&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;스레드(Thread)&lt;/strong&gt;: GPU에서 실행되는 가장 작은 실행 단위&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Warp&lt;/strong&gt;: 32개 스레드의 묶음 (항상 32개가 &lt;strong&gt;동시에 같은 명령&lt;/strong&gt; 실행)&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Thread Block&lt;/strong&gt;: 여러 Warp의 묶음 (최대 1024 스레드)&lt;/li&gt; &lt;/ul&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Thread Block ├── Warp 0: [thread 0~31] ← 32개가 동시에 같은 명령 실행 ├── Warp 1: [thread 32~63] ├── Warp 2: [thread 64~95] └── ... &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;&lt;strong&gt;Triton이 좋은 이유&lt;/strong&gt;: CUDA에서는 이 모든 것을 직접 관리해야 하지만, Triton에서는 &lt;strong&gt;블록(프로그램) 단위&lt;/strong&gt;로 생각하면 됩니다. Warp 관리는 컴파일러가 처리!&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;compute-bound-vs-memory-bound&quot;&gt;Compute-Bound vs Memory-Bound&lt;/h2&gt; &lt;p&gt;GPU 연산은 두 가지 유형으로 나뉩니다:&lt;/p&gt; &lt;h3 id=&quot;memory-bound-메모리-병목&quot;&gt;Memory-Bound (메모리 병목)&lt;/h3&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;연산량이 적고, 데이터 이동이 대부분인 경우 예: Vector Add, RMSNorm, Softmax → 원소 하나당 덧셈 1번만 하면 됨 → 대부분의 시간이 HBM에서 데이터 읽기/쓰기에 소비 최적화 전략: 커널 퓨전 (메모리 접근 횟수 줄이기) &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3 id=&quot;compute-bound-연산-병목&quot;&gt;Compute-Bound (연산 병목)&lt;/h3&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;연산량이 많고, 계산에 시간이 걸리는 경우 예: Matrix Multiplication (행렬 곱셈) → 원소 하나를 계산하려면 K번의 곱셈+덧셈 필요 → 연산량이 데이터 양보다 훨씬 많음 최적화 전략: 텐서 코어 활용, 타일링으로 데이터 재사용 &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3 id=&quot;arithmetic-intensity-연산-강도&quot;&gt;Arithmetic Intensity (연산 강도)&lt;/h3&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;AI = 연산 횟수 / 메모리 접근 바이트 수 Vector Add: AI = 1 (낮음) → Memory-Bound Matrix Multiply: AI = N (높음) → Compute-Bound &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;연산 강도가 높을수록 GPU의 계산 능력을 효과적으로 활용합니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;텐서-코어-tensor-core&quot;&gt;텐서 코어 (Tensor Core)&lt;/h2&gt; &lt;p&gt;행렬 곱셈을 위한 &lt;strong&gt;전용 하드웨어&lt;/strong&gt;입니다.&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;일반 CUDA 코어: 텐서 코어: 한 클럭에 1번의 FMA 한 클럭에 4x4x4 행렬 곱 (a*b + c) (64번의 FMA를 한 번에!) 속도: 1x 속도: ~16x (fp16 기준) &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;Triton에서 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.dot(a, b)&lt;/code&gt;를 사용하면 자동으로 텐서 코어가 활용됩니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;데이터-타입--fp32-fp16-bf16-int8&quot;&gt;데이터 타입 — FP32, FP16, BF16, INT8&lt;/h2&gt; &lt;p&gt;GPU 연산에서 데이터 타입 선택은 &lt;strong&gt;속도와 메모리&lt;/strong&gt; 모두에 영향을 줍니다.&lt;/p&gt; &lt;h3 id=&quot;부동소수점이란&quot;&gt;부동소수점이란?&lt;/h3&gt; &lt;p&gt;컴퓨터에서 소수점 있는 숫자를 표현하는 방식입니다.&lt;/p&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;3.14159를 저장하려면? 부호(sign) | 지수(exponent) | 가수(mantissa) ± | 크기 범위 | 정밀도 비유: 과학적 표기법과 같습니다 3.14159 = 3.14159 × 10^0 31415.9 = 3.14159 × 10^4 ^^^^^^^^ ^^^^ 가수 지수 &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/00_gpu_basics/data_types-480.webp 480w,/assets/img/triton/00_gpu_basics/data_types-800.webp 800w,/assets/img/triton/00_gpu_basics/data_types-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/00_gpu_basics/data_types.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;h3 id=&quot;왜-중요한가&quot;&gt;왜 중요한가?&lt;/h3&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;같은 모델을 다른 타입으로 저장하면: Llama 3 8B (80억 파라미터): FP32: 80억 × 4바이트 = 32GB → RTX 4080에 안 들어감! FP16: 80억 × 2바이트 = 16GB → 빠듯하게 들어감 INT8: 80억 × 1바이트 = 8GB → 여유 있게 들어감 INT4: 80억 × 0.5바이트 = 4GB → 넉넉! &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3 id=&quot;triton에서의-데이터-타입&quot;&gt;Triton에서의 데이터 타입&lt;/h3&gt; &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# 커널 내에서 타입 변환 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x_fp32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;float32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 정밀한 중간 계산용 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;float16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 저장 시 FP16으로 변환 &lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# tl.dot은 내부적으로 FP32로 누적 (정밀도 유지) &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dot&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# a, b는 FP16이지만 acc는 FP32 &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;hr /&gt; &lt;h2 id=&quot;대역폭-계산법--내-커널이-얼마나-효율적인지-측정하기&quot;&gt;대역폭 계산법 — 내 커널이 얼마나 효율적인지 측정하기&lt;/h2&gt; &lt;p&gt;GPU 커널의 성능을 평가할 때 가장 중요한 지표가 &lt;strong&gt;메모리 대역폭 활용률&lt;/strong&gt;입니다.&lt;/p&gt; &lt;h3 id=&quot;기본-공식&quot;&gt;기본 공식&lt;/h3&gt; &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;실효 대역폭 (GB/s) = (읽은 바이트 + 쓴 바이트) / 실행 시간(초) / 10^9 &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3 id=&quot;예제-vector-add&quot;&gt;예제: Vector Add&lt;/h3&gt; &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# 크기 N인 float32 벡터 2개를 읽고, 1개를 쓰는 경우 &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10_000_000&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 1천만 &lt;/span&gt; &lt;span class=&quot;n&quot;&gt;읽기&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;x &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;×&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;바이트&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;y &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;×&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;바이트&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MB&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;쓰기&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;output &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;N&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;×&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;바이트&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MB&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;총&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;데이터&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;이동&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MB&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;실행&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;시간이&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.2&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ms&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;걸렸다면&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;실효&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;대역폭&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MB&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.0002&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;초&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RTX&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4080&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;최대&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;대역폭&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;717&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;활용률&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;600&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;717&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;83.7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;err&quot;&gt;←&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;꽤&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;좋은&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;편&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;!&lt;/span&gt; &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;hr /&gt; &lt;h2 id=&quot;occupancy--gpu를-얼마나-바쁘게-유지하는가&quot;&gt;Occupancy — GPU를 얼마나 바쁘게 유지하는가&lt;/h2&gt; &lt;p&gt;Occupancy는 SM이 실행할 수 있는 최대 warp 수 대비 실제 활성 warp의 비율입니다.&lt;/p&gt; &lt;h3 id=&quot;왜-중요한가-1&quot;&gt;왜 중요한가?&lt;/h3&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/00_gpu_basics/latency_hiding-480.webp 480w,/assets/img/triton/00_gpu_basics/latency_hiding-800.webp 800w,/assets/img/triton/00_gpu_basics/latency_hiding-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/00_gpu_basics/latency_hiding.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;p&gt;이것을 &lt;strong&gt;Latency Hiding (지연 숨기기)&lt;/strong&gt; 이라고 합니다. Warp가 많을수록 GPU가 쉬지 않고 일할 수 있습니다.&lt;/p&gt; &lt;h3 id=&quot;triton에서는&quot;&gt;Triton에서는?&lt;/h3&gt; &lt;p&gt;Triton 컴파일러가 자동으로 레지스터와 SRAM 사용을 최적화하므로, 대부분의 경우 Occupancy를 직접 걱정할 필요가 없습니다. 하지만 &lt;strong&gt;BLOCK_SIZE를 너무 크게&lt;/strong&gt; 잡으면 SRAM이 부족해져 Occupancy가 떨어질 수 있습니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;커널-실행의-전체-흐름&quot;&gt;커널 실행의 전체 흐름&lt;/h2&gt; &lt;p&gt;Python에서 Triton 커널을 호출하면 실제로 무슨 일이 일어나는지 봅시다.&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/00_gpu_basics/kernel_execution_flow-480.webp 480w,/assets/img/triton/00_gpu_basics/kernel_execution_flow-800.webp 800w,/assets/img/triton/00_gpu_basics/kernel_execution_flow-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/00_gpu_basics/kernel_execution_flow.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;hr /&gt; &lt;h2 id=&quot;roofline-model--내-커널의-한계를-이해하기&quot;&gt;Roofline Model — 내 커널의 한계를 이해하기&lt;/h2&gt; &lt;p&gt;Roofline Model은 “이 GPU에서 이 커널이 이론적으로 얼마나 빠를 수 있는가”를 알려줍니다.&lt;/p&gt; &lt;figure&gt; &lt;picture&gt; &lt;!-- Auto scaling with imagemagick --&gt; &lt;!-- See https://www.debugbear.com/blog/responsive-images#w-descriptors-and-the-sizes-attribute and https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images for info on defining &apos;sizes&apos; for responsive images --&gt; &lt;source class=&quot;responsive-img-srcset&quot; srcset=&quot;/assets/img/triton/00_gpu_basics/roofline_model-480.webp 480w,/assets/img/triton/00_gpu_basics/roofline_model-800.webp 800w,/assets/img/triton/00_gpu_basics/roofline_model-1400.webp 1400w,&quot; type=&quot;image/webp&quot; sizes=&quot;95vw&quot; /&gt; &lt;img src=&quot;/assets/img/triton/00_gpu_basics/roofline_model.png&quot; class=&quot;img-fluid rounded z-depth-1&quot; width=&quot;100%&quot; height=&quot;auto&quot; loading=&quot;lazy&quot; onerror=&quot;this.onerror=null; $(&apos;.responsive-img-srcset&apos;).remove();&quot; /&gt; &lt;/picture&gt; &lt;/figure&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;연산 강도(AI) &amp;lt; 68&lt;/strong&gt; → Memory-Bound (메모리가 병목) — Vector Add, Softmax, RMSNorm&lt;/li&gt; &lt;li&gt;&lt;strong&gt;연산 강도(AI) &amp;gt; 68&lt;/strong&gt; → Compute-Bound (연산이 병목) — MatMul, Flash Attention&lt;/li&gt; &lt;/ul&gt; &lt;hr /&gt; &lt;h2 id=&quot;triton이-이-모든-것을-어떻게-단순화하는가&quot;&gt;Triton이 이 모든 것을 어떻게 단순화하는가&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;CUDA에서 직접 해야 하는 것&lt;/th&gt; &lt;th&gt;Triton에서는?&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Shared memory 할당/관리&lt;/td&gt; &lt;td&gt;자동 (컴파일러가 처리)&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Warp 동기화 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__syncthreads&lt;/code&gt;)&lt;/td&gt; &lt;td&gt;불필요&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Memory coalescing 최적화&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.arange&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.load&lt;/code&gt;로 자동&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;텐서 코어 호출 (WMMA API)&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tl.dot&lt;/code&gt;으로 자동&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;Thread block 크기 결정&lt;/td&gt; &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BLOCK_SIZE&lt;/code&gt;만 지정&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;레지스터 압박 관리&lt;/td&gt; &lt;td&gt;컴파일러가 최적화&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; &lt;p&gt;&lt;strong&gt;결론&lt;/strong&gt;: Triton은 “GPU의 성능은 거의 다 뽑아내면서, CUDA보다 10배 쉽게 작성”할 수 있는 도구입니다.&lt;/p&gt; &lt;hr /&gt; &lt;h2 id=&quot;용어-정리&quot;&gt;용어 정리&lt;/h2&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;용어&lt;/th&gt; &lt;th&gt;의미&lt;/th&gt; &lt;th&gt;비유&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;HBM / VRAM / Global Memory&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;GPU의 메인 메모리 (16GB)&lt;/td&gt; &lt;td&gt;큰 창고&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;SRAM / Shared Memory&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;SM 내부의 고속 메모리 (64KB)&lt;/td&gt; &lt;td&gt;작업대 위 선반&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;레지스터&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;연산 유닛 바로 옆의 메모리&lt;/td&gt; &lt;td&gt;손에 들고 있는 것&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;SM&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;코어 묶음 + 메모리를 가진 미니 프로세서&lt;/td&gt; &lt;td&gt;공장 하나&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;Warp&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;32개 스레드 묶음 (항상 같이 움직임)&lt;/td&gt; &lt;td&gt;32명이 한 줄로 행진&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;커널 퓨전&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;여러 연산을 하나의 커널로 합침&lt;/td&gt; &lt;td&gt;왕복 줄이기&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt;&lt;strong&gt;타일링&lt;/strong&gt;&lt;/td&gt; &lt;td&gt;큰 데이터를 작은 블록으로 나눠 처리&lt;/td&gt; &lt;td&gt;퍼즐 조각&lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; &lt;/table&gt; </description> <pubDate>Wed, 01 Apr 2026 15:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2026/triton-00-gpu-basics/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2026/triton-00-gpu-basics/</guid> <category>triton</category> <category>gpu</category> <category>cuda</category> <category>deep-learning</category> <category>triton</category> </item> <item> <title>LoRA vs Full Fine-tuning: An Illusion of Equivalence</title> <description>&lt;blockquote&gt; &lt;p&gt;&lt;a href=&quot;https://arxiv.org/abs/2410.21228&quot;&gt;LoRA vs Full Fine-tuning: An Illusion of Equivalence&lt;/a&gt;&lt;/p&gt; &lt;/blockquote&gt; &lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt; &lt;p&gt;Pre-trained 모델을 downstream task에 fine-tuning하는 것은 computation-, data-efficient한 방법이다. 하지만 full fine-tuning은 모든 파라미터를 업데이트해야 하므로 시간과 비용적으로 부담이 크다. 이를 해결하기 위해 LoRA(Low-Rank Adaptation)와 같은 PEFT(Parameter-Efficient Fine-Tuning) 방법이 제시되었다. LoRA는 전체 파라미터의 극히 일부만 학습하면서도 full fine-tuning에 필적하는 성능을 보여주어 널리 사용되고 있다.&lt;/p&gt; &lt;p&gt;하지만 &lt;strong&gt;성능이 비슷하다고 해서 두 방법이 정말 같은 solution을 학습하는 걸까?&lt;/strong&gt; 저자는 이 질문에 대해 weight matrix의 &lt;strong&gt;spectral properties&lt;/strong&gt;(singular value decomposition)를 분석하여 답한다. 결론적으로, 같은 성능을 내더라도 LoRA와 full fine-tuning은 &lt;strong&gt;구조적으로 매우 다른&lt;/strong&gt; 모델을 만든다.&lt;/p&gt; &lt;p&gt;핵심 발견:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;LoRA는 &lt;strong&gt;intruder dimensions&lt;/strong&gt;을 도입한다 — pre-trained weight의 singular vector와 거의 직교(orthogonal)하는 새로운 high-ranking singular vector가 나타난다. Full fine-tuning에서는 이런 현상이 없다.&lt;/li&gt; &lt;li&gt;Intruder dimensions은 &lt;strong&gt;forgetting을 유발&lt;/strong&gt;한다 — intruder dimensions의 singular value를 줄이면 pre-training distribution의 모델링이 크게 개선되고, downstream 성능 저하는 미미하다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Continual learning에서 LoRA가 더 취약&lt;/strong&gt;하다 — 여러 task를 순차적으로 학습할 때 intruder dimensions이 누적되어 성능이 떨어진다.&lt;/li&gt; &lt;/ol&gt; &lt;h1 id=&quot;background&quot;&gt;Background&lt;/h1&gt; &lt;h2 id=&quot;lora-low-rank-adaptation&quot;&gt;LoRA: Low-Rank Adaptation&lt;/h2&gt; &lt;p&gt;Pre-trained weight matrix \(W_0 \in \mathbb{R}^{m \times n}\)에 대해, full fine-tuning은 모든 원소를 업데이트하여 \(W = W_0 + \Delta W\)를 학습한다. 이때 학습 가능한 파라미터 수는 \(mn\)이다.&lt;/p&gt; &lt;p&gt;LoRA는 \(\Delta W\)를 두 개의 low-rank 행렬의 곱으로 분해한다.&lt;/p&gt; \[\Delta W = \frac{\alpha}{r} BA, \quad B \in \mathbb{R}^{m \times r}, \; A \in \mathbb{R}^{r \times n}\] &lt;p&gt;여기서 \(r \ll \min(m, n)\)이 rank이다. 학습 가능한 파라미터 수는 \((m + n)r\)로, \(mn\)에 비해 매우 작다. 예를 들어 \(m = n = 4096\)이고 \(r = 16\)이면, 파라미터가 \(16.8M\)에서 \(131K\)로 약 &lt;strong&gt;128배&lt;/strong&gt; 줄어든다.&lt;/p&gt; &lt;p&gt;초기화 시 \(B = 0\), \(A \sim \mathcal{N}(0, \sigma^2)\)으로 설정하여 학습 시작 시 \(\Delta W = 0\)이 되도록 한다. Inference 시에는:&lt;/p&gt; \[Y = W_{\text{tuned}} X = \left(W_0 + \frac{\alpha}{r} BA\right) X\] &lt;h3 id=&quot;scaling-parameter-alpha&quot;&gt;Scaling Parameter \(\alpha\)&lt;/h3&gt; &lt;p&gt;\(\alpha\)는 LoRA update의 크기를 조절하는 파라미터이다. 많은 실무에서 \(\alpha = 2r\)로 설정하는데, 이는 rank가 바뀌어도 update 크기가 일정하게 유지되도록 하기 위함이다. 이 논문에서는 \(\alpha\)의 선택이 intruder dimensions의 수와 forgetting에 큰 영향을 미친다는 것을 보여준다.&lt;/p&gt; &lt;h2 id=&quot;singular-value-decomposition-svd&quot;&gt;Singular Value Decomposition (SVD)&lt;/h2&gt; &lt;p&gt;임의의 행렬 \(M \in \mathbb{R}^{m \times n}\)은 다음과 같이 분해할 수 있다.&lt;/p&gt; \[M = U \Sigma V^\top\] &lt;ul&gt; &lt;li&gt;\(U \in \mathbb{R}^{m \times m}\): 왼쪽 singular vectors (열벡터 \(u_1, \ldots, u_m\))&lt;/li&gt; &lt;li&gt;\(\Sigma \in \mathbb{R}^{m \times n}\): singular values \(\sigma_1 \geq \sigma_2 \geq \ldots \geq 0\) (대각 행렬)&lt;/li&gt; &lt;li&gt;\(V \in \mathbb{R}^{n \times n}\): 오른쪽 singular vectors&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Singular value가 큰 singular vector일수록 행렬에서 더 중요한 방향을 나타낸다. 이 논문에서는 fine-tuning 전후의 singular vector 변화를 분석하여 LoRA와 full fine-tuning의 구조적 차이를 밝힌다.&lt;/p&gt; &lt;h3 id=&quot;effective-rank&quot;&gt;Effective Rank&lt;/h3&gt; &lt;p&gt;Effective rank는 singular value가 얼마나 집중되어 있는지를 측정한다. Frobenius norm의 90%를 차지하는 데 필요한 singular value의 개수로 정의한다. Effective rank가 낮으면 정보가 소수의 차원에 집중되어 있다는 뜻이다.&lt;/p&gt; &lt;h1 id=&quot;structural-differences-intruder-dimensions&quot;&gt;Structural Differences: Intruder Dimensions&lt;/h1&gt; &lt;h2 id=&quot;핵심-관찰&quot;&gt;핵심 관찰&lt;/h2&gt; &lt;p&gt;저자는 Sharma et al. (2024)의 SVD 기반 pruning에서 영감을 얻어, fine-tuned weight matrix의 singular vector를 pre-trained weight matrix의 singular vector와 비교했다. 구체적으로, fine-tuned 모델의 각 singular vector \(y_j\)와 pre-trained 모델의 모든 singular vector \(x_i\) 사이의 &lt;strong&gt;최대 cosine similarity&lt;/strong&gt;를 측정했다.&lt;/p&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;Fig. 2는 이 분석의 핵심 결과를 보여준다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;(a)&lt;/strong&gt; LoRA는 \(W_0 + BA\)로, full fine-tuning은 \(W_0 + \Delta W\)로 weight를 업데이트한다. 각각의 SVD를 구해서 pre-trained weight의 SVD와 비교한다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;(b)&lt;/strong&gt; Full fine-tuning의 singular vector는 pre-trained singular vector와 높은 cosine similarity를 가진다 (대각선 구조). 반면 LoRA(\(r = 64\))의 singular vector는 &lt;strong&gt;일부가 매우 낮은 cosine similarity&lt;/strong&gt;를 보인다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;(c)&lt;/strong&gt; Intruder dimension은 모든 pre-trained singular vector와 낮은 cosine similarity를 가진다 (빨간 점).&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;intruder-dimension의-정의&quot;&gt;Intruder Dimension의 정의&lt;/h2&gt; &lt;blockquote&gt; &lt;p&gt;&lt;strong&gt;Definition 3.1.&lt;/strong&gt; Fine-tuned weight matrix \(W_{\text{tuned}}\)의 singular vector \(y_j\)가 &lt;strong&gt;intruder dimension&lt;/strong&gt;이라 함은, pre-trained weight matrix \(W_0\)의 모든 singular vector \(x_i\)에 대해 \(\max_i(\cos(y_j, x_i)) &amp;lt; \epsilon\)을 만족하는 것이다. 여기서 \(\epsilon\)은 similarity threshold이다.&lt;/p&gt; &lt;/blockquote&gt; &lt;p&gt;직관적으로, intruder dimension은 pre-trained 모델이 &lt;strong&gt;전혀 알지 못하던 새로운 방향&lt;/strong&gt;이다. Full fine-tuning은 기존 방향을 미세하게 조정하는 반면, LoRA는 완전히 새로운 방향을 추가한다.&lt;/p&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 1.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;Fig. 3은 이 차이를 시각적으로 보여준다. Full fine-tuning (왼쪽)의 similarity matrix는 깨끗한 대각선 구조를 보인다. 즉, fine-tuned singular vector \(i\)가 pre-trained singular vector \(i\)에 잘 대응된다. 반면 LoRA (오른쪽)에는 &lt;strong&gt;빈 열(empty column)&lt;/strong&gt;이 존재하는데, 이것이 intruder dimensions이다 — 어떤 pre-trained singular vector와도 대응되지 않는 새로운 방향이다.&lt;/p&gt; &lt;h2 id=&quot;실험-모델과-데이터셋&quot;&gt;실험 모델과 데이터셋&lt;/h2&gt; &lt;p&gt;저자는 두 가지 모델로 실험했다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;RoBERTa-base&lt;/strong&gt; (125M): Encoder-only 모델. MNLI, QQP, SST-2, SiQA, WinoGrande, FEVER 등 6개 분류 태스크에서 fine-tuning&lt;/li&gt; &lt;li&gt;&lt;strong&gt;LLaMA2-7B&lt;/strong&gt; / &lt;strong&gt;LLaMA-7B&lt;/strong&gt;: Decoder-only 모델. Alpaca(instruction tuning), MetaMathQA(수학), Magicoder(코드) 등에서 fine-tuning&lt;/li&gt; &lt;/ul&gt; &lt;h2 id=&quot;실험-결과&quot;&gt;실험 결과&lt;/h2&gt; &lt;h3 id=&quot;1-lora는-high-ranking-intruder-dimensions을-가지지만-full-fine-tuning은-그렇지-않다&quot;&gt;1. LoRA는 high-ranking intruder dimensions을 가지지만, full fine-tuning은 그렇지 않다&lt;/h3&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 2.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;Top-\(k\) singular vectors에 대해 Algorithm 1을 적용하면:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;LoRA: \(r \leq 16\)에서 모든 \(\epsilon\)에 대해 &lt;strong&gt;지속적으로 intruder dimensions이 존재&lt;/strong&gt;한다.&lt;/li&gt; &lt;li&gt;Full fine-tuning: 심지어 \(\epsilon = 0.6 \sim 0.9\)처럼 관대한 threshold에서도 intruder dimensions이 거의 없다.&lt;/li&gt; &lt;li&gt;Rank가 올라갈수록 intruder dimensions이 줄어든다. \(r = 2048\)에서는 full fine-tuning과 유사해진다.&lt;/li&gt; &lt;/ul&gt; &lt;h3 id=&quot;2-수학코드-같은-어려운-태스크에서도-intruder-dimensions이-존재한다&quot;&gt;2. 수학/코드 같은 어려운 태스크에서도 intruder dimensions이 존재한다&lt;/h3&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 3.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;LLaMA2-7B를 MetaMathQA(수학)와 Magicoder(코드)로 fine-tuning한 경우에도 LoRA는 intruder dimensions을 보인다. 이는 pre-training domain과 target domain의 차이에서 비롯된다. 특히 코드처럼 pre-training과 매우 다른 도메인에서는 full fine-tuning도 일부 intruder dimensions을 보이지만, LoRA가 여전히 훨씬 많다.&lt;/p&gt; &lt;h3 id=&quot;3-full-fine-tuning은-lora보다-높은-effective-rank를-가진다&quot;&gt;3. Full fine-tuning은 LoRA보다 높은 effective rank를 가진다&lt;/h3&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 4.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;\(r = 768\)인 full-rank LoRA조차도 실제 effective rank는 약 300에 그친다. 즉, LoRA는 파라미터화된 capacity \(r\)을 &lt;strong&gt;전부 활용하지 못한다&lt;/strong&gt;. 반면 full fine-tuning의 \(\Delta W\)는 더 높은 effective rank로 업데이트를 수행한다. 이 차이는 코딩 같은 어려운 태스크에서 더 두드러진다.&lt;/p&gt; &lt;h3 id=&quot;4-intruder-dimensions은-singular-value의-크기와-무관하게-존재한다&quot;&gt;4. Intruder dimensions은 singular value의 크기와 무관하게 존재한다&lt;/h3&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 5.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;High singular value(중요한 방향)뿐 아니라 low singular value 영역에서도 LoRA는 full fine-tuning보다 항상 더 많은 intruder dimensions을 가진다.&lt;/p&gt; &lt;h3 id=&quot;5-alpha--2r로-설정하면-intruder-dimensions이-줄고-effective-rank가-늘어난다&quot;&gt;5. \(\alpha = 2r\)로 설정하면 intruder dimensions이 줄고 effective rank가 늘어난다&lt;/h3&gt; &lt;p&gt;많은 논문에서 \(\alpha = 2r\)로 설정한다. 저자는 \(\alpha = 2r\)과 \(\alpha = 8\)(고정)을 비교한 결과:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;\(\alpha = 8\): 모든 rank에서 intruder dimensions이 많고, effective rank가 낮다&lt;/li&gt; &lt;li&gt;\(\alpha = 2r\): intruder dimensions이 적고, effective rank가 높다 — &lt;strong&gt;generalization도 더 좋다&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;이는 \(\alpha = 2r\) 설정의 중요성을 뒷받침한다.&lt;/p&gt; &lt;h3 id=&quot;6-fine-tuning-데이터가-많을수록-intruder-dimensions이-늘어난다&quot;&gt;6. Fine-tuning 데이터가 많을수록 intruder dimensions이 늘어난다&lt;/h3&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 6.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;\(r = 8\)에서 여러 데이터셋을 학습시키면 intruder dimensions이 누적된다. \(r = 1\)에서는 표현력 한계 때문에 일정하지만, 충분한 rank가 있으면 데이터가 많을수록 새로운 intruder dimensions이 추가된다.&lt;/p&gt; &lt;h3 id=&quot;intruder-dimensions의-진화&quot;&gt;Intruder Dimensions의 진화&lt;/h3&gt; &lt;p&gt;Intruder dimensions은 학습 초기부터 점진적으로 나타난다. 학습이 진행됨에 따라:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Intruder dimension의 &lt;strong&gt;rank가 점점 높아진다&lt;/strong&gt; (더 중요한 위치를 차지한다)&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Singular value가 점점 커진다&lt;/strong&gt; (영향력이 증가한다)&lt;/li&gt; &lt;li&gt;Pre-trained singular vectors와의 &lt;strong&gt;cosine similarity는 계속 낮게 유지&lt;/strong&gt;된다&lt;/li&gt; &lt;/ol&gt; &lt;h1 id=&quot;model-differences-forgetting과-out-of-distribution-generalization&quot;&gt;Model Differences: Forgetting과 Out-of-Distribution Generalization&lt;/h1&gt; &lt;h2 id=&quot;lora는-full-fine-tuning보다-덜-잊지만-그-forgetting은-intruder-dimensions에-집중된다&quot;&gt;LoRA는 full fine-tuning보다 덜 잊지만, 그 forgetting은 intruder dimensions에 집중된다&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 7.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;Fig. 6에서 forgetting(pre-training distribution의 pseudo loss 증가)을 측정한 결과:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;LLaMA2-7B&lt;/strong&gt;: LoRA는 rank가 작을수록 더 많이 잊는다. Full fine-tuning이 가장 적게 잊는다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;RoBERTa-base&lt;/strong&gt;: 마찬가지로 LoRA의 rank가 낮을수록 pseudo loss가 크다.&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;놀라운 점은, learning rate가 클수록 intruder dimensions이 더 많이 생기고 (왼쪽), 그에 비례하여 forgetting도 증가한다는 것이다 (오른쪽). Intruder dimensions과 forgetting 사이의 &lt;strong&gt;Spearman correlation은 \(\rho = 0.971\) (\(p \ll 0.001\))&lt;/strong&gt;로 매우 강한 상관관계를 보인다.&lt;/p&gt; &lt;p&gt;반면 intruder dimensions과 test accuracy 사이에는 &lt;strong&gt;통계적으로 유의미한 상관관계가 없다&lt;/strong&gt;. 이는 intruder dimensions이 성능에는 불필요하지만 forgetting을 유발한다는 것을 의미한다.&lt;/p&gt; &lt;h2 id=&quot;pre-training-pseudo-loss의-u-shaped-curve&quot;&gt;Pre-training pseudo loss의 U-shaped curve&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 8.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;같은 test accuracy로 fine-tuning했을 때, pre-training pseudo loss가 rank에 대해 &lt;strong&gt;U자 곡선&lt;/strong&gt;을 그린다. 이는:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Rank가 너무 낮으면&lt;/strong&gt;: intruder dimensions의 영향으로 forgetting이 크다&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Rank가 너무 높으면&lt;/strong&gt;: overparameterization으로 target task에 overfitting&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;따라서 downstream task마다 &lt;strong&gt;최적의 rank가 존재&lt;/strong&gt;한다.&lt;/p&gt; &lt;h1 id=&quot;intruder-dimensions이-forgetting을-야기하는가-causal-intervention&quot;&gt;Intruder Dimensions이 Forgetting을 야기하는가: Causal Intervention&lt;/h1&gt; &lt;p&gt;상관관계를 넘어 &lt;strong&gt;인과관계&lt;/strong&gt;를 확인하기 위해, 저자는 intruder dimensions에 직접 개입(intervention)하는 실험을 수행한다.&lt;/p&gt; &lt;h2 id=&quot;실험-방법&quot;&gt;실험 방법&lt;/h2&gt; &lt;p&gt;각 weight matrix에서 가장 high-ranking인 intruder dimension의 singular value를 조절한다. 구체적으로:&lt;/p&gt; \[W = W_0 + \Delta W + (\lambda - 1) u_i \sigma_i v_i^\top\] &lt;p&gt;여기서 \(i\)는 top intruder dimension의 인덱스이다.&lt;/p&gt; &lt;ul&gt; &lt;li&gt;\(\lambda = 0\): intruder dimension 완전 제거&lt;/li&gt; &lt;li&gt;\(\lambda = 1\): 변화 없음 (원본)&lt;/li&gt; &lt;li&gt;\(\lambda &amp;gt; 1\): intruder dimension 증폭&lt;/li&gt; &lt;/ul&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 9.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;h2 id=&quot;결과&quot;&gt;결과&lt;/h2&gt; &lt;p&gt;Fig. 8에서 \(\lambda\)를 0부터 1까지 변화시키면:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Forgetting (빨간선)&lt;/strong&gt;: intruder dimensions을 줄이면 forgetting이 &lt;strong&gt;크게 감소&lt;/strong&gt;한다&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Test accuracy (초록선)&lt;/strong&gt;: 거의 변하지 않거나 아주 약간 감소한다&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;구체적 수치:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;LLaMA2-7B&lt;/strong&gt; (MetaMath, \(r = 256\)): \(\lambda = 0.3\)에서 test accuracy -0.1%, forgetting &lt;strong&gt;-33.3%&lt;/strong&gt;&lt;/li&gt; &lt;li&gt;&lt;strong&gt;RoBERTa&lt;/strong&gt; (QQP, \(r = 8\)): \(\lambda = 0.7\)에서 test accuracy 동일, forgetting &lt;strong&gt;-33.2%&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;일부 경우에는 \(\lambda &amp;gt; 1\) (intruder dimension 증폭)에서 오히려 test accuracy가 &lt;strong&gt;향상&lt;/strong&gt;되면서 forgetting이 &lt;strong&gt;악화&lt;/strong&gt;되는 현상도 관찰된다.&lt;/p&gt; &lt;p&gt;중요한 점은, 이 효과가 &lt;strong&gt;intruder dimensions에만 해당&lt;/strong&gt;한다는 것이다. Pre-trained singular vector에 가까운 normal dimensions의 singular value를 같은 방식으로 조절하면 forgetting에 큰 영향이 없고, 오히려 test accuracy가 떨어진다.&lt;/p&gt; &lt;h1 id=&quot;continual-learning에서의-영향&quot;&gt;Continual Learning에서의 영향&lt;/h1&gt; &lt;h2 id=&quot;실험-설정&quot;&gt;실험 설정&lt;/h2&gt; &lt;p&gt;Intruder dimensions이 누적되면 해로울 것이라는 가설을 검증하기 위해, RoBERTa를 6개 태스크에 &lt;strong&gt;순차적으로&lt;/strong&gt; fine-tuning한다: MNLI → QQP → SST-2 → SiQA → WinoGrande → FEVER.&lt;/p&gt; &lt;p&gt;각 태스크 학습 후 LoRA weights를 모델에 merge하고, adapter를 재초기화한 후 다음 태스크를 학습한다. 각 시점에서 모든 태스크의 성능을 측정한다.&lt;/p&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 10.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;h2 id=&quot;결과-1&quot;&gt;결과&lt;/h2&gt; &lt;p&gt;Fig. 9(a)에서:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Full fine-tuning&lt;/strong&gt;: 6개 태스크를 순차 학습해도 이전 태스크 성능이 비교적 잘 유지된다&lt;/li&gt; &lt;li&gt;&lt;strong&gt;LoRA&lt;/strong&gt; (\(r = 1, 8, 64\)): 모든 rank에서 full fine-tuning보다 빠르게 이전 태스크를 잊는다&lt;/li&gt; &lt;li&gt;Low rank(\(r = 1\))이 가장 빠르게 성능이 저하된다&lt;/li&gt; &lt;/ul&gt; &lt;p&gt;Fig. 9(b)에서 LoRA(\(r = 8\))의 similarity matrix를 보면, 각 태스크가 학습될 때마다 &lt;strong&gt;새로운 intruder dimensions이 추가&lt;/strong&gt;되는 것을 확인할 수 있다. MNLI(1번째) → QQP(2번째) → … → FEVER(6번째)로 갈수록 intruder dimensions이 누적된다.&lt;/p&gt; &lt;p&gt;반면 Fig. 9(c)에서 full fine-tuning의 similarity matrix는 6개 태스크를 거쳐도 &lt;strong&gt;대각선 구조가 유지&lt;/strong&gt;된다. Pre-trained structure를 보존하면서 적응하기 때문이다.&lt;/p&gt; &lt;h1 id=&quot;왜-intruder-dimensions이-발생하는가&quot;&gt;왜 Intruder Dimensions이 발생하는가&lt;/h1&gt; &lt;h2 id=&quot;1-low-rank-constraint&quot;&gt;1. Low-rank constraint&lt;/h2&gt; &lt;p&gt;LoRA의 update \(\Delta W = BA\)는 rank \(r\)로 제한된다. Pre-trained weight의 주요 방향과 잘 align되지 않는 경우, LoRA는 제한된 rank 안에서 task를 풀기 위해 &lt;strong&gt;기존에 없던 새로운 방향&lt;/strong&gt;을 만들어낸다. Rank가 충분히 높으면 (\(r \geq 2048\)) 이 문제가 완화되어 full fine-tuning과 유사해진다.&lt;/p&gt; &lt;h2 id=&quot;2-learning-rate와-gradient-projection&quot;&gt;2. Learning rate와 gradient projection&lt;/h2&gt; &lt;p align=&quot;center&quot;&gt;&lt;img src=&quot;/assets/post/image/2024-12-29-lora-vs-full-fine-tuning/image 9.png&quot; width=&quot;80%&quot; /&gt;&lt;/p&gt; &lt;p&gt;LoRA는 일반적으로 full fine-tuning보다 &lt;strong&gt;더 큰 learning rate&lt;/strong&gt;를 사용한다. 또한 gradient가 low-rank space로 projection되므로, 업데이트 방향이 full-rank gradient와 달라진다. 이 두 요소가 합쳐져 pre-trained structure에서 크게 벗어나는 업데이트가 발생한다.&lt;/p&gt; &lt;h2 id=&quot;3-product-parameterization-ba&quot;&gt;3. Product parameterization (\(BA\))&lt;/h2&gt; &lt;p&gt;두 행렬의 곱은 spectral differences를 증폭시킨다. \(B\)만 학습시키고 \(A\)를 고정하면 intruder dimensions이 줄어드는 것이 이를 뒷받침한다.&lt;/p&gt; &lt;h1 id=&quot;practical-implications&quot;&gt;Practical Implications&lt;/h1&gt; &lt;p&gt;이 논문의 발견은 LoRA를 실무에서 사용할 때 다음과 같은 시사점을 준다.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Rank 선택&lt;/strong&gt;: 가능하면 높은 rank를 사용하라. Low rank는 intruder dimensions을 유발하고 forgetting을 악화시킨다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;\(\alpha\) 설정&lt;/strong&gt;: \(\alpha = 2r\)이 \(\alpha = \text{constant}\)보다 intruder dimensions이 적고 generalization이 좋다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Continual learning 주의&lt;/strong&gt;: LoRA로 여러 태스크를 순차 학습하면 intruder dimensions이 누적되어 성능이 저하된다. 가능하면 adapter를 combine하지 말고 별도로 유지하라.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;모델 선택 기준&lt;/strong&gt;: 같은 test accuracy를 가진 두 모델 중, intruder dimensions이 적은 모델이 out-of-distribution에서 더 robust하다.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;Post-hoc mitigation&lt;/strong&gt;: 이미 학습된 LoRA 모델에서 intruder dimensions의 singular value를 줄이면 (\(\lambda \approx 0.3 \sim 0.7\)), 성능 저하 없이 forgetting을 크게 줄일 수 있다.&lt;/li&gt; &lt;/ol&gt; &lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt; &lt;p&gt;저자는 LoRA와 full fine-tuning이 같은 성능을 내더라도 weight matrix의 spectral properties에서 &lt;strong&gt;구조적으로 매우 다르다&lt;/strong&gt;는 것을 보여주었다. LoRA는 pre-trained singular vectors와 orthogonal한 intruder dimensions을 도입하며, 이것이 forgetting의 직접적 원인이 된다. Full fine-tuning은 pre-trained spectral structure를 유지하면서 효과적으로 적응한다.&lt;/p&gt; &lt;p&gt;이 발견은 “LoRA가 full fine-tuning과 동등하다”는 통념에 의문을 제기하며, &lt;strong&gt;성능 지표만으로는 두 방법의 차이를 포착할 수 없다&lt;/strong&gt;는 점을 강조한다. 특히 continual learning처럼 여러 task를 순차 학습하는 현실적 시나리오에서 intruder dimensions의 누적은 심각한 문제가 될 수 있다.&lt;/p&gt; </description> <pubDate>Sat, 28 Dec 2024 15:00:00 +0000</pubDate> <link>https://www.wonbeomjang.kr/blog/2024/lora-vs-full-fine-tuning/</link> <guid isPermaLink="true">https://www.wonbeomjang.kr/blog/2024/lora-vs-full-fine-tuning/</guid> <category>paper</category> <category>llm</category> <category>llm</category> </item> </channel> </rss>