Chapter Vectorization pada Section Operasi Matematika telah memperkenalkan pola-pola idiomatiknya; chapter ini membedah sisi yang belum kita ukur: performa. Kita akan membandingkan loop dan bentuk vektornya dengan tic/toc, memverifikasi hasilnya identik lewat isequal, lalu melihat pola mana yang paling berdampak. Cakupan materinya:
- Timing terukur: akumulator, filter
if, statistik per baris accumarrayuntuk agregasi per kelompok- Tabel konversi before/after untuk refactor cepat
- Kapan loop tidak bisa divektorkan, dan konteks jujur soal JIT
Akumulator: Loop vs sum #
Aturannya: loop yang hanya menjumlahkan (atau mengalikan) elemen satu per satu sebaiknya diganti sum (atau prod). Mekanismenya: sum menjumlahkan seluruh array dalam satu panggilan tingkat rendah yang dikompilasi, sementara loop pengguna membayar biaya interpreter pada tiap iterasi — penerjemahan baris, pengecekan batas, dan pengecekan tipe. Berikut pengukurannya pada 500000 elemen:
rng(42);
n = 500000;
v = rand(1, n);
tic;
sLoop = 0;
for k = 1:n
sLoop = sLoop + v(k);
end
tLoop = toc;
tic;
sVec = sum(v);
tVec = toc;
fprintf('akumulasi : loop %.4f s | sum %.6f s | speedup %.0fx\n', tLoop, tVec, tLoop / tVec);
fprintf('hasil identik: %d\n\n', isequal(sLoop, sVec));Output yang Dihasilkan:
akumulasi : loop 0.6367 s | sum 0.000648 s | speedup 983x
hasil identik: 1Mari kita bedah baris-baris kode di atas:
- Baris 2-3 (
n = 500000; v = rand(...)): menyiapkan data uji berukuran besar agar selisih waktu terukur, bukan sekadar noise. - Baris 5-9 (
for k = 1:n): akumulator klasik —sLoopdibaca dan ditulis ulang tiap iterasi. - Baris 12 (
sVec = sum(v);): penjumlahan seluruh array dalam satu panggilan fungsi. - Baris 15 (
fprintf(...)): rasiotLoop / tVecadalah speedup. - Baris 16 (
isequal(sLoop, sVec)): verifikasi bahwa kedua cara menghasilkan nilai yang persis sama — vectorization bukan pendekatan hampiran.
Filter: Mask Logical vs if dalam Loop #
Aturannya: seleksi “jika elemen memenuhi syarat, kumpulkan” diganti tiga langkah vektor: bangun mask (v > 0.5), pilih indeks (find) atau langsung pilih nilai (v(mask)). Mekanismenya: perbandingan mengevaluasi seluruh array sekaligus menghasilkan logical mask, lalu indeksasi logical menyaring elemen yang lolos tanpa percabangan per elemen. Agar perbandingannya adil, versi loop di bawah sudah memakai prealokasi — biaya pertumbuhan array tidak ikut terukur:
tic;
idx = zeros(1, n); % prealokasi agar biaya growth tidak ikut terukur
jml = 0;
for k = 1:n
if v(k) > 0.5
jml = jml + 1;
idx(jml) = k;
end
end
idx = idx(1:jml);
tIf = toc;
tic;
idxVec = find(v > 0.5);
tMask = toc;
fprintf('filter : loop-if %.4f s | mask %.6f s | speedup %.0fx\n', tIf, tMask, tIf / tMask);
fprintf('hasil identik: %d\n\n', isequal(idxVec, idx));Output yang Dihasilkan:
filter : loop-if 0.8494 s | mask 0.004036 s | speedup 210x
hasil identik: 1Pola ini bekerja untuk transformasi sekaligus seleksi: v(v > 0.5) * 2 menggandakan hanya elemen yang lolos dalam satu ekspresi. Bila yang dibutuhkan hanya jumlah elemen yang lolos, sum(v > 0.5) adalah idiom frekuensi paling ringkas.
Statistik per Baris: mean(A, 2) #
Aturannya: agregasi per baris atau per kolom matriks memakai argumen dimensi — mean(A, 2) untuk rata-rata tiap baris, mean(A, 1) untuk tiap kolom — bukan loop yang memanggil mean seribu kali. Mekanismenya: mean(A, 2) meruntuhkan dimensi kedua dalam satu panggilan; loop pengguna memanggil mean(A(i,:)) per baris sehingga biaya panggilan fungsi dikalikan jumlah baris:
A = rand(1000, 300);
tic;
rLoop = zeros(1000, 1);
for i = 1:1000
rLoop(i) = mean(A(i, :));
end
tBaris = toc;
tic;
rVec = mean(A, 2);
tKolom = toc;
fprintf('mean baris: loop %.4f s | mean(A,2) %.6f s | speedup %.0fx\n', ...
tBaris, tKolom, tBaris / tKolom);
fprintf('selisih maksimum: %.2e\n\n', max(abs(rLoop - rVec)));Output yang Dihasilkan:
mean baris: loop 0.0492 s | mean(A,2) 0.000356 s | speedup 138x
selisih maksimum: 0.00e+00Pola dimensi ini berlaku seragam untuk sum, max, min, cumsum, dan std — pola yang telah kita pakai di Section Pengolahan Data untuk skor per observasi.
accumarray: Agregasi per Kelompok #
Aturannya: ringkasan per kelompok (total penjualan per pelanggan, jumlah kejadian per kategori) memakai accumarray — satu panggilan menggantikan loop dengan kondisi per kelompok. Mekanismenya: accumarray(sub, val, [jumlahKelompok 1]) menjumlahkan val ke keranjang yang ditunjuk sub; kita tidak perlu membangun if per kelompok secara manual:
pelanggan = [1 3 2 1 3 1 2];
tagihan = [50 30 20 70 60 10 40];
total = accumarray(pelanggan', tagihan', [3 1]);
totalLoop = zeros(3, 1);
for p = 1:3
totalLoop(p) = sum(tagihan(pelanggan == p));
end
fprintf('accumarray: total per pelanggan = %d %d %d\n', total);
fprintf('versi loop : total per pelanggan = %d %d %d\n', totalLoop);
fprintf('hasil identik: %d\n\n', isequal(total, totalLoop));Output yang Dihasilkan:
accumarray: total per pelanggan = 130 60 90
versi loop : total per pelanggan = 130 60 90
hasil identik: 1Pelanggan 1 menagih 50 + 70 + 10 = 130, dan kedua versi sepakat. Argumen ketiga [3 1] menentukan jumlah keranjang keluaran; tanpa argumen tersebut, accumarray menentukan jumlah keranjang dari nilai sub terbesar.
Tabel Konversi Before/After #
| Pola loop (before) | Bentuk vector (after) | Fungsi kunci |
|---|---|---|
s = 0; for k, s = s + v(k) | s = sum(v) | sum |
for k, if v(k) > t, kumpulkan | find(v > t) / v(v > t) | logical indexing |
for i, r(i) = mean(A(i,:)) | r = mean(A, 2) | sum, mean, dimensi |
for k, s(k) = s(k-1) + v(k) | s = cumsum(v) | cumsum |
for p, for k, if kelompok... | accumarray(sub, val) | accumarray |
for i, for j, Z(i,j) = f(...) | Z = f(X, Y) lewat meshgrid | meshgrid |
Kapan Loop Tidak Bisa Divektorkan #
Vectorization berhenti ketika iterasi saling bergantung: hasil iterasi k dibutuhkan untuk menghitung iterasi k+1, dan hubungan tersebut bukan akumulasi standar. Contoh klasiknya adalah Fibonacci — setiap suku adalah jumlah dua suku sebelumnya:
a = 0;
b = 1;
fib = zeros(1, 10);
for k = 1:10
fib(k) = b;
baru = a + b; % iterasi berikutnya memakai hasil iterasi ini
a = b;
b = baru;
end
fprintf('fibonacci (10 suku): %d %d %d %d %d %d %d %d %d %d\n', fib);Output yang Dihasilkan:
fibonacci (10 suku): 1 1 2 3 5 8 13 21 34 55Keluarga kasus lain yang menolak vectorization: simulasi dinamis yang statusnya berubah tiap langkah waktu, dan algoritme dengan early exit. Untuk kasus-kasus ini, loop eksplisit adalah pilihan yang jujur dan benar — dipercepat dengan preallocation (chapter berikutnya), bukan dipaksakan menjadi array.
Ringkasan #
Pengukuran pada 500000 elemen menegaskan pola dari chapter sebelumnya: sum mengalahkan akumulator loop, logical indexing mengalahkan if per elemen, dan mean(A, 2) mengalahkan loop per baris — semuanya dengan hasil identik yang diverifikasi isequal. accumarray melengkapi keluarga ini untuk agregasi per kelompok. Batasnya juga jelas: iterasi yang saling bergantung seperti Fibonacci tetap milik loop. Satu sumber perlambatan yang kerap menempel pada loop yang tersisa — array yang tumbuh di dalamnya — menjadi topik chapter berikutnya: preallocation.