Alur kontrol compile-time mengulang dan mencabangkan kode pada fase kompilasi, sehingga pilihan tipe dan pengulangan berparameter selesai sebelum program berjalan. Topik ini membahas comptime if yang memilih satu cabang menurut parameter compile-time dan comptime for yang menguraikan perulangan menjadi salinan kode per nilai range. Setelah topik ini dibaca, percabangan menurut dtype dan pengulangan sesuai lebar vektor dapat ditulis tanpa biaya apa pun pada saat eksekusi.
Percabangan comptime if#
Pernyataan comptime if mengevaluasi syaratnya pada fase kompilasi dan hanya mengkompilasi cabang yang terpilih. Syaratnya wajib berupa nilai compile-time, misalnya parameter fungsi atau konstanta comptime, sehingga percabangan menurut DType ditulis dengan perbandingan == sebagaimana nilai biasa. Bentuknya memakai elif dan else yang ditulis polos setelah blok comptime if, dan ketiganya menjadi satu rantai percabangan compile-time.
Program berikut memilih deskripsi tipe menurut parameter dtype pada sebuah fungsi generik.
def deskripsi[dtype: DType]() -> String:
comptime if dtype == DType.float64:
return "pecahan presisi ganda"
elif dtype == DType.float32:
return "pecahan presisi tunggal"
else:
return "tipe lain"
def main():
print(deskripsi[DType.float64]())
print(deskripsi[DType.float32]())
print(deskripsi[DType.int64]())# jalankan program percabangan compile-time
mojo run deskripsi.mojopecahan presisi ganda
pecahan presisi tunggal
tipe lainPemanggilan deskripsi[DType.float64]() mengaktifkan cabang pertama dan menghasilkan pecahan presisi ganda, sedangkan DType.float32 mengaktifkan cabang kedua. Parameter dtype dibandingkan dengan == langsung pada syaratnya, tanpa fungsi bantu apa pun. Pemanggilan deskripsi[DType.int64]() jatuh ke cabang else karena int64 bukan tipe pecahan, dan keluaran tipe lain menegaskan cabang ketiganya. Tiga string pada keluaran itu sudah melekat pada tiga spesialisasi fungsi, bukan dipilih saat program berjalan.
Hasil ketiga pemanggilan dalam satu program menunjukkan spesialisasi yang berdampingan. Fungsi generik yang sama menerima tiga dtype berbeda tanpa penulisan ulang body, dan pilihan deskripsinya sudah mengeras pada setiap spesialisasi. Penulisan ini menggantikan pola menyalin fungsi per tipe dan mengubah isi salinannya secara manual.
Perulangan comptime for dan unroll#
Pernyataan comptime for menguraikan (unroll) perulangan dengan menyalin body per nilai range pada fase kompilasi, sehingga tidak ada penghitung perulangan saat program berjalan. Variabel perulangannya merupakan konstanta compile-time, sehingga sah dipakai di tempat yang menuntut nilai tetap, misalnya indeks lane dan parameter kurung siku. Rentang perulangan ditulis dengan range yang batasnya juga berupa nilai compile-time.
Program berikut menjumlahkan lane sebuah vektor secara manual dengan comptime for, kemudian memanggil fungsi jumlahkan untuk empat lebar yang dihitung dari variabel perulangan.
comptime LEBAR = 4
def jumlahkan[lebar: Int](v: SIMD[DType.float64, lebar]) -> Float64:
return v.reduce_add()
def main():
var v = SIMD[DType.float64, LEBAR](1.0, 2.0, 3.0, 4.0)
var total: Float64 = 0.0
comptime for i in range(LEBAR):
total += v[i]
print(total)
comptime for i in range(4):
comptime lebar = 1 << i
print(jumlahkan[lebar](SIMD[DType.float64, lebar](1.0)))# jalankan program perulangan yang diuraikan compiler
mojo run unroll.mojo10.0
1.0
2.0
4.0
8.0Perulangan pertama menyalin total += v[i] sebanyak empat kali dengan i bernilai 0 sampai 3, dan penjumlahan keempat lane itu menghasilkan 10.0. Perulangan kedua menghitung comptime lebar = 1 << i, sehingga lebar bernilai 1, 2, 4, dan 8 pada tiap salinan body. Setiap nilai lebar memanggil jumlahkan[lebar] pada vektor berisi 1.0, dan keluaran 1.0, 2.0, 4.0, 8.0 berasal dari empat spesialisasi berbeda yang dibangun compiler. Pola ini menjadi dasar penulisan kernel generik yang melayani banyak lebar sekaligus.
Kapan alur compile-time dipilih#
Alur kontrol compile-time dipilih ketika keputusan bergantung pada tipe atau lebar, bukan pada data yang dibaca program saat berjalan. Percabangan menurut dtype pada contoh di atas tidak mungkin ditulis dengan if biasa karena parameternya tidak ada pada saat eksekusi. Pilihan cabang yang selesai saat kompilasi juga menghapus biaya pemeriksaan syarat, dan hanya satu cabang yang masuk ke kode akhir.
Perulangan runtime tetap menjadi pilihan untuk data yang panjangnya baru diketahui saat program berjalan, sedangkan comptime for tepat untuk pengulangan yang jumlahnya terikat parameter. Kedua bentuk dapat dipadukan dalam satu fungsi, misalnya unroll per lane di dalam kernel yang menerima data runtime. Kernel generik yang melayani banyak dtype dan lebar memanfaatkan kedua konstruksi ini bersama parameter kurung siku.
Deklarasi konstanta comptime pada tingkat modul menyediakan nilai untuk alur kontrol ini. Konstanta LEBAR pada program di atas menjadi batas range perulangan pertama, sehingga perubahan lebar di satu tempat langsung mengubah jumlah salinan body. Konstanta dan pernyataan alur kontrol memakai kata kunci yang sama, sehingga syarat percabangan maupun rentang perulangan selalu berupa nilai compile-time.
Kesalahan umum#
- Menuliskan
comptime elif. Kata kuncicomptimehanya berlaku padaifpertama, dan penulisancomptime elifditolak dengan pesan'comptime' cannot be used with 'elif'. Perbaikannya adalah menuliskanelifdanelsepolos setelah blokcomptime if, karena keduanya tetap menjadi bagian rantai compile-time. - Menggunakan nilai runtime sebagai syarat. Pernyataan
comptime if x > 5yang merujuk variabelxyang dideklarasikan denganvarditolak dengan pesancannot use a dynamic value in 'comptime if' condition. Perbaikannya adalah memakaiifbiasa bila syaratnya bergantung pada data runtime, atau menjadikan syaratnya parameter dan konstanta compile-time. - Menghitung nilai parameter dengan
vardi dalamcomptime for. Penulisanvar lebar = 1 << imenghasilkan nilai dinamis yang ditolak pada daftar parameter dengan pesancannot use a dynamic value in a parameter list. Perbaikannya adalah mendeklarasikannya dengancomptime lebar = 1 << iagar nilainya tetap konstanta compile-time.
Ringkasan#
comptime ifmengevaluasi syarat pada fase kompilasi dan hanya mengkompilasi cabang yang terpilih.elifdanelsepolos melanjutkan rantaicomptime if, sedangkancomptime eliftidak sah.comptime formenguraikan perulangan menjadi salinan body per nilai range, dan variabelnya konstanta compile-time.- Rentang yang panjang menaikkan waktu kompilasi, sehingga unroll dipakai untuk iterasi kecil yang jumlahnya diketahui.
Referensi#
- Parameterized declarations — Mojo Manual — Dokumentasi resmi deklarasi berparameter yang menyertai alur kontrol compile-time.
- Mojo Manual — Modular — Panduan resmi bahasa Mojo untuk pernyataan comptime.