Profiling artinya mengukur ke mana waktu dan byte program kamu pergi, sebelum mengubah apa pun. Bayangin Bu Sri ingin tahu kenapa sarapan di Homestay Kabut sering telat. Dia gak langsung beli kompor baru. Dia memegang stopwatch dan mencatat total waktu tiap pagi, lalu satu pagi dia mengikuti juru masak dan mencatat berapa lama tiap pekerjaan, lalu membuka gudang untuk melihat apa yang paling makan tempat. Toples tanpa label cuma bisa dicatat sebagai "toples nomor 0". Stopwatch itu benchmark, mengikuti juru masak itu profiler, membuka gudang itu analisis ukuran binary, dan label toplesnya adalah name section di file .wasm.
🧪 Tebakan cepat:
olahfoto.wasmdi-build ulang denganopt-level = "z"(fokus ke ukuran) sebagai gantiopt-level = 3. File-nya turun dari 4.177 jadi 2.637 byte. Blur σ 2 di foto 1200 × 800 jadi lebih cepat, sama saja, atau lebih lambat? Tebak dulu, jawabannya ada di bagian "Ukuran lawan kecepatan".
Ukur dulu, dengan jujur
Aturannya sudah kamu kenal dari pelajaran "Wasm vs JavaScript": performance.now(), banyak putaran, buang putaran pertama (pemanasan), ambil median, dan cek hasilnya sama.
function ukur(fn, putaran = 10, pemanasan = true) {
const waktu = [];
for (let i = 0; i < putaran; i++) {
const mulai = performance.now();
fn();
waktu.push(performance.now() - mulai);
}
const dipakai = (pemanasan ? waktu.slice(1) : waktu).sort((a, b) => a - b);
return { pertama: waktu[0], median: dipakai[Math.floor(dipakai.length / 2)] };
}
- Putaran pertama ikut membayar compile dan pemanasan engine, jadi dicatat terpisah.
- Median lebih tahan gangguan daripada rata-rata. Laptop yang sibuk bikin satu-dua putaran melonjak.
- Ukur jalur lengkapnya: menyalin foto masuk dan keluar memori Wasm (0,6 ms) ikut dibayar tiap kali.
Diukur di Chromium 147, MacBook M1 Pro yang sedang sibuk, median setelah pemanasan (11 putaran, build debug 4 putaran):
| Pekerjaan di foto 1200 × 800 | JavaScript | Wasm (release) | Wasm (build debug) |
|---|---|---|---|
| Grayscale | 1,98 ms | 0,90 ms | 8,6 ms |
| Blur Gaussian σ 2 | 155 ms | 49 ms | 646 ms |
Build debug (cargo build tanpa --release, opt-level = 0) 13 kali lebih lambat dari release, dan 4 kali lebih lambat dari JavaScript. Jangan pernah menyimpulkan "Wasm lambat" dari build debug.
Chrome DevTools: Performance dan nama fungsi
Benchmark bilang berapa lama. Profiler bilang di mana. Caranya: buka DevTools (F12), tab Performance, klik Record, jalankan blur-nya, klik Stop. Di tab Bottom-Up, urutkan berdasarkan Self Time: fungsi yang paling lama bekerja ada di atas. Fungsi Wasm muncul di sana bersama fungsi JavaScript.
Nama yang muncul tergantung isi file .wasm-nya. Ini hasil profiler yang sama dengan panel Performance (Chromium 147, 3 kali grayscale dan blur):
Build olahfoto.wasm |
Baris teratas di profiler | Self time |
|---|---|---|
strip = true (4.177 byte) |
wasm-function[0] di wasm://wasm/8de44fbe |
177,6 ms |
| bawaan Cargo, name section ikut (4.761 byte) | _RNvCsaWdBoUbM9cW_9olah_foto14blur_satu_arah di wasm://wasm/olah_foto.wasm-c7e9edd6 |
177,4 ms |
- Name section adalah custom section berisi nama fungsi. Tanpa itu, engine cuma tahu nomor urutnya:
wasm-function[0]. - Nama Rust ditulis dalam bentuk mangled (
_RNv...), tapiolah_fotodanblur_satu_arahterbaca di dalamnya. - Di profil yang sama juga muncul baris
js-to-wasm(0,8 ms): itu ongkos menyeberang dari JS ke Wasm. - Stack trace error memakai nama yang sama:
at olah_foto.wasm.blur_h (wasm://wasm/olah_foto.wasm-c7e9edd6:wasm-function[3]:0x3be). Tanpa name section:at wasm://wasm/8de44fbe:wasm-function[3]:0x3be.
Untuk debugging sampai ke baris kode sumber, butuh DWARF (info debug lengkap): debug = true di Cargo (olahfoto jadi 38.183 byte) atau -g di Emscripten, ditambah ekstensi Chrome C/C++ DevTools Support (DWARF). Dengan itu kamu bisa pasang breakpoint di baris C/C++, melihat isi variabel, dan nama fungsi di Call Stack. Kirim build tanpa info debug ke pengguna, simpan build dengan nama untuk profiling.
Apa yang besar di binary-ku
wasm-tools objdump (wasm-tools 1.261.0) memecah file per section, dalam byte:
$ wasm-tools objdump olahfoto.wasm
types | 0xa - 0x26 | 28 bytes | 4 count
functions | 0x28 - 0x34 | 12 bytes | 11 count
memories | 0x36 - 0x39 | 3 bytes | 1 count
exports | 0x3b - 0xa5 | 106 bytes | 8 count
code | 0xa8 - 0x1051 | 4009 bytes | 11 count
custom "name" | 0x1059 - 0x11aa | 337 bytes | 1 count
custom "producers" | 0x11b6 - 0x11f9 | 67 bytes | 1 count
custom "target_features" | 0x120c - 0x1299 | 141 bytes | 1 count
Lalu wasm-objdump -x -j Code olahfoto.wasm (wabt 1.0.42) memberi ukuran tiap fungsi, dengan nama dari name section: resize_bilinear 930 byte, grayscale_simd 861, resize_area 815, blur_satu_arah 757, resize_nearest 291, grayscale 272.
Di modul yang lebih besar, yang makan tempat sering bukan kodemu. Versi komponen olahfoto di pelajaran berikutnya memakai Vec, String, dan format! dari std. Modul intinya 21.003 byte, dan 5.204 byte di antaranya cuma satu fungsi: dlmalloc::malloc, allocator bawaan std. Mesin format Rust (core::fmt) juga ikut, 17 fungsi dengan total 4.345 byte, karena format! dan pesan panic. Tool khusus untuk ini, twiggy (cargo install twiggy, lalu twiggy top dan twiggy dominators), masih bisa dipasang (versi 0.8.0), tapi repo-nya diarsipkan sejak 14 Maret 2026. wasm-objdump dan wasm-tools masih dirawat.
Ukuran lawan kecepatan: opt-level, LTO, wasm-opt
[profile.release]
opt-level = 3 # 3 = paling cepat; "s" dan "z" = fokus ukuran
lto = true # optimasi lintas crate saat linking
codegen-units = 1 # satu unit: optimasi lebih menyeluruh, compile lebih lama
panic = "abort" # tanpa mesin unwind
strip = true # buang name section dan info debug
Setelah itu, wasm-opt (dari Binaryen, versi 133) mengoptimasi file .wasm-nya sendiri. -O1 sampai -O4 fokus ke kecepatan (makin tinggi makin lama prosesnya), -Os dan -Oz fokus ke ukuran:
wasm-opt -O3 --enable-simd --enable-bulk-memory --enable-sign-ext \
--enable-nontrapping-float-to-int --enable-mutable-globals \
--enable-multivalue --enable-reference-types \
olahfoto.wasm -o olahfoto.opt.wasm
Semua build ini dari kode Rust yang sama, hasilnya sama byte demi byte, diukur bergantian (median, Chromium 147 dan WebKit 26.4):
| Build | Ukuran | Blur σ 2 Chromium | WebKit |
|---|---|---|---|
opt-level = 3, LTO, codegen-units = 1, strip |
4.177 byte | 49 ms | 50 ms |
yang sama + wasm-opt -O3 |
3.683 byte | 49 ms | 49 ms |
opt-level = "s" |
2.504 byte | 95 ms | 173 ms |
opt-level = "z" |
2.637 byte | 131 ms | 152 ms |
"z" + wasm-opt -Oz |
2.284 byte | 126 ms | |
tanpa LTO, codegen-units = 16 |
7.453 byte | 47 ms | 48 ms |
build debug + wasm-opt -O3 |
15.548 byte | 711 ms |
Jadi jawaban tebakannya: lebih lambat, 49 ms jadi 131 ms, hampir 3 kali. Profiler menunjukkan sebabnya: di build "z", fungsi Ord::clamp muncul sendiri dengan self time 37 ms. Demi hemat byte, "z" gak menyisipkan (inline) fungsi kecil itu dan mematikan vektorisasi loop, jadi tiap titik blur memanggil fungsi. Grayscale tetap 0,9 ms di semua build release.
- Release, wasm-opt, strip: tiga langkah yang paling efektif mengecilkan
.wasm. Di sini wasm-opt memangkas 12% tanpa mengubah kecepatan, karena LLVM sudah mengoptimasi. - wasm-opt gak bisa menyelamatkan build debug: tetap 711 ms. Optimasi yang hilang di compiler gak bisa dikembalikan belakangan.
wasm-pack buildmenjalankan wasm-opt otomatis.- Gzip atau brotli di server mengecilkan unduhan, tapi browser tetap meng-compile file utuhnya. Itu bahasan pelajaran "Wasm di Production".
💡 Kapan JavaScript sudah cukup
- Grayscale: JS 1,98 ms, Wasm 0,90 ms ditambah 0,6 ms menyalin. Hematnya sekitar 0,5 ms per foto, sekali tiap unggah. Gak sepadan dengan toolchain Rust kalau cuma itu.
- Anggarannya: 16,7 ms per frame di layar 60 Hz, dan 50 ms sebelum sebuah tugas dihitung long task yang bikin halaman terasa macet.
- Blur 155 ms jadi 49 ms: ini baru sepadan, apalagi dipindah ke Worker.
- Profil dulu. Kalau yang lambat ternyata DOM, jaringan, atau render, Wasm gak membantu.
⚠️ Jebakan yang sering ketemu
- Mengukur build debug. Blur 646 ms lawan 49 ms. Selalu
--release. - Memakai
opt-level = "z"tanpa mengukur. Hemat 1,5 KB, blur hampir 3 kali lebih lambat. - Profil tanpa name section. Yang terlihat cuma
wasm-function[0]. Buat build profiling dengan nama. - wasm-opt tanpa flag fitur. Modul yang memakai SIMD ditolak:
[wasm-validator error in function 6] unexpected false: SIMD operations require SIMD [--enable-simd]. - Lupa ongkos menyeberang dan menyalin. Ukur dari JS memanggil sampai hasilnya kembali di JS.
TL;DR: Ukur dulu sebelum mengoptimasi: performance.now(), banyak putaran, buang pemanasan, median, hasil dicek sama, dan selalu build release (build debug: blur 646 ms lawan 49 ms). Tab Performance di Chrome DevTools menampilkan fungsi Wasm dengan nama aslinya kalau file-nya punya name section atau DWARF; tanpa itu cuma wasm-function[0]. DWARF plus ekstensi "C/C++ DevTools Support (DWARF)" memberi breakpoint di kode sumber. wasm-tools objdump dan wasm-objdump -x menunjukkan section dan fungsi yang paling besar; twiggy masih bisa dipasang tapi diarsipkan. Untuk mengecilkan: release, wasm-opt, strip (4.761 jadi 3.683 byte tanpa kehilangan kecepatan). opt-level = "z" memang lebih kecil (2.637 byte) tapi blur jadi 131 ms karena fungsi kecil gak di-inline. Kalau JavaScript sudah di bawah anggaran (16,7 ms per frame, 50 ms per tugas), Wasm belum perlu.