Back to IF3130 Sistem Paralel dan Terdistribusi

Model Pemrograman CUDA: Interaksi Host-Device, Kernel, dan Alur Eksekusi

Questions/Cues

  • Apa itu model Host + Device?

  • Apa itu Kernel?

  • Bagaimana alur eksekusi program CUDA?

  • Apa saja 3 cara akselerasi aplikasi?

  • Apa itu CUDA C?

  • Bagaimana cara meluncurkan Kernel?

  • Bagaimana manajemen memori Host-Device?

  • Apa saja qualifier fungsi di CUDA?

Reference Points

  • 7 - IF-3230-07-GPU-01-2022.pdf

  • 6a - IF3230-06a-GPU-2022.pdf

  • 7 - IF-3230-07-GPU-02-2022.pdf

Model Pemrograman Host + Device

Model pemrograman CUDA didasarkan pada sistem heterogen yang terdiri dari dua entitas utama:

  1. Host: Merujuk pada CPU dan memorinya (RAM sistem). Host bertindak sebagai “otak” atau orkestrator utama. Ia menjalankan bagian sekuensial dari program, menangani I/O, dan mengelola eksekusi secara keseluruhan.

  2. Device: Merujuk pada GPU dan memorinya sendiri (VRAM atau device memory). Device bertindak sebagai co-processor atau “pekerja keras” yang sangat kuat untuk komputasi paralel.

Keduanya adalah unit pemrosesan yang terpisah, terhubung melalui bus PCI Express, dan yang terpenting, memiliki ruang memori yang terpisah. Data harus secara eksplisit ditransfer antara memori host dan memori device.

Apa itu Kernel?

Sebuah Kernel adalah fungsi C/C++ yang ditulis oleh programmer untuk dieksekusi di Device (GPU). Ciri khas utama dari kernel adalah:

  • Eksekusi Paralel: Saat diluncurkan dari Host, sebuah kernel dieksekusi secara bersamaan oleh ribuan atau jutaan thread di GPU.

  • SPMD (Single Program, Multiple Data): Semua thread menjalankan kode program yang sama (kernel yang sama), tetapi setiap thread bekerja pada porsi data yang berbeda, yang ditentukan oleh ID uniknya (akan dibahas di catatan berikutnya).

  • Qualifier Khusus: Kernel didefinisikan dalam kode menggunakan qualifier __global__. Ini menandakan kepada compiler NVCC bahwa fungsi ini dapat dipanggil dari Host dan akan dieksekusi di Device.

Contoh Deklarasi Kernel:

__global__ void MyKernel(float* data, int size) {
    // Kode yang akan dijalankan oleh setiap thread di GPU
}

Alur Eksekusi Program CUDA

Sebuah program CUDA tipikal mengikuti alur kerja standar yang melibatkan interaksi bolak-balik antara Host dan Device:

  1. Inisialisasi di Host: Program dimulai dan berjalan di CPU.

  2. Alokasi Memori di Device: Host menginstruksikan GPU untuk mengalokasikan ruang memori di device memory untuk input dan output (cudaMalloc()).

  3. Transfer Data ke Device: Host menyalin data input dari memorinya ke memori device yang baru dialokasikan (cudaMemcpy() dengan flag cudaMemcpyHostToDevice).

  4. Peluncuran Kernel: Host meluncurkan kernel di Device (MyKernel<<<...>>>()). Pada titik ini, CPU melanjutkan tugasnya atau menunggu, sementara GPU mulai mengeksekusi kernel secara masif paralel.

  5. Transfer Hasil Kembali ke Host: Setelah kernel selesai, Host menyalin data hasil dari memori device kembali ke memorinya (cudaMemcpy() dengan flag cudaMemcpyDeviceToHost).

  6. Pembersihan: Host membebaskan memori yang dialokasikan di Device (cudaFree()).

  7. Lanjutan di Host: Program melanjutkan eksekusi di CPU menggunakan hasil yang telah disalin.

Tiga Cara Akselerasi Aplikasi

