Tipe SIMD

Mendeklarasikan SIMD dengan parameter DType dan lebar serta mengoperasikan nilainya.

Daftar isi

Tipe SIMD di Mojo dinyatakan dengan dua parameter compile-time sekaligus, yaitu tipe elemen dan lebar vektor. Topik ini membahas cara menuliskan keduanya, membuat nilai dengan konstruktor variadic dan broadcast, membaca serta mengubah lane tertentu, mengonversi tipe elemen dengan cast, dan kedudukan tipe skalar di dalam sistem tipe SIMD. Setelah topik ini dibaca, deklarasi SIMD dengan lebar dan tipe elemen apa pun dapat ditulis dengan benar tanpa menebak bentuk konstruktornya.

Dua parameter tipe: DType dan lebar#

Setiap penulisan tipe SIMD membutuhkan dua parameter di dalam kurung siku. Parameter pertama bertipe DType, yaitu enumerasi tipe elemen yang berisi DType.float64, DType.float32, DType.float16, DType.int32, dan lainnya. Parameter kedua bertipe Int dan menyatakan lebar vektor, yaitu jumlah lane yang dihitung bersamaan. Penulisan SIMD[DType.float64, 4] berarti vektor empat lane dengan setiap lane bertipe float64, sedangkan SIMD[DType.int32, 8] berarti delapan lane bilangan bulat 32 bit.

Lebar vektor wajib berupa pangkat dua antara 1 dan 2^15, sehingga lebar 1, 2, 4, 8, dan 16 sah, sedangkan lebar 3 ditolak compiler dengan pesan SIMD vector length must be a power of two between 1 and 2^15. Aturan pangkat dua ini berakar pada cara perangkat keras membagi register vektornya. Kedua parameter sama-sama dievaluasi pada compile time, sehingga lebar dan tipe elemen menjadi bagian tetap dari tipe tersebut. Akibatnya, SIMD[DType.float64, 4] dan SIMD[DType.float64, 8] adalah dua tipe yang berbeda, dan nilai satu tipe tidak otomatis dipandang sebagai nilai dari tipe lainnya.

Membuat nilai SIMD#

Konstruktor variadic menerima satu argumen untuk setiap lane, dan urutan argumennya menentukan isi lane dari awal ke akhir. Penulisan SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0) menghasilkan lane bernilai 1.0, 2.0, 3.0, dan 4.0 pada posisi 0 sampai 3. Bila seluruh lane diisi nilai yang sama, konstruktor broadcast lebih ringkas, yaitu memberikan satu argumen yang disiarkan ke semua lane. Penulisan SIMD[DType.float16, 8](1.0) menghasilkan delapan lane yang semuanya bernilai 1.0 tanpa menulis delapan angka.

Jalankan program berikut yang memuat kedua bentuk konstruktor beserta pembacaan dan pengubahan lane.

mojo deklarasi.mojo
def main():
    var v = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0)
    print(v)
    print(v[2])
    print(len(v))

    var seragam = SIMD[DType.float16, 8](1.0)
    print(seragam)

    v[0] = 10.0
    print(v)
