Bölüm 10: Paralel Algoritmalar
Giriş
Öğrenme Çıktıları
Paralel algoritmanın böl–ata–birleştir yapısını, avantaj ve zorluklarını (iletişim, senkronizasyon, yük dengeleme) açıklamak.
Flynn sınıflarını ve PRAM modelini tanımak; hızlanma, verim, maliyet, iş ve derinliği hesaplamak; Amdahl yasasını uygulamak.
PRAM ağaç toplamını, paralel önek toplamını, boru hattını ve boru hatlı ağacı adım adım yürütmek ve adım sayılarını bulmak.
Fork/Join ile paralel birleştirme sıralaması, MapReduce ve veri paralelliği örneklerini okumak; yarış durumlarını fark etmek.
Giriş
Birlikte Daha Hızlı
- Bazı işleri bitirmek için çok fazla zaman gerekir: büyük veri analizi, bilimsel simülasyon, görüntü işleme.
- Paralel algoritmalar (parallel algorithms) bir problemi aynı anda birden fazla işlemci (çekirdek) kullanarak çözer.
- Karmaşık görevler çok daha kısa sürede tamamlanabilir.
- Tek çekirdeğin saat hızı artık neredeyse artmıyor; performans artışı çekirdek sayısından geliyor.
Giriş
Paralel Algoritmalar
- Bir grup insanın birlikte çalışmasına benzer.
- Karmaşık problemi daha küçük parçalara ayırır.
- Parçaları aynı anda farklı işlemcilere atayarak işi hızlandırır.
- İşlemciler görevlerini bitirince sonuçlar birleştirilir.
- Yüksek başarımlı hesaplamanın (HPC) temelidir.
- Büyük veri analizi, bilimsel simülasyon, makine öğrenmesi gibi alanlarda kullanılır.
Giriş
Dezavantajlar ve Zorluklar
Kazanımlar
- Aynı işi daha kısa sürede bitirmek (hızlanma)
- Tek makineye sığmayan büyük problemleri çözebilmek (ölçeklenebilirlik)
- Donanımı (tüm çekirdekleri, GPU'yu) verimli kullanmak
Dezavantajlar
- Problemi parçalara ayırmak ve birleştirmek ek çaba gerektirir
- İletişim ve senkronizasyon ek yük getirir
- Yük dengeleme: tüm işlemcilere eşit iş düşmeli, yeterli işlem gücü olmalı
- Hata ayıklama zordur (yarış durumu, kilitlenme)
- Bazı problemler yapısı gereği sıralıdır
Giriş
Uygulama Alanları
Bilimsel hesaplamanın temel işlemi; sonuç matrisinin her satırı (hatta her hücresi) bağımsız hesaplanabilir.
Kenar bulma, filtreleme, nesne tanıma: her piksel ya da blok için aynı işlem (veri paralelliği, GPU).
Uçak, gemi, araç tasarımında akış simülasyonu: alan ızgaraya bölünür, her bölge bir işlemcide.
Popülasyondaki bireylerin uygunluğu birbirinden bağımsız ve paralel değerlendirilir (çizelgeleme, rotalama).
Modeller ve Ölçütler
Flynn Sınıflandırması
| Sınıf | Açılım | Anlamı | Örnek |
|---|---|---|---|
| SISD | Single Instruction, Single Data | tek komut akışı, tek veri | klasik tek çekirdekli işlemci |
| SIMD | Single Instruction, Multiple Data | aynı komut, birçok veri üzerinde aynı anda | vektör komutları (AVX), GPU |
| MISD | Multiple Instruction, Single Data | farklı komutlar aynı veri üzerinde | nadir; hata toleranslı sistemler |
| MIMD | Multiple Instruction, Multiple Data | her işlemci kendi programını kendi verisinde | çok çekirdekli CPU, kümeler |
Modeller ve Ölçütler
Bellek Mimarileri
- Paylaşımlı bellek (shared memory): tüm işlemciler aynı adres uzayını görür. İletişim ucuzdur; eşzamanlı erişim senkronizasyon ister.
- Java iş parçacıkları,
ForkJoinPool, OpenMP.
- Dağıtık bellek (distributed memory): her düğümün kendi belleği vardır; veri mesajla taşınır.
- Ölçeklenir ama iletişim pahalıdır: MPI, Hadoop MapReduce.
Modeller ve Ölçütler
PRAM Modeli
- PRAM (Parallel Random Access Machine): paralel algoritmaların kuramsal modeli; sıralı RAM modelinin paralel karşılığı.
- p işlemci, ortak bir bellek ve ortak saat: işlemciler adımları eş zamanlı (lockstep) atar.
- Her adımda bir işlemci: bellekten oku → yerel hesapla → belleğe yaz. Her biri O(1) sürer.
- İletişim ve senkronizasyon maliyeti yok sayılır; bu yüzden algoritmanın içsel paralelliğini ölçer.
| Tür | Okuma | Yazma |
|---|---|---|
| EREW | dışlayıcı | dışlayıcı |
| CREW | eşzamanlı | dışlayıcı |
| CRCW | eşzamanlı | eşzamanlı |
Modeller ve Ölçütler
Hızlanma, Verim, Maliyet
| Ölçüt | Tanım |
|---|---|
| T₁ | en iyi sıralı algoritmanın süresi |
| Tₚ | p işlemcideki paralel süre |
| Hızlanma (speedup) | S = T₁ / Tₚ (ideal: S = p) |
| Verim (efficiency) | E = S / p (0 < E ≤ 1) |
| Maliyet (cost) | C = p · Tₚ |
S = 100 / 16 = 6,25
E = 6,25 / 8 ≈ 0,78
C = 8 · 16 = 128 s (> 100 s)
- Maliyet-optimal: C = Θ(T₁).
- S < p olmasının nedenleri: iletişim, bekleme, yük dengesizliği, sıralı kısımlar.
Modeller ve Ölçütler
Amdahl Yasası
- Programın f kesri paralelleştirilebilir, 1 − f kesri sıralıdır.
- S(p) = 1 / ((1 − f) + f / p)
- p → ∞ iken S → 1 / (1 − f): sıralı kısım hızlanmayı sınırlar.
- f = 0,9, p = 8: S = 1 / (0,1 + 0,1125) ≈ 4,71; sınır 10.
Modeller ve Ölçütler
İş ve Derinlik
- İş (work) W: tüm işlemlerin toplam sayısı = T₁ (tek işlemcide süre).
- Derinlik (depth, span) D: en uzun bağımlılık zinciri = T∞ (sınırsız işlemcide süre).
- Paralellik = W / D: yararlı kullanılabilecek en fazla işlemci sayısı.
- Alt sınırlar: Tₚ ≥ W / p ve Tₚ ≥ D.
- Brent teoremi: Tₚ ≤ W / p + D.
- İş-verimli (work-efficient): W, en iyi sıralı algoritmayla aynı mertebede.
W = n − 1 toplama, D = log₂ n tur.
n = 1024: W = 1023, D = 10, paralellik ≈ 102.
p = 32 için Brent: T₃₂ ≤ 1023/32 + 10 ≈ 42 adım.
PRAM ile Toplama
Problem: Paralel Toplama
- Girdi: n sayı; çıktı: toplamları. Sıralı algoritma n − 1 toplama yapar: O(n).
- Sıralı döngüde her toplama bir öncekini bekler: zincir uzunluğu n − 1, paralellik yok.
- Toplama birleşmeli (associative) olduğu için parantezleri değiştirebiliriz:
((2 + 3) + (5 + 1)) + ((7 + 6) + (8 + 4)) → dengeli bir ikili ağaç. - Ağacın her düzeyindeki toplamlar birbirinden bağımsızdır → aynı turda paralel yapılır.
- Aynı fikir her birleşmeli işlem için çalışır: max, min, çarpım, AND, OR (indirgeme, reduction).
PRAM ile Toplama · Adım adım
PRAM Toplama Adım Adım
Başlangıç: 8 sayı 8 işlemciye (p₀ … p₇) birer birer dağıtılır. Kesikli kutular, sonraki turlarda yazılacak hücrelerdir.
Tur 1: 4 işlemci aynı anda komşu çiftleri toplar: 2+3=5, 5+1=6, 7+6=13, 8+4=12.
Tur 2: 2 işlemci, aradaki uzaklık 2 olan kısmi toplamları birleştirir: 5+6=11, 13+12=25.
Tur 3: p₀, 11+25 = 36 sonucunu bulur. 8 sayı log₂ 8 = 3 turda toplandı (sıralı: 7 adım).
PRAM ile Toplama · Kodu adım adım çalıştır
Ağaç Toplamı: Kodu Adım Adım İzle
Dizi oluşturulur; n = 8. Her hücreyi bir işlemcinin tuttuğunu düşünün.
Yeni tur: adim = 1. Bu turda 4 toplama paralel yapılır.
a[0] = a[0] + a[1] = 2 + 3 = 5.
a[2] = a[2] + a[3] = 5 + 1 = 6.
a[4] = a[4] + a[5] = 7 + 6 = 13.
a[6] = a[6] + a[7] = 8 + 4 = 12.
Yeni tur: adim = 2. Bu turda 2 toplama paralel yapılır.
a[0] = a[0] + a[2] = 5 + 6 = 11.
a[4] = a[4] + a[6] = 13 + 12 = 25.
Yeni tur: adim = 4. Bu turda 1 toplama paralel yapılır.
a[0] = a[0] + a[4] = 11 + 25 = 36.
adim = 8 → döngü biter. Toplam a[0] = 36; 3 tur, 7 toplama.
int[] a = {2, 3, 5, 1, 7, 6, 8, 4}; int n = a.length; for (int adim = 1; adim < n; adim *= 2) { // aynı turdaki tüm i'ler paralel for (int i = 0; i + adim < n; i += 2 * adim) a[i] = a[i] + a[i + adim]; } // tur sonu: senkronizasyon // toplam a[0]'da
PRAM ile Toplama
PRAM Toplama: Analiz
| Ölçüt | n işlemci (n/2 yeterli) | p = n / log₂ n işlemci |
|---|---|---|
| Süre Tₚ | log₂ n | O(log n) |
| İş W | n − 1 | n − 1 |
| Maliyet p · Tₚ | Θ(n log n) | Θ(n) |
| Maliyet-optimal mi? | hayır | evet |
- Turlar ilerledikçe çalışan işlemci sayısı yarıya iner (4, 2, 1): çoğu işlemci boşta bekler, maliyet yüksektir.
- Çözüm: her işlemci önce kendi log₂ n'lik bloğunu sıralı toplar (O(log n)), sonra n / log₂ n kısmi toplam ağaçla birleştirilir (O(log n)).
- EREW yeterlidir: bir turda her hücreyi tek işlemci okur/yazar. Her tur sonunda bir senkronizasyon (bariyer) gerekir.
PRAM ile Toplama
Paralel Önek Toplamı
- Önek toplamı (prefix sum, scan): yᵢ = x₀ + x₁ + … + xᵢ.
- Sıralı: yᵢ = yᵢ₋₁ + xᵢ, O(n) ama tamamen zincirleme.
- Hillis–Steele: d = 1, 2, 4, … için her i ≥ d paralel olarak x[i] += x[i − d].
- D = ⌈log₂ n⌉ tur, W = O(n log n) → iş-verimli değil.
- Blelloch algoritması (yukarı/aşağı süpürme) W = O(n), D = O(log n).
- Kullanım: paralel sıralama, sıkıştırma (filter), Java'da
Arrays.parallelPrefix.
PRAM ile Toplama · Adım adım
Hillis–Steele Önek Toplamı Adım Adım
Girdi: 2 3 5 1 7 6 8 4. Amaç: her i için x₀ + … + xᵢ (kapsayıcı önek toplamı).
d = 1: i ≥ 1 olan her işlemci kendi değerine solundakini ekler (7 toplama, aynı anda).
d = 2: i ≥ 2 için 2 soldakini ekler. Artık her hücre 4 elemanlık pencerenin toplamıdır.
d = 4: sonuç 2 5 10 11 18 24 32 36. log₂ 8 = 3 turda bitti; ama toplam 7+6+4 = 17 toplama yapıldı.
Boru Hattı
Boru Hattı Fikri
- Boru hattı (pipeline): iş, ardışık aşamalara bölünür; her aşama bir işlemcidir.
- Fabrika montaj hattı gibi: bir iş 2. aşamaya geçerken 1. aşama bir sonraki işe başlar.
- Tek bir işin süresi kısalmaz; ama birim zamanda biten iş (throughput) artar.
- k aşama, m iş: sıralı k · m adım; boru hattıyla k + m − 1 adım.
- Başta hat dolarken ve sonda boşalırken bazı aşamalar boştur.
3 aşama, 4 iş: 3 · 4 = 12 yerine 3 + 4 − 1 = 6 adım.
Boru Hattı
Boru Hattı Problemi
- 5 işlemci p₀ … p₄ doğrusal bir zincir oluşturur; her işlemci yalnızca sağ komşusuna mesaj gönderebilir.
- Her pj, 4 değerlik bir sütun tutar (satır 0 … 3).
- Amaç: her satır r için pj'deki değer, p₀ … pj'nin o satırdaki değerlerinin toplamı olsun (satır bazında önek toplamı). Böylece p₄ her satırın toplamını tutar.
- Bir adımda her işlemci bir değer gönderir; alıcı onu kendi değerine ekler.
- Saf yöntem: satır 0'ı zincir boyunca taşı (4 adım), sonra satır 1'i… → 4 · 4 = 16 adım.
- Boru hattı: pj, satır r'yi t = r + j anında gönderir; satırlar zincirde arka arkaya akar.
Boru Hattı · Adım adım
Boru Hattı Adım Adım: 5 İşlemci, 4 Satır
Her pj 4 satırlık bir sütun tutar. Hedef: her satırda pj, p₀ … pj değerlerinin toplamını tutsun. p₀'ın değerleri zaten sonuçtur.
Adım 1: p₀→p₁ satır 0: 2+3=5.
Adım 2: p₀→p₁ satır 1: 1+7=8, p₁→p₂ satır 0: 5+3=8.
Adım 3: p₀→p₁ satır 2: 7+2=9, p₁→p₂ satır 1: 8+4=12, p₂→p₃ satır 0: 8+6=14.
Adım 4: p₀→p₁ satır 3: 4+9=13, p₁→p₂ satır 2: 9+7=16, p₂→p₃ satır 1: 12+1=13, p₃→p₄ satır 0: 14+8=22.
Adım 5: p₁→p₂ satır 3: 13+3=16, p₂→p₃ satır 2: 16+0=16, p₃→p₄ satır 1: 13+6=19.
Adım 6: p₂→p₃ satır 3: 16+2=18, p₃→p₄ satır 2: 16+3=19.
Adım 7: p₃→p₄ satır 3: 18+5=23. Bitti: 7 adım; p₄ satır toplamlarını (22 19 19 23) tutar.
Boru Hattı
Boru Hattı: Analiz
| adım | p₀→p₁ | p₁→p₂ | p₂→p₃ | p₃→p₄ |
|---|---|---|---|---|
| 1 | satır 0 | — | — | — |
| 2 | satır 1 | satır 0 | — | — |
| 3 | satır 2 | satır 1 | satır 0 | — |
| 4 | satır 3 | satır 2 | satır 1 | satır 0 |
| 5 | — | satır 3 | satır 2 | satır 1 |
| 6 | — | — | satır 3 | satır 2 |
| 7 | — | — | — | satır 3 |
for (int t = 0; t < m + p - 2; t++) // tüm j'ler aynı anda for (int j = 0; j < p - 1; j++) { int r = t - j; if (r >= 0 && r < m) x[j + 1][r] += x[j][r]; }
- Adım sayısı: m + p − 2 = 4 + 5 − 2 = 7 (saf: m(p − 1) = 16).
- Verim: m(p − 1) toplama / ((p − 1)(m + p − 2)) = m / (m + p − 2) = 4/7 ≈ %57.
- m ≫ p iken verim → 1.
Boru Hatlı Ağaç
Ağaçta İndirgeme
- İşlemciler bir ağaç biçiminde bağlıdır; her düğüm değerini ebeveynine gönderir, ebeveyn kendi değerine ekler. Sonuç kökte toplanır.
- Ağacın derinliği h ise tek bir değerin indirgenmesi h tur sürer (örneğimizde h = 3).
- Şimdi her işlemcide tek sayı değil, k bileşenli bir vektör var (örnekte k = 3: yeşil, mavi, kırmızı). Her bileşen ayrı ayrı toplanacak.
- Saf yöntem: bileşenleri sırayla indirge → k · h = 9 tur.
- Boru hatlı ağaç (pipelined tree): bir bileşen yukarı çıkarken bir sonraki bileşen yapraklardan yola çıkar.
- Kural: bir düğüm bir turda en fazla bir mesaj alabilir (aynı anda bir mesaj da gönderebilir).
Boru Hatlı Ağaç · Adım adım
Boru Hatlı Ağaç Adım Adım
7 işlemcinin her biri 3 değerlik bir vektör tutar (yeşil, mavi, kırmızı). Hedef: üç bileşenin toplamı kökte (p₀) toplansın. Değerler alttan köke doğru akar.
Tur 1: yeşil p₃→p₂: 9+8=17, p₄→p₁: 7+3=10, p₆→p₅: 2+3=5.
Tur 2: yeşil p₂→p₁: 17+10=27, p₅→p₀: 5+3=8.
Tur 3: yeşil p₁→p₀: 27+8=35; mavi p₃→p₂: 5+5=10, p₄→p₁: 5+7=12, p₆→p₅: 1+4=5.
Tur 4: mavi p₂→p₁: 10+12=22, p₅→p₀: 5+6=11.
Tur 5: mavi p₁→p₀: 22+11=33; kırmızı p₃→p₂: 8+3=11, p₄→p₁: 2+5=7, p₆→p₅: 7+5=12.
Tur 6: kırmızı p₂→p₁: 11+7=18, p₅→p₀: 12+1=13.
Tur 7: kırmızı p₁→p₀: 18+13=31. Kökte (35, 33, 31): 7 turda bitti.
- Kenarın rengi, o turda taşınan bileşeni gösterir; ok ebeveyne doğrudur.
- Kalın çerçeveli kutu, bu turda güncellenen değerdir.
- Her bileşen 3 aşamada köke çıkar:
(1) p₃→p₂, p₄→p₁, p₆→p₅
(2) p₂→p₁, p₅→p₀
(3) p₁→p₀. - Yeni bileşen 2 turda bir başlar.
Boru Hatlı Ağaç
Boru Hatlı Ağaç: Analiz
| tur | aşama 1 p₃→p₂, p₄→p₁, p₆→p₅ | aşama 2 p₂→p₁, p₅→p₀ | aşama 3 p₁→p₀ |
|---|---|---|---|
| 1 | yeşil | — | — |
| 2 | — | yeşil | — |
| 3 | mavi | — | yeşil |
| 4 | — | mavi | — |
| 5 | kırmızı | — | mavi |
| 6 | — | kırmızı | — |
| 7 | — | — | kırmızı |
- Neden 2 turda bir? p₁ iki çocuğundan (p₄ ve p₂) mesaj alır; bir turda tek mesaj alabildiği için bir bileşen p₁'i 2 tur meşgul eder. p₀ için de aynısı.
- Toplam tur: h + (k − 1) · 2 = 3 + 2 · 2 = 7 (saf: k · h = 9).
- k büyüdükçe: 2k + 1 ≈ 2k tur; saf yöntem 3k. Genel olarak tur ≈ h + (k − 1) · (en fazla çocuk sayısı).
- Sonuç kökte: yeşil 35, mavi 33, kırmızı 31.
Paralel Algoritma Türleri
Paralel Algoritma Türleri
Problem bağımsız alt problemlere bölünür; alt problemler farklı çekirdeklerde aynı anda çözülür, sonuçlar birleştirilir. Paralel birleştirme sıralaması, hızlı sıralama, matris çarpımı.
Büyük veri dağıtık düğümlerde işlenir: map adımı ara anahtar–değer çiftleri üretir, reduce adımı bunları birleştirir. Hadoop, Spark; sayma, dizinleme, günlük analizi.
Aynı hesaplama verinin farklı parçalarına aynı anda uygulanır (SIMD komutları, GPU). Bilimsel simülasyon, görüntü işleme, sayısal hesaplama.
Paralel Algoritma Türleri
Paralel Birleştirme Sıralaması
class SiralamaGorevi extends RecursiveTask<int[]> { private final int[] dizi; SiralamaGorevi(int[] dizi) { this.dizi = dizi; } protected int[] compute() { if (dizi.length <= 1) return dizi; int orta = dizi.length / 2; var sol = new SiralamaGorevi( Arrays.copyOfRange(dizi, 0, orta)); var sag = new SiralamaGorevi( Arrays.copyOfRange(dizi, orta, dizi.length)); sol.fork(); // paralel başlat sag.fork(); return birlestir(sol.join(), sag.join()); } } // kullanım: int[] s = new ForkJoinPool().invoke( new SiralamaGorevi(dizi));
RecursiveTask: sonuç döndüren Fork/Join görevi.fork(): alt görevi havuza verir (asenkron).join(): alt görevin bitmesini bekler, sonucunu alır.birlestir: bilinen sıralı birleştirme, O(n).ForkJoinPooliş çalma (work stealing) ile boşta kalan çekirdeğe iş verir.
Paralel Algoritma Türleri · Adım adım
Paralel Birleştirme Sıralaması: 2 3 5 1 7 6 8 4
Görev: 8 elemanlı diziyi sırala. SiralamaGorevi kök görevdir.
Görev diziyi ikiye böler ve iki alt görevi fork() ile havuza verir; iki yarı aynı anda işlenebilir.
Her alt görev de böler: artık 4 görev paralel çalışabilir.
Taban durumu: 1 elemanlı diziler zaten sıralıdır (8 yaprak görev).
join(): her ebeveyn iki çocuğunun sonucunu bekler ve birleştirir: [2 3] [1 5] [6 7] [4 8].
Bir üst düzeyde iki birleştirme paralel yapılır: [1 2 3 5] ve [4 6 7 8].
Kök görev son birleştirmeyi tek başına yapar: [1 2 3 4 5 6 7 8]. Bu sıralı birleştirme, derinliği (span) Θ(n) yapar.
Paralel Algoritma Türleri
Paralel Sıralama: Analiz
| Sürüm | İş W | Derinlik D | Paralellik W / D |
|---|---|---|---|
| Sıralı birleştirme sıralaması | Θ(n log n) | Θ(n log n) | 1 |
| Paralel özyineleme + sıralı birleştirme | Θ(n log n) | Θ(n) | Θ(log n) |
| Paralel özyineleme + paralel birleştirme (CLRS) | Θ(n log n) | Θ(log³ n) | Θ(n / log² n) |
- Derinlik yinelemesi: D(n) = D(n/2) + Θ(n) = Θ(n), çünkü iki yarı paralel ama birleştirme sıralıdır.
- Paralellik yalnızca Θ(log n): n = 10⁶ için ≈ 20 çekirdekten fazlası pek fayda etmez.
- Uygulamada: küçük parçalarda (örn. < 1000 eleman) sıralı sıralamaya geçin; aksi hâlde görev oluşturma yükü kazancı yer.
- Hazır çözüm:
Arrays.parallelSort(dizi).
Paralel Algoritma Türleri
MapReduce
- Map: her parça bağımsız işlenir ve ara anahtar–değer çiftleri üretir (kelime → adet).
- Karıştır (shuffle): aynı anahtarlar aynı reducer'a toplanır.
- Reduce: her anahtarın değerleri birleştirilir (toplanır).
- Örnekte: 13 kelime, 4 iş parçacığı → parça boyu ⌊13/4⌋ = 3; son parça 4 kelime.
Paralel Algoritma Türleri
MapReduce: Kod
// Map: bir parçadaki kelimeleri say protected Map<String, Integer> compute() { Map<String, Integer> sonuc = new HashMap<>(); for (String kelime : parca) sonuc.merge(kelime, 1, Integer::sum); return sonuc; } // Ana akış for (MapGorevi g : mapGorevleri) g.fork(); List<Map<String, Integer>> ara = new ArrayList<>(); for (MapGorevi g : mapGorevleri) ara.add(g.join()); // Reduce: ara sonuçları birleştir Map<String, Integer> toplam = new HashMap<>(); for (Map<String, Integer> m : ara) m.forEach((k, v) -> toplam.merge(k, v, Integer::sum));
- Her map görevinin kendi HashMap'i vardır: paylaşılan durum yok, kilit gerekmez.
- Sonuç: apple 5, banana 5, orange 2, cherry 1.
- Bu örnekte reduce tek görevdir; gerçek MapReduce'ta anahtarlar birçok reducer'a bölüştürülür (hash(k) mod r).
Paralel Algoritma Türleri
Veri Paralelliği: Matris Çarpımı
// her satır ayrı bir görev class SatirGorevi extends RecursiveAction { protected void compute() { for (int j = 0; j < n; j++) { C[satir][j] = 0; for (int k = 0; k < n; k++) C[satir][j] += A[satir][k] * B[k][j]; } } } try (var havuz = new ForkJoinPool()) { for (int i = 0; i < n; i++) havuz.submit(new SatirGorevi(i)); } // close(): tüm görevleri bekler
| C = A · B | ||
|---|---|---|
| 30 | 24 | 18 |
| 84 | 69 | 54 |
| 138 | 114 | 90 |
- A = [[1,2,3],[4,5,6],[7,8,9]], B = [[9,8,7],[6,5,4],[3,2,1]].
- Satırlar bağımsız: n görev, her biri Θ(n²) iş. W = Θ(n³), D = Θ(n²) (satır başına).
- Her görev yalnızca kendi satırına yazar → yarış yok.
Paralel Algoritma Türleri
Yarış Durumu
İki iş parçacığı paylaşılan sayac'ı aynı anda artırır. sayac++ aslında üç adımdır: oku → artır → yaz.
| zaman | İş parçacığı 1 | İş parçacığı 2 | sayac |
|---|---|---|---|
| 1 | oku (0) | 0 | |
| 2 | oku (0) | 0 | |
| 3 | yaz 0 + 1 | 1 | |
| 4 | yaz 0 + 1 | 1 |
Beklenen 2, sonuç 1: kayıp güncelleme (lost update).
- Yarış durumu (race condition): sonuç, iş parçacıklarının zamanlamasına bağlıdır; hata her çalıştırmada ortaya çıkmayabilir.
- Çözümler:
synchronizedblok / kilit,AtomicInteger.incrementAndGet(), eşzamanlı koleksiyonlar (ConcurrentHashMap). - En iyisi: paylaşılan durumdan kaçının. Her görev kendi kısmi sonucunu üretsin, sonra birleştirin (indirgeme, MapReduce).
- Kilitler de bedelsiz değildir: sıralı bölge oluşturur (Amdahl) ve kilitlenme (deadlock) riski getirir.
Paralel Algoritma Türleri
Eşzamanlı Çizge Gezinme
- Fikir: her ziyaret edilmemiş komşu için yeni bir gezinme görevi
fork()edilir; alt ağaçlar paralel gezilir. - Görevler
ziyaretkümesini paylaşır. Bu küme sıradan birHashSetolursa eşzamanlı erişimde güvenli değildir. - Ayrıca
contains+addiki ayrı adımdır: iki görev aynı düğümü birlikte ziyaret edebilir.
Set<Dugum> ziyaret = ConcurrentHashMap.newKeySet(); if (!ziyaret.add(komsu)) continue; // atomik kontrol+ekle
Düzey eşzamanlı BFS: her düzeyin tüm düğümleri paralel genişletilir, düzey sonunda bariyer; D = O(çap).
Paralel Algoritma Türleri
Java'da Paralellik Araçları
| Araç | Ne zaman? |
|---|---|
Thread, Runnable | en alt düzey; elle yönetim, nadiren doğrudan kullanılır |
ExecutorService | bağımsız görevleri sabit bir iş parçacığı havuzunda çalıştırmak |
ForkJoinPool, RecursiveTask | böl ve fethet; iş çalma ile özyinelemeli görevler |
list.parallelStream() | veri paralel map / filter / reduce (ortak ForkJoinPool kullanır) |
Arrays.parallelSort | büyük dizileri paralel sıralamak |
Arrays.parallelPrefix | paralel önek toplamı (scan) |
AtomicInteger, ConcurrentHashMap | paylaşılan sayaç/küme gerektiğinde güvenli erişim |
Map<String, Long> adet = kelimeler.parallelStream()
.collect(Collectors.groupingByConcurrent(k -> k, Collectors.counting()));Özet
Özet
| Konu | Hatırlanacaklar |
|---|---|
| Fikir | böl → işlemcilere ata → aynı anda çöz → birleştir; bedeli iletişim, senkronizasyon, yük dengeleme |
| Modeller | Flynn: SISD/SIMD/MISD/MIMD; PRAM: EREW, CREW, CRCW; paylaşımlı / dağıtık bellek |
| Ölçütler | S = T₁/Tₚ, E = S/p, C = p·Tₚ; Amdahl S ≤ 1/(1 − f); Brent Tₚ ≤ W/p + D |
| PRAM toplama | log₂ n tur, W = n − 1; p = n/log n ile maliyet-optimal |
| Önek toplamı | Hillis–Steele: log₂ n tur, O(n log n) iş; Blelloch: O(n) iş |
| Boru hattı | p işlemci, m satır: m + p − 2 adım (saf m(p − 1)) |
| Boru hatlı ağaç | h + (k − 1)·2 tur (örnek 7; saf k·h = 9) |
| Java | Fork/Join (fork, join), MapReduce, satır başına görev; paylaşılan durumda yarış → atomik/eşzamanlı yapılar |
Özet
Sık Yapılan Hatalar
ConcurrentHashMap, AtomicInteger ya da yerel sonuç + birleştirme kullanın.fork() yapmak, görev oluşturma yükünü artırır. Bir eşiğin altında sıralı çalışın.Özet
Örnek Problemler
- n = 16 sayı PRAM ağacıyla toplanıyor. Kaç tur sürer, ilk turda kaç işlemci çalışır, toplam kaç toplama yapılır?
- Boru hattında p = 5 işlemci ve m = 100 satır varsa kaç adım gerekir? Saf yöntemle karşılaştırın.
- Bölümdeki boru hatlı ağaç örneğinde (7 işlemci, h = 3) her işlemci k = 10 bileşenli vektör tutsaydı kaç tur gerekirdi?
- Programın %80'i paraleldir. p = 4 için Amdahl hızlanması nedir, üst sınır kaçtır?
- T₁ = 120 s, T₆ = 30 s. Hızlanma, verim ve maliyeti bulun.
- [1, 2, 3, 4] dizisinin Hillis–Steele önek toplamını turlar hâlinde yazın; kaç toplama yapılır?
Cevapları göster
- log₂ 16 = 4 tur; ilk turda 8 işlemci; 8 + 4 + 2 + 1 = 15 toplama (= n − 1).
- m + p − 2 = 103 adım; saf m(p − 1) = 400 adım (≈ 3,9 kat).
- 3 + (10 − 1) · 2 = 21 tur (saf 10 · 3 = 30).
- S = 1 / (0,2 + 0,8/4) = 1 / 0,4 = 2,5; sınır 1 / 0,2 = 5.
- S = 4, E = 4/6 ≈ 0,67, C = 6 · 30 = 180 s.
- d = 1: [1, 3, 5, 7]; d = 2: [1, 3, 6, 10]. 3 + 2 = 5 toplama (sıralı: 3).
Özet
Alıştırmalar
- PRAM ağaç toplamını en büyük eleman bulacak şekilde değiştirin. Süre ve iş nedir?
- n = 8 için Hillis–Steele önek toplamı kaç toplama yapar? Sıralı algoritmayla karşılaştırın.
- Paralel gezinmedeki
ziyaretkümesini iş parçacığı güvenli yapın. - Paralel birleştirme sıralamasına eşik ekleyin; neden iki alt görevden birini
fork()yerine doğrudancompute()etmek daha iyidir? - p = 32 işlemcide S = 16 hızlanma için programın en az ne kadarı paralel olmalıdır (Amdahl)?
- m satır, p işlemcili boru hattında adım sayısını ve verimi genel olarak ifade edin.
Cevaplar / ipuçları
a[i] = Math.max(a[i], a[i + adim]); D = log₂ n, W = n − 1.- d = 1: 7, d = 2: 6, d = 4: 4 → 17 toplama; sıralı 7. Genel: n log₂ n − (n − 1).
ConcurrentHashMap.newKeySet()kullanın veziyaret.add(komsu)ile kontrol + ekleme atomik olsun.- Boyut < eşik ise
Arrays.sort.sol.fork(); x = sag.compute(); y = sol.join();: iş parçacığı boşta beklemez, görev sayısı yarıya iner. - 1 / ((1 − f) + f/32) = 16 → 1 − (31/32) f = 1/16 → f = 30/31 ≈ %96,8.
- Adım: m + p − 2; verim: m / (m + p − 2).
Özet
Kaynaklar
- Cormen, Leiserson, Rivest ve Stein, Introduction to Algorithms, 4. baskı, MIT Press, 2022 (Bölüm 26: Parallel Algorithms).
- J. JáJá, An Introduction to Parallel Algorithms, Addison-Wesley, 1992.
- A. Grama, A. Gupta, G. Karypis ve V. Kumar, Introduction to Parallel Computing, 2. baskı, Addison-Wesley, 2003.
- G. M. Amdahl, "Validity of the Single Processor Approach to Achieving Large Scale Computing Capabilities", AFIPS, 1967.
- J. Dean ve S. Ghemawat, "MapReduce: Simplified Data Processing on Large Clusters", OSDI, 2004.
- D. Lea, "A Java Fork/Join Framework", ACM Java Grande Conference, 2000.