
Pendahuluan
Emu adalah bahasa pemrograman kartu grafis tingkat tinggi yang dapat disematkan dalam kode reguler dalam bahasa pemrograman sistem Rust .
Artikel ini akan fokus pada sintaks Emu, fitur-fiturnya, dan beberapa contoh ilustrasi penggunaannya dalam kode nyata akan ditampilkan.
Instalasi
- Perpustakaan yang Anda cari membutuhkan ketergantungan OpenCL eksternal. Anda perlu menginstal driver yang sesuai untuk perangkat keras Anda.
Cargo.toml
teks di bawah ini. Ini akan mengunduh versi terbaru yang tersedia (jika Anda memerlukan perakitan khusus, maka daripada *
meletakkan versi yang Anda butuhkan):
[dependencies] em = "*" // Emu ocl = "*" // OpenCL
Sintaks
Sintaks Emu cukup sederhana, karena bahasa ini dimaksudkan hanya untuk menulis fungsi kernel yang dikompilasi ke OpenCL .
Tipe data
Bahasa Emu memiliki sembilan tipe data yang mirip dengan yang ada di Rust. Berikut ini adalah tabel jenis data ini:
Variabel
Variabel dideklarasikan menggunakan kata kunci let
, yang terletak di belakang pengidentifikasi, titik dua, tipe data, tanda sama, nilai yang diberikan, dan titik koma.
let age: i32 = 54; let growth: f32 = 179.432; let married: bool = true;
Konversi
Konversi tipe data primitif dilakukan dengan menggunakan operator biner, mengikuti tipe target. Saya perhatikan bahwa tipe target juga bisa menjadi unit pengukuran (lihat bagian selanjutnya):
let width: i16 = 324; let converted_width: i64 = width as i64;
Unit
Bahasa Emu memungkinkan Anda untuk memperlakukan angka sebagai satuan ukuran, yang dirancang untuk menyederhanakan perhitungan ilmiah. Dalam contoh ini, length
variabel awalnya didefinisikan dalam meter, tetapi kemudian unit pengukuran lainnya ditambahkan ke dalamnya:
let length: f32 = 3455.345;
Konstanta yang ditentukan sebelumnya
Emu memiliki seperangkat konstanta standar yang nyaman digunakan dalam praktik. Di bawah ini adalah tabel terkait.
Konstanta yang berhubungan dengan data ilmiah juga didefinisikan. Anda dapat menemukan tabel yang terdiri dari konstanta ini di sini .
Pernyataan bersyarat
Pernyataan bersyarat Emu mirip dengan pernyataan terkait di Rust. Kode berikut menggunakan konstruksi kondisional:
let number: i32 = 2634; let satisfied: bool = false; if (number > 0) && (number % 2 == 0) { satisfied = true; }
Untuk loop
Header untuk loop didefinisikan sebagai for NUM in START..END
, di mana NUM
adalah variabel yang mengambil nilai dari rentang [START; END)
[START; END)
melalui unit.
let sum: u64 = 0; for i in 0..215 { sum += i; }
Sementara loop
Judul loop Sementara didefinisikan sebagai while (CONDITION)
, di mana CONDITION
adalah kondisi bagi loop untuk melanjutkan ke iterasi berikutnya. Kode ini mirip dengan contoh sebelumnya:
let sum: u64 = 0; let idx: i32 = 0; while (idx < 215) { sum += idx; idx += 1; }
Loop tak berujung
Loop tak terbatas tidak memiliki kondisi keluar yang eksplisit dan ditentukan oleh kata kunci loop
. Namun, mereka dapat dilanjutkan atau diinterupsi oleh pernyataan break
dan continue
(seperti dua jenis loop lainnya).
let collapsed: u64 = 1; let idx: i32 = 0; loop { if idx % 2 == 0 { continue; } sum *= idx; if idx == 12 { break; } }
Kembali dari fungsi
Seperti dalam semua bahasa pemrograman lain, return
adalah output dari fungsi saat ini. Itu juga dapat mengembalikan nilai tertentu jika tanda tangan fungsi (lihat bagian berikut) memungkinkan ini.
let result: i32 = 23446; return result;
Operator lain
- Operator penugasan yang tersedia:
=
, +=
, -=
, *=
, /=
, %=
, &=
, ^=
, <<=
, >>=
; - Operator indeks adalah
[IDX]
; - Hubungi Operator -
(ARGS)
; - Operator unary:
*
untuk dereferencing ,! untuk membalikkan data Boolean, -
untuk meniadakan angka; - Operator biner:
+
, -
, *
, /
, %
, &&
, ||
, &
, |
, ^
, >>
, <<
, >
, <
, >=
, <=
, ==
, ==
!=
.
Fungsi
Ada tiga bagian fungsi pada Emu: pengidentifikasi, parameter, dan isi fungsi, yang terdiri dari urutan instruksi yang dapat dieksekusi. Pertimbangkan fungsi menambahkan dua angka:
add(left f32, right f32) f32 { return left + right; }
Seperti yang mungkin telah Anda perhatikan, fungsi ini mengembalikan jumlah dua argumen yang diteruskan menggunakan tipe data f32
.
Address Spaces
Setiap parameter fungsi sesuai dengan ruang alamat tertentu . Secara default, semua parameter sesuai dengan ruang __private__
.
Menambahkan awalan global_
dan local_
ke pengidentifikasi parameter secara eksplisit menunjukkan ruang alamatnya.
Dokumentasi menyarankan penggunaan awalan global_
untuk semua vektor dan tidak awalan apa pun.
Fungsi bawaan
Emu menyediakan sejumlah kecil fungsi bawaan (diambil dari OpenCL) yang memungkinkan Anda mengelola data GPU:
get_work_dim()
- Mengembalikan jumlah dimensi;get_global_size()
- Mengembalikan jumlah elemen global untuk dimensi yang diberikan;get_global_id()
- Mengembalikan pengidentifikasi unik elemen untuk dimensi yang ditentukan;get_global_size()
- Mengembalikan jumlah elemen global untuk dimensi yang diberikan;get_local_id()
- Mengembalikan pengidentifikasi unik untuk elemen lokal dalam kelompok kerja tertentu untuk dimensi tertentu;get_num_groups()
- Mengembalikan jumlah workgroups untuk dimensi yang diberikan;get_group_id()
- Mengembalikan pengidentifikasi unik untuk workgroup.
Dalam kode aplikasi, paling sering Anda akan menemukan ekspresi get_global_id(0)
, yang mengembalikan indeks saat ini dari elemen vektor yang terkait dengan panggilan ke fungsi kernel Anda.
Eksekusi kode
Pertimbangkan sintaks untuk memanggil fungsi Emu dari kode Rust biasa. Sebagai contoh, kita akan menggunakan fungsi yang mengalikan semua elemen vektor dengan angka yang diberikan:
use em::emu; emu! { multiply(global_vector [f32], scalar f32) { global_vector[get_global_id(0)] *= scalar; } }
Untuk menerjemahkan fungsi ini ke dalam kode OpenCL, Anda harus meletakkan tanda tangannya di makro build!
sebagai berikut:
use em::build;
Tindakan selanjutnya datang ke memanggil fungsi Emu yang Anda tulis dari kode Rust. Itu tidak bisa lebih mudah:
fn main() { let vector = vec![0.4445, 433.245, 87.539503, 2.0]; let result = multiply(vector, 2.0).unwrap(); dbg!(result); }
Contoh aplikasi
Program ini menggunakan skalar sebagai argumen pertama, dimana perlu untuk melipatgandakan argumen berikut. Vektor yang dihasilkan akan dicetak ke konsol:
use em::{build, emu};
Anda dapat menjalankan kode ini dengan cargo run -- 3 2.1 3.6 6.2
perintah. Kesimpulan yang dihasilkan memenuhi harapan:
[src/main.rs:33] result = [ 6.2999997, 10.799999, 18.599998, ]
Tautan ke OpenCL
Seperti yang disebutkan sebelumnya, Emu hanyalah abstraksi dari OpenCL , dan karena itu ia memiliki kemampuan untuk berinteraksi dengan ocl crate . Kode di bawah ini diambil dari contoh di repositori resmi :
use em::emu;
Penyelesaian
Saya harap Anda menikmati artikel ini. Anda bisa mendapatkan jawaban cepat untuk pertanyaan Anda di obrolan bahasa Rusia di Rust ( versi untuk pemula ).