bash
# jalankan program deklarasi dan pengoperasian SIMD
mojo run deklarasi.mojo
text Keluaran
[1.0, 2.0, 3.0, 4.0]
3.0
4
[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
[10.0, 2.0, 3.0, 4.0]

Keluaran menampilkan isi vektor dalam kurung siku dengan setiap lane dipisahkan koma. Baris print(v[2]) membaca lane pada indeks 2, yaitu 3.0, karena indeks lane dimulai dari 0. Penugasan v[0] = 10.0 menulis lane pertama, dan keluaran terakhir memperlihatkan perubahannya menjadi [10.0, 2.0, 3.0, 4.0]. Pemanggilan len(v) mengembalikan 4 sesuai lebar yang ditulis pada deklarasinya, sehingga jumlah lane dapat diperiksa langsung dari nilai, bukan dari ingatan tentang deklarasinya.

Cast dan tipe skalar#

Metode cast mengubah tipe elemen vektor tanpa mengubah lebar maupun isi numeriknya. Pemanggilan v.cast[DType.float32]() pada vektor float64 menghasilkan vektor SIMD[DType.float32, 4] dengan nilai yang sama, dan bentuk kurung sikunya meniru bentuk deklarasi tipe. Konversi antar presisi mengikuti aturan konversi numerik pada umumnya, sehingga penurunan presisi dapat menghilangkan ketepatan desimal apabila nilainya tidak dapat diwakili tipe tujuan.

Tipe skalar pada Mojo sebenarnya adalah SIMD dengan lebar 1. Tipe Float64 adalah alias dari SIMD[DType.float64, 1], sehingga variabel skalar dapat ditugaskan ke variabel bertipe SIMD lebar 1 tanpa konversi apa pun. Kesatuan ini membuat operasi antara skalar dan vektor terasa konsisten, karena keduanya berada pada satu sistem tipe yang sama. Tipe elemen sebuah nilai SIMD dapat diperiksa melalui atribut dtype, dan perbandingannya memakai operator ==, bukan is, misalnya n.dtype == DType.int32.

Jalankan program berikut yang memuat konversi tipe, vektor bilangan bulat, dan persamaan tipe skalar.

mojo skalar.mojo
def main():
    var v = SIMD[DType.float64, 4](1.0, 2.0, 3.0, 4.0)
    var kecil = v.cast[DType.float32]()
    print(kecil)

    var n = SIMD[DType.int32, 4](1, 2, 3, 4)
    print(n)
    print(n.dtype == DType.int32)

    var x: Float64 = 2.5
    var y: SIMD[DType.float64, 1] = x
    print(y)
bash
# jalankan program konversi tipe dan persamaan skalar
mojo run skalar.mojo
text Keluaran
[1.0, 2.0, 3.0, 4.0]
[1, 2, 3, 4]
True
2.5

Keluaran memperlihatkan bahwa cast mempertahankan nilai dan lebar, vektor int32 dicetak tanpa bagian desimal, dan perbandingan n.dtype == DType.int32 menghasilkan True. Penugasan var y: SIMD[DType.float64, 1] = x diterima compiler tanpa konversi apa pun karena Float64 dan SIMD[DType.float64, 1] memang satu tipe yang sama. Dari sini, skalar dan vektor dapat dipandang sebagai satu keluarga tipe yang hanya berbeda lebar.

Kesalahan umum#

  • Menyediakan jumlah argumen variadic yang tidak sama dengan lebar. Penulisan SIMD[DType.float64, 4](1.0, 2.0) tidak menghasilkan pesan error yang rapi, melainkan menghentikan proses kompilasi dengan pesan execution crashed tanpa penjelasan penyebabnya. Perbaikannya adalah menyamakan jumlah argumen dengan lebar atau memakai bentuk broadcast bila isi semua lane sama.
  • Mengindeks lane di luar batas. Pembacaan v[4] pada vektor empat lane tidak menampilkan pesan error apa pun, melainkan membaca memori di sekitarnya sehingga nilai yang diperoleh tidak berarti. Perbaikannya adalah memastikan indeks berasal dari perulangan dengan batas yang benar atau dibatasi len(v) - 1.
  • Menulis lebar yang bukan pangkat dua. Lebar 3, 5, atau 6 ditolak compiler dengan pesan SIMD vector length must be a power of two between 1 and 2^15, sehingga pilihan lebar perlu dihitung sejak awal dari nilai 1, 2, 4, 8, dan seterusnya.

Ringkasan#

  • Tipe SIMD dibentuk oleh dua parameter compile-time, yaitu DType untuk tipe elemen dan Int untuk lebar vektor yang wajib pangkat dua.
  • Konstruktor variadic menerima satu argumen per lane, sedangkan konstruktor broadcast menerima satu argumen yang mengisi semua lane.
  • Lane dibaca dan ditulis lewat indeks v[i], jumlah lane diperiksa dengan len(v), dan tipe elemen dikonversi dengan cast tanpa mengubah lebar.
  • Float64 adalah alias SIMD[DType.float64, 1], sehingga skalar dan vektor berada pada satu sistem tipe yang sama.

Referensi#