ÖNEMLİ : Kendim için aldığım notlar. Umarım size de bir faydası olur. Kullanılan her bir makale referans olarak eklenmiştir. Rice Üniversitesi’nin hazırladığı eğitsel bir Framework olan PCDP bu ve sonraki bölümlerde kullanılacaktır. async ve finish notasyonları bu Framework’de yer almaktadır.
Java Paralel Programlama Serisi
Genel Bakış
Bir önceki bölümde gördüğümüz async
ve finish
notasyonları eğitsel ve gösterimsel kavramlardı. Fakat Java’daki karşılıkları tabii ki bu şekilde değildir. Bu bölümde ise aynı konseptin Java’daki karşılıklarına bakacağız. Java’da çok çekirdekli paralellikten yararlanmanın en popüler yollarından biri olan Java Fork/Join Framework‘üdür.
Öyleyse array-sum örneğimize geri dönelim. Ve önce bir “böl ve fethet algoritmasına” kadar uzatabilir miyiz bir bakalım. Şimdi, daha nesne odaklı düşünelim ve bir sınıfımız olduğunu söyleyelim. Bu sınıfımızın adı ASUM olsun.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
CLASS ASUM {
A, // an array
LO, // lower bound
UP, // upper bound
SUM;
//compute() metodu, aslında alt ve üst sınırlar ve
//array göz önüne alındığında toplamı hesaplayacak
//bir hesaplama yöntemimizdir.
COMPUTE(){
// Örneğin, eğer LO, UP ile aynıysa, sum değerini
//array LO'ya ata, ki set high ile aynı yaparız.
IF(LO == UP) SUM = A[LO];
ELSE IF (LO>UP) SUM = 0;
ELSE {
MID = (LO+UP)/2;
L = NEW ASUM(A,LO,MID);
R = NEW ASUM(A,MID+1,UP);
L.COMPUTE();
R.COMPUTE();
SUM = L.SUM + R.SUM;
}
}
}
Hatırlatma
Bu bölümde, async, finish ve Fork/Join kavramlarını deneysel anlatmak için böl ve fethet algoritması(divide and conquer algorithm) kullanılacaktır. Bu algoritmanın arkasındaki temel fikir, sorunu ikiye bölmektir. Amacımız algoritmanın detayına girmek değil. Temel hedefimiz, bu algoritmayı nasıl paralel hale getirebiliriz? Algoritma ile ilgili daha detaylı bilgiye ulaşmak için bu linki ziyaret edebilirsiniz.
Yukarıda görüleceği üzere L.COMPUTE() ve R.COMPUTE() olarak ifade edilmiş 2 işlem bulunmaktadır. Biz bu işlemlerden herhangi birini, örneğin L.COMPUTE() olanı asenkron olarak işaretlersek paralellik sağlamış oluruz. finish
notasyonu ile de bu iki işlemi bir kapsam içine alırsak, iki işlemin nihayete ereceği garanti altına alınmış olur. Böylelikle iki işlem bitmeden SUM işlemine geçilemez.
1
2
3
4
5
FINISH {
ASYNC L.COMPUTE() // L.FORK async ile aynı işi yapar.
R.COMPUTE()
}
SUM = L.SUM + R.SUM;
finish
ise join ile aynı işi yapar. Tek fark, finish scope içindeki bütün işlerin bitmesini beklerken, join sadece bir işe odaklanır. Benzer işlem async
ve finish
notasyonları yerine fork/join kullanılarak ise şu şekilde yapılır.
1
2
3
4
5
L.COMPUTE();
L.FORK;
R.COMPUTE()
L.JOIN();
SUM = L.SUM + R.SUM;
Fork/Join görevleri, java iş parçacığı havuzu olan bir “ForkJoinPool” içinde yürütülür. Bu havuz, hem fork hem de join işlemlerini paralel bir dizi görevi yürüterek ve bunların tamamlanmasını bekleyerek birleştiren invokeAll() yöntemini destekler. Örneğin, ForkJoinTask.invokeAll (sol, sağ), örtülü olarak sol ve sağda fork() işlemlerini gerçekleştirir, ardından her iki nesnede de join() işlemi gerçekleştirir.
Bu Framework’de, kullanıcının kendi oluşturacağı bir sınıf, FJ Framework‘ü içinde bulunan RecursiveAction sınıfı extends edilip, bu sınıfın yerleşik metodlarından biri olan compute() metodu override edildikten sonra, metodun içinde istenilen görev belirlenebilir.
1
2
3
4
5
6
7
8
9
10
11
12
private static class ASum extends RecursiveAction {
int[] A; // input array
int LO, HI; // subrange
int SUM; // return value
...
@Override
protected void compute() {
SUM = 0;
for (int i = LO; i <= HI; i++) SUM += A[i];
} // compute()
}