Topik ini membahas disiplin pengukuran kinerja yang menjadi syarat sebelum satu pass optimasi dinyatakan bermanfaat. Pembaca memperoleh berkas bench.py yang berisi pemasa virtual dengan antarmuka time.perf_counter, fungsi time_median yang menjalankan sebuah fungsi N kali dan melaporkan median, serta runner yang membandingkan dua versi fungsi penjumlahan, yaitu naif dan dioptimasi. Setelah membaca topik ini, pembaca dapat menjelaskan mengapa median dipilih daripada rerata, mengapa kesetaraan kedua versi diverifikasi sebelum timing dibandingkan, bagaimana rasio percepatan dilaporkan sebagai hasil benchmark, dan mengapa signifikansi serta blind A/B menjadi disiplin yang melengkapi benchmark sebelum versus sesudah.
Disiplin benchmark sebelum versus sesudah#
Setiap pass optimasi yang ditambahkan ke compiler harus dibuktikan memberi dampak. Tanpa pengukuran, klaim bahwa sebuah pass lebih cepat hanyalah dugaan, sebab beberapa pass justru memperlambat kode akibat overhead analisis yang lebih besar daripada penghematan instruksi. Disiplin yang dipakai pada topik ini adalah benchmark sebelum versus sesudah, yaitu menjalankan fungsi target dengan kedua versi, mencatat waktu kedua versi, lalu melaporkan rasio.
Pembandingan langsung sebelum versus sesudah hanya bermakna bila kedua versi fungsi yang dibandingkan menghasilkan nilai yang sama pada masukan yang sama. Verifikasi kesetaraan diletakkan paling awal pada runner, sebelum timing dilakukan. Bila verifikasi gagal, benchmark dihentikan, sebab membandingkan waktu dua fungsi yang menghasilkan nilai berbeda tidak ada artinya. Pada berkas bench.py, verifikasi dilakukan dengan assert yang membandingkan keluaran sum_naive dan sum_optimized terhadap rumus Gauss pada n = 1000, sehingga kedua fungsi dinyatakan setara sebelum timing dilakukan. Pada compiler nyata, kedua versi yang dibandingkan sering kali adalah versi tanpa pass baru sebagai baseline dan versi dengan pass baru sebagai kandidat, sehingga rasio yang dilaporkan menyatakan dampak pass kandidat terhadap baseline.
Median bukan rerata#
Hasil benchmark berupa deret sampel waktu, satu sampel per pemanggilan. Deret ini biasanya memiliki satu-dua pencilan akibat penjadwalan OS, interupsi, atau efek cache. Rerata sensitif terhadap pencilan sebab pencilan menarik nilai rerata menjauhi mayoritas sampel. Median, sebaliknya, hanya melihat nilai tengah setelah pengurutan, sehingga satu-dua pencilan tidak menggeser median secara signifikan.
Pada berkas bench.py, fungsi time_median mengumpulkan N sampel pemasaan, lalu melaporkan statistics.median dari sampel tersebut. Pemasa di-reset sebelum setiap pemanggilan agar setiap sampel bersifat mandiri. Pada benchmark nyata, pemasa adalah time.perf_counter bawaan Python, yang memiliki resolusi cukup untuk mengukur perbedaan kecil. Pada topik ini, pemasa virtual dipakai sebagai pengganti agar keluaran benchmark dapat direproduksi di sandbox, sebab waktu dinding bervariasi antar mesin dan antar eksekusi. Pemilihan jumlah sampel N juga menjadi pertimbangan: N yang terlalu kecil menghasilkan median yang tidak stabil, sedangkan N yang terlalu besar memperlambat benchmark tanpa menambah kepastian. Pada bench.py, N dipilih 100, yang cukup untuk menjaga median stabil sekaligus menjaga benchmark cepat selesai.
Pemasa virtual sebagai pengganti time.perf_counter#
Pemasa virtual pada bench.py memperlihatkan antarmuka yang sama dengan time.perf_counter, yaitu metode perf_counter() yang mengembalikan nilai pemasa saat ini. Perbedaannya, nilai pemasa virtual dimajukan oleh kode yang diukur melalui tick(), bukan oleh OS scheduler. Dengan demikian, keluaran benchmark menjadi deterministik, sebab nilai pemasa hanya bergantung pada berapa kali tick() dipanggil.
Pada benchmark nyata, pemasa virtual diganti dengan time.perf_counter dan panggilan tick() dihapus dari kode yang diukur. Sisanya, struktur kode tetap sama, yaitu reset pemasa sebelum setiap pemanggilan, jalankan fungsi, catat nilai pemasa sebagai satu sampel, dan ambil median dari N sampel. Pendekatan ini memungkinkan benchmark tetap berfungsi sebagai alat pengajaran tanpa mengorbankan reproduksibilitas di sandbox, sehingga pembaca dapat menjalankan ulang skrip dan memperoleh keluaran yang persis sama. Satuan yang dilaporkan pemasa virtual adalah tik, yang bersifat abstrak, yaitu satu tik per operasi atomik yang ditandai oleh kode yang diukur. Pada benchmark nyata, satuan tik diganti dengan detik, yang dimajukan oleh OS scheduler, sehingga nilai timing bervariasi antar mesin dan antar beban sistem.
Signifikansi dan blind A/B#
Rasio percepatan yang dilaporkan benchmark hanya bermakna bila perbedaannya signifikan secara statistik. Pada benchmark sederhana, kriteria signifikansi yang dipakai adalah selisih median yang jauh melebihi variansi sampel. Bila median kedua versi hanya berbeda sedikit dan variansi sampel besar, rasio yang dilaporkan mungkin tidak merepresentasikan percepatan nyata, melainkan derau pengukuran. Pada benchmark nyata yang lebih ketat, uji statistik seperti Mann-Whitney atau bootstrap dipakai untuk memastikan perbedaan median bukan akibat kebetulan.
Blind A/B adalah praktik yang menyembunyikan label versi dari pengukur, sehingga keputusan versi mana yang lebih cepat tidak dipengaruhi ekspektasi. Pada benchmark compiler, blind A/B jarang dipraktikkan secara harfiah sebab versi dapat dibedakan dari kode sumbernya. Namun, disiplin yang setara adalah menjalankan kedua versi pada urutan acak, dengan dataset yang sama, dan pada mesin yang sama dalam keadaan beban yang serupa, sehingga perbedaan timing benar-benar mencerminkan perbedaan kode, bukan perbedaan lingkungan. Disiplin ini melengkapi benchmark sebelum versus sesudah agar keputusan memasukkan pass optimasi ke pipeline didasarkan pada bukti, bukan dugaan.
Mempraktikkan benchmark#
Berkas bench.py berisi pemasa virtual, dua versi fungsi penjumlahan, fungsi time_median, dan runner yang menjalankan kedua versi lalu melaporkan rasio. Kontrak IR tidak terlibat pada topik ini, sebab pengukuran dilakukan pada tingkat fungsi Python, bukan IR. Runner menguji sum_naive(1000) versus sum_optimized(1000), di mana versi naif melakukan seribu penambahan dalam perulangan, sedangkan versi dioptimasi memakai rumus Gauss n * (n + 1) // 2 yang hanya melakukan tiga operasi aritmetika.
"""Benchmark mini untuk optimasi: pemasa virtual sebagai pengganti
time.perf_counter agar keluaran benchmark dapat direproduksi di sandbox.
Pemasa virtual memperlihatkan antarmuka yang sama dengan
time.perf_counter, yaitu metode perf_counter() yang mengembalikan nilai
'waktu' saat ini. Perbedaannya, nilai pemasa virtual dimajukan oleh
kode yang diukur melalui tick(), bukan oleh OS scheduler, sehingga
keluaran benchmark menjadi deterministik. Pada benchmark nyata,
pemasa virtual diganti dengan time.perf_counter dan panggilan tick()
dihapus dari kode yang diukur.
Disiplin yang ditunjukkan pada topik ini:
1. Verifikasi kesetaraan: kedua versi fungsi harus menghasilkan nilai
yang sama sebelum timing dibandingkan, agar tidak membandingkan
fungsi yang berbeda secara semantik.
2. Median bukan rerata: median dipakai karena tahan terhadap satu-dua
sampel pencilan akibat penjadwalan OS.
3. Rasio sebelum/sesudah: perbandingan waktu sebelum (naif) dan sesudah
(dioptimasi) dilaporkan sebagai rasio percepatan.
"""
import statistics
class Pemasa:
"""Pemasa virtual: counter 'tik' dengan antarmuka time.perf_counter.
Kode yang diukur memajukan pemasa dengan tick() per operasi yang
dianggap 'atomik' (misalnya satu penambahan). Pada benchmark nyata,
pemasa ini diganti dengan time.perf_counter dan kode yang diukur
tidak lagi memanggil tick(), sebab waktu dinding sudah dimajukan
oleh OS.
"""
_t: int = 0
@classmethod
def tick(cls, n: int = 1) -> None:
cls._t += n
@classmethod
def reset(cls) -> None:
cls._t = 0
@classmethod
def perf_counter(cls) -> int:
return cls._t
def sum_naive(n: int) -> int:
"""Penjumlahan naif: 1 + 2 + ... + n dengan perulangan eksplisit.
Setiap iterasi menambahkan satu nilai, sehingga pemasa virtual
dimajukan satu tik per iterasi.
"""
total = 0
for i in range(1, n + 1):
total += i
Pemasa.tick()
return total
def sum_optimized(n: int) -> int:
"""Penjumlahan dioptimasi: rumus Gauss n*(n+1)//2.
Tiga operasi 'atomik' dilakukan: kali, tambah, bagi. Pemasa
virtual dimajukan tiga tik.
"""
result = n * (n + 1) // 2
Pemasa.tick(3)
return result
def time_median(fn, n: int = 100) -> int:
"""Jalankan fn() sebanyak n kali, kembalikan median 'tik' pemasa.
Pemasa di-reset sebelum setiap pemanggilan, lalu nilai akhirnya
dicatat sebagai satu sampel. Median dari n sampel dilaporkan
sebagai hasil benchmark.
"""
samples: list[int] = []
for _ in range(n):
Pemasa.reset()
fn()
samples.append(Pemasa.perf_counter())
return statistics.median(samples)
def main() -> None:
n = 1000
repeats = 100
expected = n * (n + 1) // 2
# Verifikasi kesetaraan: kedua versi harus menghasilkan nilai sama.
assert sum_naive(n) == expected
assert sum_optimized(n) == expected
print(f"verifikasi: sum_naive({n}) = sum_optimized({n}) = {expected}")
print()
t_naive = int(time_median(lambda: sum_naive(n), repeats))
t_opt = int(time_median(lambda: sum_optimized(n), repeats))
print(f"sum_naive median dari {repeats}x: {t_naive} tik")
print(f"sum_opt median dari {repeats}x: {t_opt} tik")
print(f"rasio percepatan: {t_naive // t_opt}x")
if __name__ == "__main__":
main()Keluaran berikut diperoleh dengan menjalankan python3 bench.py. Pemasa virtual mencatat 1000 tik untuk sum_naive, sebab seribu penambahan dilakukan. Pemasa virtual mencatat 3 tik untuk sum_optimized, sebab tiga operasi aritmetika dilakukan. Rasio percepatan dilaporkan sebagai pembagian bulat kedua nilai, yaitu sekitar 333x. Pada benchmark nyata dengan time.perf_counter, rasio sebenarnya bervariasi tergantung mesin, tetapi pola yang sama terlihat, yaitu versi dioptimasi jauh lebih cepat daripada versi naif pada n yang besar.
verifikasi: sum_naive(1000) = sum_optimized(1000) = 500500
sum_naive median dari 100x: 1000 tik
sum_opt median dari 100x: 3 tik
rasio percepatan: 333xKeluaran menunjukkan tiga pilar disiplin benchmark sebelum versus sesudah. Pertama, verifikasi kesetaraan memastikan kedua fungsi menghasilkan 500500 sebelum timing dibandingkan. Kedua, median dipakai bukan rerata agar satu-dua pencilan tidak menggeser hasil. Ketiga, rasio percepatan dilaporkan sebagai angka tunggal, yaitu 333x, yang dapat dibandingkan langsung antar pass. Pada benchmark compiler nyata, rasio seperti ini menjadi dasar keputusan apakah sebuah pass optimasi layak masuk pipeline, atau dibuang karena dampaknya terlalu kecil dibandingkan biaya analisisnya.
Ringkasan#
- Disiplin benchmark sebelum versus sesudah membandingkan waktu kedua versi fungsi yang menghasilkan nilai sama. Verifikasi kesetaraan diletakkan paling awal sebelum timing dilakukan.
- Median dipakai sebagai pengganti rerata sebab tahan terhadap satu-dua pencilan akibat penjadwalan OS.
- Pada topik ini, pemasa virtual menggantikan
time.perf_counteragar keluaran benchmark dapat direproduksi di sandbox. Pada benchmark nyata, pemasa virtual diganti dengantime.perf_counterdan panggilantick()dihapus. - Signifikansi dan blind A/B melengkapi benchmark sebelum versus sesudah agar keputusan memasukkan pass ke pipeline didasarkan pada bukti, bukan dugaan.
Referensi#
- time — Time access and measurements — Python documentation
— Penjelasan resmi
time.perf_countersebagai pemasa resolusi tinggi pada Python. - statistics — Mathematical statistics functions — Python documentation
— Definisi
statistics.mediandan keterangannya terhadap pencilan. - Benchmarking — Wikipedia — Konsep umum benchmark pada komputasi, termasuk disiplin sebelum versus sesudah.