Ada tiga pendekatan utama untuk memanfaatkan kekuatan GPU, dengan trade-off antara kemudahan penggunaan dan fleksibilitas:

  1. Libraries (Pustaka): Cara termudah. Menggunakan pustaka yang sudah dioptimalkan oleh para ahli (seperti NVIDIA) untuk tugas-tugas umum (misalnya, cuBLAS untuk aljabar linear, Thrust untuk algoritma data paralel). Seringkali bersifat “drop-in” dengan sedikit perubahan kode.

  2. Compiler Directives (Direktif Compiler): Memberikan petunjuk kepada compiler tentang bagian kode mana yang harus diparalelkan. Contohnya adalah OpenACC. Ini memungkinkan kode tetap portabel dan lebih mudah dikelola, tetapi kinerjanya mungkin tidak seoptimal kode kustom.

  3. Programming Languages (Bahasa Pemrograman): Pendekatan yang paling kuat dan fleksibel. Menggunakan bahasa seperti CUDA C/C++ atau OpenCL untuk menulis kernel kustom. Ini memberikan kontrol penuh atas paralelisme dan pergerakan data, memungkinkan potensi performa maksimal.

Pembelajaran kita akan fokus pada pendekatan ketiga, yaitu menggunakan CUDA C.

CUDA C: Ekstensi untuk Pemrograman Paralel

CUDA C bukanlah bahasa yang sepenuhnya baru, melainkan ekstensi dari bahasa C/C++. Ia menambahkan beberapa elemen kunci untuk memungkinkan pemrograman GPU:

  • Function Qualifiers: Seperti __global__, __device__, __host__ untuk menentukan di mana sebuah fungsi dieksekusi dan dari mana ia bisa dipanggil.

  • Variable Qualifiers: Seperti __shared__, __constant__ untuk menentukan tipe memori GPU tempat variabel disimpan.

  • Execution Configuration: Sintaks <<<...>>> yang digunakan saat memanggil kernel untuk menentukan jumlah thread block dan thread per-blok yang akan diluncurkan.

  • Built-in Variables: Variabel yang disediakan secara otomatis di dalam kernel seperti threadIdx dan blockIdx yang memungkinkan setiap thread mengetahui identitas uniknya.

Manajemen Memori dan Transfer Data

Karena Host dan Device memiliki memori terpisah, manajemen data harus dilakukan secara eksplisit:

  • cudaMalloc((void**)&d_ptr, size): Mengalokasikan size byte memori di Device dan menyimpan alamatnya di pointer d_ptr.

  • cudaMemcpy(dst, src, size, direction): Menyalin size byte data. direction bisa berupa:

    • cudaMemcpyHostToDevice: Dari CPU ke GPU.

    • cudaMemcpyDeviceToHost: Dari GPU ke CPU.

  • cudaFree(d_ptr): Membebaskan memori di Device yang ditunjuk oleh d_ptr.

Function Qualifiers di CUDA

Qualifier ini menentukan lokasi eksekusi dan pemanggilan sebuah fungsi:

QualifierDieksekusi di…Dapat Dipanggil dari…
__global__Device (GPU)Host (CPU)
__device__Device (GPU)Device (GPU)
__host__Host (CPU)Host (CPU)

Fungsi __host__ dan __device__ dapat digabungkan agar satu fungsi bisa dipanggil dari CPU maupun GPU.

Summary

Model pemrograman CUDA adalah sistem komputasi heterogen yang memisahkan peran antara Host (CPU) sebagai orkestrator dan Device (GPU) sebagai eksekutor paralel masif. Alur kerja utamanya melibatkan Host yang secara eksplisit mengelola alokasi memori dan transfer data ke Device, lalu meluncurkan Kernel (fungsi global yang berjalan secara paralel di ribuan thread) di Device untuk melakukan komputasi berat, dan akhirnya mengambil kembali hasilnya. CUDA C menyediakan ekstensi minimal pada C/C++ untuk memfasilitasi interaksi ini dan memberikan kontrol penuh atas paralelisme.