Kecepatan program pada CPU modern tidak lagi bertambah dengan sendirinya setiap kali prosesor generasi baru dirilis. Topik ini menjelaskan peran register prosesor, perbedaan register skalar dan register vektor, arti lane pada SIMD, serta alasan era peningkatan kecepatan gratis sudah berakhir. Setelah topik ini dibaca, alasan SIMD menjadi bagian penting dalam Mojo dapat dipahami dari mekanisme perangkat kerasnya, bukan dari saran umum tentang optimasi.
Register skalar dan register vektor#
Register adalah lokasi penyimpanan kecil di dalam prosesor yang menjadi tempat perhitungan sesungguhnya berlangsung. Setiap operasi aritmetika membaca operand dari register dan menulis hasilnya kembali ke register, sehingga data yang masih berada di memori utama harus dipindahkan ke register lebih dahulu. Satu register skalar menampung tepat satu nilai, misalnya satu bilangan float64 berukuran 64 bit atau 8 byte. Instruksi penjumlahan skalar mengambil dua register, menjumlahkan isinya, dan menghasilkan satu nilai baru, sehingga empat penjumlahan membutuhkan empat instruksi yang dijalankan satu per satu.
Selain register skalar, CPU modern menyediakan register vektor dengan kapasitas jauh lebih besar. Register vektor berukuran 128, 256, atau 512 bit pada CPU yang beredar saat ini, dan isinya dapat dibagi menjadi beberapa posisi bilangan. Setiap posisi tersebut disebut lane, yaitu satu slot penyimpanan bilangan di dalam register vektor yang dihitung bersamaan dengan slot lainnya. Register 256 bit memuat empat lane float64 karena , delapan lane float32, atau enam belas lane float16. Jumlah lane inilah yang disebut lebar vektor.
Satu instruksi untuk semua lane#
SIMD bekerja sesuai dengan namanya, yaitu Single Instruction, Multiple Data. Satu instruksi penjumlahan vektor mengambil dua register vektor, menjumlahkan lane pertama dengan lane pertama, lane kedua dengan lane kedua, dan seterusnya, lalu menulis setiap hasil ke lane yang bersesuaian pada register hasil. Empat penjumlahan yang biasanya membutuhkan empat instruksi kini cukup dilakukan dengan satu instruksi, karena keempat lane berbagi instruksi yang sama dan tidak ada alur eksekusi tambahan yang diperlukan.
Program berikut membandingkan penjumlahan skalar dengan penjumlahan vektor pada empat nilai sekaligus. Penulisan SIMD[DType.float64, 4] menyatakan vektor empat lane float64, dan ekspresi va + vb dihitung pada seluruh lane dalam satu ekspresi.
def main():
var a = 2.0
var b = 3.0
print(a + b)
var va = SIMD[DType.float64, 4](2.0, 4.0, 6.0, 8.0)
var vb = SIMD[DType.float64, 4](3.0, 3.0, 3.0, 3.0)
print(va + vb)
print(len(va))# jalankan program perbandingan skalar dan vektor
mojo run lane.mojo5.0
[5.0, 7.0, 9.0, 11.0]
4Keluaran memperlihatkan kedua bentuk perhitungan. Nilai 5.0 adalah hasil penjumlahan skalar dua variabel, sedangkan deret [5.0, 7.0, 9.0, 11.0] adalah empat hasil yang dihitung sekaligus oleh satu ekspresi vektor. Pemanggilan len(va) mengembalikan 4 karena vektor tersebut memiliki empat lane. Pada bagian ini tidak ada klaim tentang berapa kali lebih cepat, karena kecepatan sesungguhnya bergantung pada CPU, compiler, dan bentuk program, tetapi mekanismenya sudah jelas, yaitu satu instruksi mengerjakan pekerjaan empat lane.
Keuntungan ini paling besar ketika pekerjaannya seragam, yaitu operasi yang sama diterapkan pada banyak elemen, seperti penjumlahan deret nilai, penghitungan statistik, dan konversi satuan pada data pengukuran. Pekerjaan dengan percabangan yang berbeda-beda per elemen justru sulit dimanfaatkan, karena satu instruksi aritmetika menerapkan operasi yang sama pada tiap lane. Karena itu SIMD menonjol pada perhitungan numerik dengan pola yang berulang, dan pola semacam itu mendominasi pekerjaan pengolahan data.
Akhirnya peningkatan kecepatan gratis#
Selama beberapa dekade, program yang sama menjadi lebih cepat tanpa perubahan apa pun setiap kali prosesor generasi baru dipasang. Peningkatan itu bersumber dari frekuensi jam yang terus naik, yaitu laju prosesor menjalankan instruksi. Frekuensi jam berhenti naik pesat karena setiap kenaikan menuntut daya listrik yang lebih besar dan menghasilkan panas yang sulit dibuang. Sejak itu, pertambahan kecepatan datang dari jalur lain, yaitu paralelisme, baik berupa jumlah inti prosesor yang bertambah maupun lebar register vektor yang makin lebar.
Pergeseran ini berdampak langsung pada kode skalar. Kode yang mengolah satu nilai per instruksi tidak lagi menerima peningkatan gratis dari generasi prosesor baru, sehingga SIMD menjadi salah satu cara utama mempercepat perhitungan pada satu inti prosesor. Compiler sesungguhnya dapat mengubah perulangan biasa menjadi instruksi SIMD secara otomatis, tetapi keberhasilannya bergantung pada bentuk perulangan, pola akses memori, dan kemampuan compiler membuktikan bahwa perubahan itu aman. Kegagalan proses otomatis tersebut tidak menampilkan pesan error apa pun, sehingga kode tampak normal padahal keuntungannya hilang.
Paralelisme yang dibawa SIMD juga berbeda bentuknya dari paralelisme antar inti prosesor. Paralelisme SIMD terjadi di dalam satu inti pada satu instruksi, sehingga perhitungan besar pada deret data dapat dipercepat tanpa memecah tugasnya ke beberapa inti, sedangkan paralelisme antar inti memecah pekerjaan menjadi bagian yang dijalankan bersamaan. Kedua bentuk ini saling melengkapi dan dapat dipadukan, tetapi SIMD beroperasi pada tingkat yang paling dekat dengan perhitungan satu elemen.
Mojo menjadikan SIMD sebagai tipe bawaan yang eksplisit. Lebar vektor ditulis dalam kode sebagai bagian dari tipe, sehingga jumlah lane yang dihitung bersamaan dapat dinyatakan dan diperiksa compiler. Pendekatan ini mengubah keputusan vektorisasi dari sekadar harapan terhadap compiler menjadi keputusan yang tertulis di dalam program.
Ringkasan#
- Register skalar menampung satu nilai, sedangkan register vektor membagi kapasitasnya menjadi beberapa lane yang dihitung bersamaan oleh satu instruksi.
- Register vektor 256 bit memuat empat lane
float64, dan fungsilenmenghitung jumlah lane sebuah vektor SIMD. - Frekuensi jam prosesor berhenti naik pesat, sehingga peningkatan kecepatan kini datang dari paralelisme, dan SIMD merupakan salah satu bentuknya pada satu inti prosesor.
- Mojo menuliskan lebar vektor secara eksplisit sebagai bagian dari tipe, sehingga vektorisasi tidak bergantung pada keberhasilan vektorisasi otomatis compiler.
Referensi#
- Mojo Manual — Panduan resmi bahasa Mojo yang menjadi dasar penulisan sintaks pada topik ini.
- SIMD — Mojo standard library — Dokumentasi tipe SIMD beserta parameter tipe elemen dan lebar vektornya.