Back to IF3130 Sistem Paralel dan Terdistribusi

Topic

Questions/Cues

  • Apa itu scope variabel di OpenMP?

  • Beda shared & private?

  • Apa itu race condition agregasi?

  • Bagaimana reduction bekerja?

  • Operator reduction apa saja yang ada?

  • Mengapa default(none) penting?

  • Apa itu data dependency?

Reference Points

  • Slide “paralel programming model: shared memory - OpenMP” (Hal. 25-34, 41-47)

Scope Variabel: Milik Bersama atau Milik Pribadi?

Saat sebuah blok kode dijalankan oleh banyak thread, kita harus menentukan variabel mana yang bisa diakses bersama dan mana yang harus dimiliki secara pribadi oleh setiap thread. Ini disebut scope.

  • shared: Satu variabel asli yang dapat dilihat dan dimodifikasi oleh semua thread. Ini adalah default untuk variabel yang dideklarasikan di luar blok paralel. Potensi race condition sangat tinggi di sini.

  • private: Setiap thread mendapatkan salinan (kopi) variabelnya sendiri yang terpisah. Perubahan yang dibuat oleh satu thread tidak akan terlihat oleh thread lain. Ini adalah default untuk variabel yang dideklarasikan di dalam blok paralel (termasuk variabel iterator loop).

Masalah Klasik: Race Condition pada Agregasi

Bayangkan setiap thread menghitung hasil lokal (my_result) dan mencoba menambahkannya ke hasil global (global_result).

global_result += my_result; // BAHAYA!

Operasi ini tidak atomic (instan). Ia terdiri dari tiga langkah: (1) Baca global_result, (2) Tambahkan my_result, (3) Tulis kembali ke global_result. Jika dua thread melakukannya bersamaan, salah satu pembaruan bisa hilang.

Solusi Buruk: Menggunakan #pragma omp critical untuk melindungi baris ini. Cara ini aman, tetapi sangat lambat karena memaksa semua thread untuk antre (serialisasi), menghilangkan keuntungan dari paralelisasi.

Solusi Elegan: Klausa reduction

reduction adalah cara OpenMP yang paling efisien dan tepat untuk melakukan operasi agregasi secara paralel dan aman.

  • Cara Kerja:

    1. Setiap thread secara otomatis mendapatkan salinan private dari variabel reduksi (misal, approx), yang diinisialisasi sesuai operatornya (0 untuk +, 1 untuk *).

    2. Setiap thread bekerja hanya pada salinan private-nya, tanpa ada race condition.

    3. Setelah semua thread selesai, OpenMP akan mengambil semua nilai dari salinan private dan menggabungkannya menjadi satu nilai pada variabel global asli menggunakan operator yang ditentukan.

Sintaks: reduction(<operator>:<variabel>)

#pragma omp parallel for reduction(+: approx)
for (i = 1; i <= n - 1; i++) {
    approx += f(a + i * h); // Aman, 'approx' di sini adalah private
}
// Di akhir, semua 'approx' private dijumlahkan ke 'approx' global
  • Operator Umum: +, *, -, & (bitwise AND), | (bitwise OR), ^ (bitwise XOR), && (logical AND), || (logical OR). Versi lebih baru juga mendukung min dan max.

Praktik Terbaik: default(none)

Untuk menghindari kesalahan scope yang tidak disengaja, sangat disarankan untuk menggunakan klausa default(none). Klausa ini memaksa programmer untuk secara eksplisit menentukan scope dari setiap variabel yang digunakan di dalam blok paralel.

#pragma omp parallel for default(none) \
                         private(i, factor) \
                         shared(n) \
                         reduction(+: sum)

Compiler akan memberikan error jika ada variabel yang scope-nya belum ditentukan. Ini adalah jaring pengaman yang sangat baik.

Masalah Dependensi Data

reduction hanya bekerja untuk operasi yang asosiatif dan komutatif. Ia tidak bisa menyelesaikan masalah dependensi data antar iterasi. Contohnya adalah pada perhitungan deret Fibonacci: fibo[i] = fibo[i-1] + fibo[i-2]. Nilai fibo[i] bergantung langsung pada hasil iterasi sebelumnya. Memparalelkan loop semacam ini akan menghasilkan jawaban yang salah, bahkan dengan reduction. Tanggung jawab untuk memastikan iterasi independen ada pada programmer.

Summary

Manajemen data yang aman dalam OpenMP bergantung pada pengaturan scope variabel yang benar (shared atau private). Untuk operasi agregasi, klausa reduction adalah mekanisme yang aman dan efisien untuk menghindari race condition dengan memberikan setiap thread salinan privat dari variabel dan menggabungkan hasilnya di akhir. Menggunakan default(none) adalah praktik krusial untuk memaksa deklarasi scope secara eksplisit dan mencegah bug.