Semua kode sejauh ini berjalan di CPU — dan jujur, mesin uji Course ini hanya berisi CPU (torch.cuda.is_available() mengembalikan False). Meski begitu, pola penulisan kodenya harus kita kuasai sejak sekarang, karena kode yang sama akan berjalan di GPU begitu perangkatnya tersedia: cukup satu variabel device yang dipilih di awal program. Chapter ini membahas pola device-agnostic tersebut, menjelaskan mengapa GPU unggul untuk matmul besar sementara CPU tetap menang di tempat lain, lalu membuktikan lewat benchmark nyata bahwa waktu komputasi di CPU tumbuh kubik terhadap ukuran matriks — inti alasan mengapa model besar membutuhkan GPU.
Satu Kode untuk Dua Perangkat #
Idiomnya berpusat pada satu baris: device = torch.device("cuda" if torch.cuda.is_available() else "cpu"). Tensor dan model kemudian dipindah dengan .to(device), dan hasil yang ingin diperiksa dikembalikan dengan .cpu(). Berkas pola_device.py menjalankan pola lengkap — forward pass kecil plus backward — dan keluarannya membuktikan bahwa di mesin ini seluruh jalur berjalan di CPU:
"""Pola device-agnostic: kode yang sama berjalan di CPU maupun GPU."""
import torch
print("CUDA tersedia :", torch.cuda.is_available())
print("jumlah GPU :", torch.cuda.device_count())
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print("device dipilih:", device)
torch.manual_seed(42)
X = torch.randn(4, 3)
w = torch.randn(3, 2, requires_grad=True)
X = X.to(device)
w = w.to(device)
print("X berada di", X.device, "| w berada di", w.device)
y = X @ w
loss = y.pow(2).sum()
loss.backward()
print("loss :", round(loss.item(), 4))
print("w.grad berada di", w.grad.device)
hasil = w.grad.cpu()
print("hasil dipindah ke", hasil.device)
print("norm w.grad :", round(hasil.norm().item(), 4))Output yang Dihasilkan:
CUDA tersedia : False
jumlah GPU : 0
device dipilih: cpu
X berada di cpu | w berada di cpu
loss : 34.8295
w.grad berada di cpu
hasil dipindah ke cpu
norm w.grad : 28.0875Mari kita telaah kode di atas baris per baris:
- Baris 8 (pemilihan device):
torch.cuda.is_available()mengembalikanFalsepada mesin ini sehinggadevicemenjadicpu; pada komputer dengan NVIDIA GPU dan wheel CUDA, baris yang sama memilihcudatanpa mengubah satu karakter lain di program. - Baris 15-16 (pemindahan):
.to(device)mengembalikan tensor di perangkat tujuan — tensor lama tidak berpindah, oleh karena itu hasilnya kita simpan kembali ke variabel yang sama. - Baris 19-23 (komputasi dan gradien):
loss.backward()bekerja normal; gradien ikut berada di device yang sama denganw. - Baris 25-27 (kembali ke CPU):
.cpu()memindahkan tensor ke RAM sebelum diproses lebih lanjut — pola wajib sebelum mencetak, menggambar grafik, atau menyimpan hasil, karena pustaka di luar torch (NumPy, matplotlib) hanya membaca data di CPU.
Mengapa GPU Lebih Cepat #
CPU memiliki belasan inti yang masing-masing cepat dan serbaguna; GPU memiliki ribuan inti kecil yang hanya pandai melakukan operasi sederhana secara serentak. Perkalian matriks adalah pekerjaan yang cocok untuk arsitektur kedua: setiap elemen hasil bisa dihitung tanpa menunggu elemen lain, sehingga ribuan inti GPU bekerja bersamaan dan juga menikmati bandwidth memori yang jauh lebih tinggi. Sebaliknya, biaya pemindahan data dari RAM ke memori GPU melalui jalur PCIe bisa menelan waktu — untuk tensor kecil atau operasi yang banyak cabangnya, CPU sering tetap lebih cepat. Tabel berikut menjadi panduan kasar:
| Perangkat | Tepat dipakai untuk | Kurang tepat untuk |
|---|---|---|
| CPU | preprocessing data, model kecil, logika Python kompleks, iterasi kecil | matmul besar berulang |
| GPU (satu) | pelatihan model menengah-besar, batch besar, conv/matmul dominan | transfer data kecil-selingan yang sering |
| Beberapa GPU | model sangat besar atau dataset raksasa (dibahas section distributed) | model kecil — biaya koordinasi lebih besar dari untungnya |
Benchmark di CPU #
Untuk merasakan skala waktunya, berkas benchmark_cpu.py mengukur matmul persegi pada empat ukuran di CPU mesin uji, masing-masing diulang tiga kali setelah satu pemanasan:
"""Benchmark kecil perkalian matriks pada CPU mesin uji Course."""
import time
import torch
torch.manual_seed(42)
hasil_cek = {}
print(f"{'ukuran n':>9} {'waktu (ms)':>11} {'GFLOP/s':>8}")
for n in (128, 256, 512, 1024):
A = torch.randn(n, n)
B = torch.randn(n, n)
C = A @ B
mulai = time.perf_counter()
for _ in range(3):
C = A @ B
waktu = (time.perf_counter() - mulai) / 3
flops = 2 * n ** 3
print(f"{n:>9} {waktu * 1000:>11.2f} {flops / waktu / 1e9:>8.1f}")
hasil_cek[n] = C[0, 0].item()
print()
print("deterministik dengan seed 42, nilai C[0, 0] tiap ukuran:")
for n, nilai in hasil_cek.items():
print(f" n={n:5d} -> {nilai:.4f}")Output yang Dihasilkan:
ukuran n waktu (ms) GFLOP/s
128 0.05 81.0
256 0.17 199.1
512 1.22 219.3
1024 7.76 276.9
deterministik dengan seed 42, nilai C[0, 0] tiap ukuran:
n= 128 -> -0.2671
n= 256 -> 23.6305
n= 512 -> 5.5166
n= 1024 -> 0.5228Mari kita telaah kode di atas baris per baris:
- Baris 14 (pemanasan): satu
A @ Bdi luar pengukuran menyiapkan alokasi memori dan thread BLAS; tanpa pemanasan, ukuran pertama terkontaminasi biaya sekali-jalan. - Baris 15-18 (pengukuran): tiga pengulangan dibagi rata-rata;
time.perf_counter()adalah pewaktu resolusi tinggi yang tepat untuk interval pendek. - Baris 19-20 (GFLOP/s): matmul
n×nmembutuhkan sekitar2n³operasi floating point; membaginya dengan waktu memberi laju komputasi — angka ini naik dengan ukuran karena kernel BLAS semakin efisien pada blok data besar. - Baris 9, 21, dan 24-26 (cek determinisme): nilai
C[0, 0]dicatat ke kamushasil_cekuntuk membuktikan isi hasil identik antar eksekusi berkat seed tetap — hanya waktu yang berubah.
Angka waktu pada keluaran di atas adalah hasil satu eksekusi di mesin uji dan akan berbeda di komputer Anda maupun pada eksekusi berikutnya, karena bergantung pada prosesor, jumlah thread, dan beban saat itu. Pola yang stabil adalah pertumbuhannya: menaikkan n dari 512 ke 1024 melipatgandakan pekerjaan 8 kali, dan waktu membesar dari 1,22 ms ke 7,76 ms — mendekati tumbuh kubik n³. Pada GPU modern, matmul 1024 yang sama diselesaikan dalam hitungan sub-milidetik, dan celahnya melebar lagi pada ukuran ribuan.
Panduan lengkap pemakaian CUDA di PyTorch — pemilihan device, to(), sinkronisasi — tersedia di docs.pytorch.org/docs/stable/notes/cuda.html
, sedangkan pilihan wheel pemasangan per perangkat dirangkum di pytorch.org/get-started/locally
. Cepat atau lambatnya komputasi, satu hal pasti: setiap tensor menempati memori. Cara menghitungnya dan kebiasaan menghematnya adalah materi penutup section ini.