Konstruksi do concurrent adalah perulangan Fortran yang iterasinya dinyatakan bebas dari ketergantungan satu sama lain, sehingga kompiler bebas mengeksekusinya secara berurutan, secara vektor, maupun secara paralel tanpa mengubah hasilnya. Topik ini membahas bentuk header konstruksi tersebut, aturan ketergantungan antar-iterasi yang wajib dipatuhi, serta program normalisasi vektor yang dikompilasi dan dijalankan sungguhan dengan gfortran. Setelah topik ini selesai dibaca, batas antara perulangan biasa dan perulangan yang aman diserahkan kepada kompiler untuk diparalelkan sudah terlihat jelas.
Bentuk header DO CONCURRENT#
Header konstruksi ini ditulis do concurrent (i = 1:8) dan ditutup end do, dengan variabel kendali berupa bilangan bulat dan batas berbentuk batas_bawah:batas_atas. Bentuk lengkapnya dapat memuat langkah, misalnya i = 1:8:2 untuk iterasi berselang dua, sedangkan langkah yang tidak ditulis bernilai satu. Header juga dapat memuat kondisi mask (mask), yaitu ekspresi logika dalam tanda kurung siku seperti [v(i) > 0.0], yang hanya menjalankan iterasi ketika kondisinya terpenuhi.
Perbedaan pokoknya dengan perulangan do biasa terletak pada janji yang diberikan penulis kode. Perulangan do menegaskan bahwa iterasi berjalan berurutan sesuai urutan rentangnya, yaitu dari nilai awal menuju nilai akhir, sedangkan do concurrent menegaskan sebaliknya, yaitu urutan eksekusi tidak dipentingkan dan hasil program tidak boleh bergantung padanya. Janji inilah yang membuka peluang bagi kompiler untuk menyusun eksekusi secepat mungkin. Konstruksi ini ditetapkan sejak standar Fortran 2008, sehingga kode yang memakainya tetap Fortran murni tanpa direktif tambahan.
Aturan ketergantungan antar-iterasi#
Aturan utamanya sederhana, yaitu satu iterasi tidak boleh membaca atau menimpa data yang ditulis oleh iterasi lain. Penugasan v(i) = v(i) / norma memenuhi aturan itu karena setiap iterasi hanya menyentuh v(i) sendiri. Sebaliknya, penugasan v(i) = v(i - 1) + 1.0 melanggar aturan yang sama karena iterasi i membaca v(i - 1) yang baru saja diubah oleh iterasi sebelumnya.
! tidak-dijalankan
! iterasi i membaca v(i - 1) yang ditulis iterasi lain, sehingga
! hasilnya bergantung pada urutan eksekusi
do concurrent (i = 2:8)
v(i) = v(i - 1) + 1.0
end doSelain aturan data, badan konstruksi tidak boleh memuat pernyataan yang menghentikan program seperti stop dan tidak boleh bercabang ke luar perulangan. Kompiler tidak wajib mendeteksi setiap pelanggaran, sehingga kode yang melanggar dapat tetap terkompilasi dan menghasilkan keluaran yang salah secara diam-diam. Tanggung jawab memastikan kebebasan antar-iterasi dengan demikian berada pada penulis kode, dan pemeriksaan paling praktis dilakukan dengan membaca setiap variabel di sebelah kanan penugasan, lalu memeriksa apakah ada variabel di antaranya yang ditulis oleh iterasi lain.
Program normalisasi vektor#
Program berikut menormalisasi vektor berisi delapan sudut, yaitu membagi setiap elemen dengan panjang vektornya sehingga jumlah kuadrat elemennya menjadi satu. Norma dihitung lebih dahulu dengan fungsi reduksi sum yang bekerja serial, kemudian perulangan do concurrent membagi setiap elemen dengan norma tersebut. Program dikompilasi dan dijalankan dari direktori topik, dan blok di bawahnya adalah keluaran asli dari eksekusi tersebut.
program normalisasi_vektor
implicit none
real, parameter :: pi = acos(-1.0)
real :: v(8) = [0.0, pi/6.0, pi/4.0, pi/3.0, pi/2.0, 2.0*pi/3.0, 3.0*pi/4.0, pi]
real :: norma
integer :: i
norma = sqrt(sum(v**2))
print '(A, F10.6)', 'norma vektor : ', norma
do concurrent (i = 1:8)
v(i) = v(i) / norma
end do
do i = 1, 8
print '(A, I2, A, F8.4)', 'v(', i, ') = ', v(i)
end do
print '(A, F10.6)', 'kuadrat vektor hasil : ', sum(v**2)
end program normalisasi_vektor# mengompilasi dan menjalankan program dari direktori topik
gfortran normalisasi_vektor.f90 -o normalisasi_vektor
./normalisasi_vektornorma vektor : 4.925726
v( 1) = 0.0000
v( 2) = 0.1063
v( 3) = 0.1594
v( 4) = 0.2126
v( 5) = 0.3189
v( 6) = 0.4252
v( 7) = 0.4783
v( 8) = 0.6378
kuadrat vektor hasil : 1.000000Keluaran di atas menunjukkan perhitungan yang benar, yaitu norma bernilai sekitar 4.925726 dan jumlah kuadrat elemen setelah pembagian bernilai 1.000000. Setiap iterasi do concurrent hanya membaca v(i) dan menulis kembali elemen yang sama, sehingga tidak ada satu pun ketergantungan antar-iterasi pada program ini. Bila kompiler memilih mengeksekusi perulangan itu secara paralel, hasil keluarannya tetap sama persis.
Eksekusi berurutan pada gfortran dan peluang paralelnya#
Secara default, gfortran mengeksekusi do concurrent berurutan, satu iterasi demi satu iterasi, sehingga program di atas berjalan seolah-olah ditulis dengan do biasa. Pernyataan kebebasan antar-iterasi tetap memberi manfaat pada tahap optimasi, karena opsi seperti -ftree-parallelize-loops memungkinkan gfortran memparalelkan perulangan yang aman tanpa mengubah satu baris kode pun. Kompiler lain melangkah lebih jauh, misalnya dengan memetakan konstruksi ini ke akselerator GPU, dan seluruh pilihan itu sah menurut semantik bahasa.
Kedudukan do concurrent dapat dibandingkan dengan direktif OpenMP yang dibahas pada topik direktif OpenMP di modul ini. Konstruksi do concurrent merupakan bagian bahasa sehingga portabel ke kompiler mana pun tanpa pustaka tambahan, sedangkan OpenMP memberi kendali yang lebih rinci, misalnya pengaturan jumlah thread dan skema pembagian kerja. Untuk perulangan yang aturan datanya bersih, keduanya mengekspresikan maksud yang sama, yaitu iterasi yang boleh berjalan serentak.
Kesalahan umum#
Kesalahan pertama adalah menganggap do concurrent pasti berjalan paralel. Konstruksi ini hanya memberi izin, bukan perintah, sehingga kenaikan kinerja tidak dapat dijanjikan dan pengukuran tetap diperlukan sebelum menyimpulkan apa pun. Kode yang benar secara semantik tetap mungkin berjalan berurutan pada kompiler yang tidak memanfaatkannya.
Kesalahan kedua adalah menyisipkan ketergantungan yang tersembunyi, misalnya mengakumulasi hasil ke satu variabel bersama di dalam badan perulangan. Pola itu berarti membiarkan banyak iterasi menimpa satu lokasi memori, dan akibatnya dibahas rinci pada topik Jebakan paralel di modul ini. Kesalahan ketiga adalah memindahkan perhitungan yang memang bergantung urutan, seperti relasi yang menghitung elemen dari elemen sebelumnya, ke dalam do concurrent hanya agar tampak modern, karena hasilnya berubah menjadi salah secara diam-diam.
Ringkasan#
do concurrentmenyatakan perulangan yang iterasinya bebas, dengan header berbentukdo concurrent (i = 1:8)yang dapat memuat langkah serta kondisi mask dalam tanda kurung siku.- Aturan utamanya adalah tiap iterasi tidak boleh membaca atau menimpa data yang ditulis iterasi lain, dan badan perulangan tidak boleh memuat
stopmaupun percabangan keluar. - gfortran mengeksekusinya secara berurutan secara default, sedangkan kompiler yang lain boleh memparalelkannya atau memetanya ke GPU tanpa perubahan kode.
Referensi#
- Learn: tutorial resmi Fortran — fortran-lang — Kumpulan tutorial resmi komunitas Fortran yang menjadi rujukan fitur bahasa pada jalur ini.
- The GNU Fortran Compiler — GNU Project — Manual gfortran, kompiler yang digunakan untuk mengompilasi dan menjalankan contoh pada topik ini.