- GPU memiliki kecepatan komputasi yang jauh lebih tinggi daripada kecepatan akses memori, sehingga hierarki memori menjadi bottleneck kinerja
- Bergantung pada intensitas aritmetika (Arithmetic Intensity, AI), komputasi dibagi menjadi kondisi memory-bound dan compute-bound; titik ambang GPU A100 adalah sekitar 13 FLOPs/Byte
- Strategi utama untuk optimasi kinerja meliputi fusion dan tiling; Fusion mengurangi bolak-balik memori yang tidak perlu, sedangkan Tiling memaksimalkan penggunaan ulang data
- Memahami karakteristik struktural perangkat keras GPU seperti sinkronisasi, coalesced load, dan penyelesaian konflik bank penting untuk menulis kernel berkinerja tinggi
- Pertimbangan tambahan seperti occupancy, meminimalkan percabangan thread, dan quantization juga sangat memengaruhi kinerja nyata
Struktur Hierarki Komputasi dan Memori GPU
- GPU pada umumnya memiliki kecepatan pemrosesan operasi aritmetika yang jauh lebih tinggi dibanding bandwidth memori
- Sebagai contoh, NVIDIA A100 menawarkan performa sekitar 19.5 TFLOPS (floating point 32-bit), sedangkan bandwidth memorinya berada di kisaran 1.5TB/s
- Karena puluhan operasi dapat diproses selama membaca data 4 byte, perpindahan data menjadi bottleneck kinerja
- Memori global (VRAM) adalah memori off-chip yang lambat tempat semua data berada, sedangkan Streaming Multiprocessor(SM) bertugas menangani komputasi
- Setiap SM memiliki Shared Memory(SRAM) on-chip berkecepatan tinggi, yang bisa dimanfaatkan sebagai cache yang dikelola langsung oleh program
- Thread adalah unit eksekusi terkecil, dan setiap thread memiliki kumpulan register individual
- 32 thread membentuk sebuah Warp, dan Block adalah grid thread yang dijalankan pada SM yang sama
Wilayah Kinerja: Memory-Bound vs Compute-Bound
- Kinerja sebuah kernel dibatasi oleh salah satu dari dua hal: memory-bound (dibatasi oleh kecepatan perpindahan data) atau compute-bound (dibatasi oleh kemampuan komputasi SM)
- Intensitas aritmetika (AI) didefinisikan sebagai Total FLOPs / Total Bytes Accessed, dan nilai ini menjadi metrik penting
- Model Roofline: grafik dengan AI pada sumbu x dan FLOPS/s pada sumbu y yang menunjukkan kinerja aktual sebuah kernel
- Jika AI rendah sehingga memory-bound, posisinya berada pada garis diagonal (batas bandwidth memori)
- Jika AI tinggi sehingga compute-bound, posisinya berada pada garis horizontal (batas performa komputasi maksimum)
- Ridge Point A100 adalah 19.5 TFLOPS / 1.5 TB/s ≈ 13 FLOPs/Byte
- Meningkatkan AI akan memperbaiki kinerja dan dapat membawa kernel mencapai kondisi compute-bound
Strategi untuk Meningkatkan Intensitas Aritmetika
- Model sederhana: 1 thread menghitung 1 buah C[i,j] → AI = 0.25 (sangat rendah, memory-bound)
- Bahkan saat thread menghitung tile 2x2, AI = 0.5 (masih rendah)
- Untuk menaikkan AI, banyak thread harus memuat tile besar ke Shared Memory pada level block agar penggunaan ulang data dapat dimaksimalkan
- Melalui kerja sama thread dalam Block, AI dapat ditingkatkan menjadi > 13 sehingga bisa masuk ke wilayah compute-bound
Kondisi Overhead-Bound
- Overhead dapat muncul saat CPU (host) menugaskan pekerjaan ke GPU
- Jika kernel GPU terlalu kecil atau terlalu banyak, GPU bisa berakhir menunggu pekerjaan berikutnya
- Framework modern memperkenalkan eksekusi asinkron untuk meminimalkan overhead dengan mengantrekan command stream lebih awal
Dua Strategi Inti untuk Meningkatkan Kinerja: Fusion dan Tiling
Operator Fusion
- Pada rangkaian operasi sederhana, misalnya
y = relu(x + 1), jika tiap operasi berjalan sebagai kernel terpisah maka data akan bolak-balik ke memori global
- Fusion menggabungkan beberapa operasi menjadi satu kernel sehingga nilai antara tidak disimpan ke memori global, melainkan diproses di register dan hanya hasil akhirnya yang ditulis
- Contoh: kompiler JIT seperti Triton dan torch.compile Inductor mengotomatisasi proses ini
Tiling
- Pada operasi yang lebih kompleks seperti perkalian matriks, model thread tunggal menghasilkan AI yang rendah
- Dengan membagi tile per block, semua thread di dalam block bekerja sama memuat tile data ke Shared Memory untuk mewujudkan penggunaan ulang data dalam skala besar
- Komputasi mengikuti pola 3 tahap: "Load(global -> Shared Memory) - Synchronize(sinkronisasi) - Compute(komputasi)"
Coalesced Load dan Vektorisasi
- Saat memindahkan data dari memori global ke Shared Memory, coalesced access (32 thread dalam satu warp mengakses rentang 128 byte yang berurutan) sangat penting
- Dengan vektorisasi (misalnya
float4) untuk memuat beberapa data sekaligus, resource perangkat keras dapat dihemat dan bandwidth memori dapat dimanfaatkan secara maksimal
- Penyelarasan data (alignment) wajib dipenuhi, dan nilai K dalam jumlah byte matriks harus merupakan kelipatan 4 agar efisien
Bank Shared Memory dan Konflik Bank
- Shared Memory terdiri dari 32 bank independen, sehingga 32 thread dalam satu warp harus mengakses bank yang berbeda agar berjalan tanpa konflik
- Akses per baris tidak menimbulkan konflik, sedangkan akses per kolom menyebabkan konflik (mengakses bank yang sama)
- Tile B disimpan secara transpose di Shared Memory dengan strategi "load and transpose", sehingga saat komputasi dapat berfokus pada akses per baris dan menghindari konflik bank
Pola Komputasi On-Chip Berkecepatan Tinggi
Strategi Dasar 1: Satu thread menghitung satu output
- Dengan batasan BLOCK_DIM=32, AI maksimum hanya 8 sehingga tidak bisa mencapai kondisi compute-bound
Strategi 2: Satu thread menghitung beberapa output
- Jika disetel BLOCK_DIM=16 dan TILE_DIM=64, satu thread menghitung output 4x4 → AI=16
- Karena AI>13, berdasarkan standar A100 dapat mencapai kinerja compute-bound
- Komputasi yang efisien dimungkinkan lewat load tervektorisasi seperti
float4 dari Shared Memory
Batas Nyata Tiling: Kuantisasi Tile
- Jika ukuran matriks bukan kelipatan ukuran tile, block di tepi akan menghitung area yang lebih besar dari kebutuhan sebenarnya (komputasi tidak perlu) dan diperlakukan dengan padding
- Thread di tepi mencegah akses memori yang tidak perlu dengan kondisi guard, tetapi loop komputasi tetap berjalan sama sehingga menghasilkan komputasi sampah (misalnya
C += A * 0)
Faktor Tambahan untuk Tuning Kinerja
Occupancy dan Menyembunyikan Latensi
- Saat sebuah warp menunggu lama, misalnya karena pembacaan memori, SM dapat langsung beralih ke warp lain untuk mengurangi waktu menganggur (latency hiding)
- Dengan menugaskan beberapa Thread Block secara bersamaan, waktu tunggu dapat diminimalkan melalui occupancy yang tinggi
- Jika ukuran block atau tile terlalu besar, jumlah resident block akan berkurang dan occupancy turun sehingga kinerja menurun
Meminimalkan Percabangan Thread
- Jika terjadi percabangan
if-else di dalam warp, kedua jalur harus dieksekusi secara berurutan sehingga kinerja efektif bisa turun hingga sekitar setengah
- Perlu meminimalkan percabangan dengan kode branchless seperti
min, max, dan sejenisnya
Quantization
- Jika presisi diturunkan dari FP32 ke FP16/BFP16, jumlah perpindahan memori dan jumlah data yang dapat diproses masing-masing meningkat 2x
- Pada A100, komputasi FP16 dapat mencapai 312 TFLOPS (hingga 16x performa dibanding 19.5 TFLOPS FP32)
- Dengan quantization, posisi pada Roofline dapat sekaligus bergeser ke kanan (efisiensi memori) dan ke atas (performa komputasi maksimum)
Ringkasan Keseluruhan
- Batas mendasar kinerja GPU berasal dari ketidakseimbangan antara bandwidth memori dan kemampuan komputasi on-chip
- Peningkatan kinerja dicapai melalui memaksimalkan penggunaan ulang data (tiling) dan meminimalkan traffic memori antara (fusion)
- Karakteristik struktur perangkat keras seperti warp, bank, coalesced access, dan sinkronisasi harus dipahami agar penulisan dan optimasi kernel berkinerja tinggi memungkinkan
- Dalam praktik, faktor tambahan seperti occupancy, meminimalkan percabangan, dan quantization secara langsung memengaruhi kecepatan nyata
- Perancangan komputasi GPU berkinerja tinggi memerlukan pertimbangan gabungan, mulai dari peningkatan AI secara teoretis, pemanfaatan karakteristik hardware, hingga penanganan tata letak dan ukuran data nyata
1 komentar
Komentar Hacker News
Penasaran seberapa baik optimisasi seluruh program dilakukan di level compiler; pendekatan saat ini yang mengoptimalkan tiap arsitektur LLM satu per satu terasa agak tertinggal
Berbagi pengalaman mencoba menjalankan llama.cpp dan vllm pada 4070 yang sama untuk memproses lebih banyak prompt secara batch; mulai batch 8 llama.cpp menjadi sangat lambat, dan walau utilisasi GPU terlihat baik, sebenarnya terjadi bottleneck; vllm terasa menanganinya jauh lebih baik
vllm menggunakan paged KV cache dan layout fully coalesced yang disukai GPU sehingga memberi performa yang dioptimalkan untuk batch, sedangkan llama.cpp memakai layout flat yang bagus untuk prompt tunggal sehingga pada situasi batch pola akses memori L2 rusak dan kecepatan turun
Berbagi pengalaman bahwa dengan meng-interleave tensor KV di llama.cpp dari [seq, head, dim] ke bentuk [head, seq, dim], sehingga mengikuti cara vllm memasok data ke fused attention kernel, performa komputasi langsung meningkat sekitar 2x
Penyebab bottleneck bukan GPU itu sendiri, melainkan cara akses shared memory dan bagaimana global read dirancang; vllm menyerang titik itu tepat lewat perubahan layout
Analisis bottleneck seperti ini memakan waktu lebih dari 2 hari, tidak bisa diketahui hanya dari grafik utilisasi GPU, dan kebanyakan dipahami lewat trial and error
Mengajukan pertanyaan apakah ada cara untuk mengulang eksperimen seperti ini dengan lebih mudah, dalam gaya hot reload
Menunjukkan bahwa walau disebut GPU bukan bottleneck, pada praktiknya inefisiensi layout memori pada akhirnya tetap menjadi bottleneck yang menurunkan efisiensi komputasi GPU
Menyebut proyek nano-vllm yang dirilis kemarin oleh karyawan deepseek; hanya sekitar 1200 baris tetapi dikabarkan mencatat performa lebih cepat daripada vanilla vllm https://github.com/GeeeekExplorer/nano-vllm
Bertanya apakah layout yang diubah di llama.cpp sudah diajukan sebagai pull request; peningkatan 2x bisa menjadi keuntungan besar bagi semua orang
Merekomendasikan juga untuk mencoba proyek bernama ik_llama.cpp https://github.com/ikawrakow/ik_llama.cpp
Menyampaikan pendapat bahwa ini artikel berisi informasi bagus, dan isinya lebih merupakan cerita tentang faktor-faktor yang dipilih NVIDIA saat mengembangkan arsitektur GPU; menekankan agar perbedaannya dengan vendor lain tidak disalahpahami
Misalnya AMD Instinct MI300 memiliki hingga 160 TFLOPS pada FP32 dan bandwidth HBM3/3E 6TB/s sehingga ridge-point berubah; ini berarti 27 FLOPs/byte, dua kali A100 yang 13 FLOPs/byte, dan memori HBM besar (128~256GB) juga mengubah trade-off realistis antara tiling depth dan occupancy; tetapi GPU seperti ini mahal dan ada trade-off berupa tidak mendukung CUDA
Berpendapat bahwa sampai AMD lebih serius menangani software komputasi, hanya GPU NVIDIA yang akan tetap punya daya saing nyata
Sebagai spoiler, ditekankan bahwa yang sebenarnya penting bukan cara kerja GPU itu sendiri, melainkan bagaimana memanfaatkannya untuk komputasi machine learning
Ada pendapat bahwa penggunaan warna kontras itu wajib, demi keterbacaan
Berbagi pengalaman dengan
font-weight: 300; kebanyakan desainer Mac mengembangkan sambil menyesuaikan opsi font smoothing sehingga biasanya disetel agar terlihat seperti "normal", tetapi Mac membuat font tipis terlihat setengah lebih tebal, jadi desainer cenderung memakai font yang lebih tipis untuk memberi kesan "biasa"; membagikan tautan terkait https://news.ycombinator.com/item?id=23553486Berspekulasi bahwa penulis mungkin sedang mengedit dan memformat dalam dark mode; menyebut bahwa jika
edge://flags/#enable-force-darkditerapkan, tautannya terlihat jelasMenunjukkan bahwa komentar di dalam tautan dan code block milik penulis terasa sangat sulit dibaca dan butuh usaha ekstra; menyarankan peningkatan kontras, sambil menilai kualitas kontennya sendiri sangat bagus
Mengkritik bahwa situs web itu membuat kesalahan besar dengan memakai transparansi alfa pada teks sehingga kontras turun drastis
Mengusulkan bahwa judul yang lebih tepat sebenarnya lebih dekat ke fakta-fakta dasar tentang GPU Nvidia; istilah WARP juga merupakan ciri GPU Nvidia modern, sementara GPU Nvidia sekitar tahun 2003 adalah perangkat keras yang hanya untuk rendering video game, sehingga sangat berbeda dari GPU komputasi umum masa kini; ringkasnya, isi postingan itu bukan penjelasan umum yang berlaku untuk semua GPU
Mengucapkan terima kasih karena ini benar-benar materi pengantar yang sangat bagus; saat merakit AI PC sendiri sempat meneliti GPU berhari-hari, dan tulisan ini sangat membantu karena merangkum hal-hal inti yang wajib diketahui serta area aplikasi bernilai tinggi seperti AI generatif; terutama diagram hierarki memori pada GPU A100 dinilai sangat berguna
Bingung dengan penggunaan diagram ASCII