---
title: 'Dağıtık Veri İşleme'
source: 'https://academia.sh/tr/kurslar/dagitik-veri-isleme'
language: tr
updated: '2026-08-17T04:40:13+00:00'
license: 'CC BY-SA 4.0'
lessons: 11
outcomes:
    - 'Dağıtık hesaplamanın sürücü, yürütücü, görev ve bölüm soyutlamalarını kurgu bir küme üzerinde açıklar'
    - 'Karıştırma maliyetini ve veri çarpıklığını sayar ve bölüşüm anahtarı seçiminin işçi eklemeyi nasıl anlamsızlaştırdığını gösterir'
    - 'Süre ile toplam işi ayrı ayrı ölçerek küme boyutlandırma kararını gerekçelendirir'
---

# Dağıtık Veri İşleme

Veri tek makineye sığmadığında işin nasıl bölündüğünün ve bölmenin neye mal olduğunun sayılması: sürücü, yürütücü ve görev kavramlarının kurgu bir küme üzerinde kurulması, blok yerleşimi ile çoğaltmanın okumaya kattığı, eşle ve indirge soyutlamasının hangi adımda ağa dokunduğu, bölümler ile tembel değerlendirmenin gereksiz hesabı elemesi, kaynak yöneticisinin işi zamanlarken beklettiği görev, abone anahtarında 1,02 olan çarpıklığın bölge anahtarında 3,32'ye çıkması ve dört işçinin ikisinin boş kalması, aynı düzende karıştırmanın 1738 satır taşıyıp toplam işi 2200'den 7414'e çıkarması, olgu tarafı bölgeyle bölünmüşken yayınlı birleştirmenin 1600 satırlık boyuta kadar kazanıp 4000'de bölümlüye yenilmesi, tek işçide 2250 olan sürenin ikinci işçiyle 3691'e çıkması ve ancak dörtten sonra inmesi, süre 3691'den 1398'e inerken toplam işin 5830'dan 8635'e çıkması, ara sonucun önbelleğe alınmasıyla iki sorgunun 5500'den 3300'e inmesi, kontrol noktası aralığını altıdan üçe indirmenin aynı düşme noktasında kurtarmayı hiç azaltmaması.

## Dağıtık Hesaplama

Bir işin birden çok makineye bölünmesinin temel soyutlamalarının kurgu bir küme üzerinde kurulması ve her soyutlamanın kendi bedelinin sayılması: sürücünün planı, yürütücünün payı ve görevin tanımı ile tek işçilik tabanın 2250 süre ve 2200 toplam iş vermesi, blok yerleşimi ile çoğaltmanın okumayı yerelleştirdiği durum ve çoğaltma katsayısının sakladığı blok sayısı, eşle ve indirge soyutlamasında hangi adımın yerel kaldığı ile hangisinin ağa dokunduğu, satırların bölümlere ayrılması ve tembel değerlendirmenin hiç kullanılmayan hesabı elemesi, kaynak yöneticisinin işçi sayısı ile eşzamanlı görev arasındaki dengeyi kurarken beklettiği görev sayısı ve her işçiye iş vermenin elli birimlik sabit bedeli.

### Küme Hesaplama Temelleri

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/kume-hesaplama-temelleri

Bir işin birden çok makineye bölünmesinin sürücü, yürütücü ve görev soyutlamalarıyla kurulması ve bölmenin iki ayrı sayıyla ölçülmesi: hattan çıkan 2200 satır tek işçide süre 2250 ve toplam iş 2200 verirken dört işçiye abone anahtarıyla bölündüğünde süre 611'e iniyor, bölge anahtarıyla bölündüğünde dört işçinin ikisi boş kalıyor, çarpıklık 1,02 yerine 3,32 oluyor ve süre yalnızca 1876'ya inebiliyor; üç kurulumun toplam işi birebir aynı 2200 olduğu için hangisinin ölçüldüğü yazılmayan bir hızlanma iddiası eksik kalıyor.

On altı derste hat tek bir makinede koşuyormuş gibi ele alındı. Görevler sıraya girdi, düştü,
yeniden denendi ve kırk sekiz düğüm tek bir listeye dizildi; hattın aşamaları arasında veri bir
değişkenden öbürüne geçti ve bu geçişin bir bedeli yokmuş gibi davranıldı. Varsayım tek makine için
doğrudur: aynı bellekteki bir listeyi bir sonraki işleve vermek bedavaya yakındır.

Veri tek bir makineye sığmadığında varsayım düşer. İşin bölünmesi gerekir, bölünen parçalar farklı
makinelerde koşar ve aralarındaki her geçiş ağdan geçer. Bu ders o bölünmeyi kurar ve tek bir şeyi
gösterir: bölünmüş bir işin **tek bir sayısı yoktur**. En az iki sayı vardır ve ikisi aynı yöne
gitmek zorunda değildir.

- **DH1.** Kaynak, hat ve küme **kurgudur**. Gerçek bir dağıtık işleme çatısı, kaynak yöneticisi,
  dağıtık dosya sistemi ya da bulut servisi çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **DH2.** Kursun kuralı: dağıtık bir işin sayısı tek bir sayı değildir. **Süre** (en yüklü işçinin
  işi) ile **toplam iş** ayrı ölçülür ve **hangisinin ölçüldüğü yazılmayan hızlanma iddiası
  eksiktir.**
- **DH3.** Taban çizgisi **tek işçidir**: karıştırma yok, çarpıklık 1,00, süre **2250**, toplam iş
  **2200**.
- **DH4.** **Süre bu modelin kendi birimidir**, ham milisaniye değildir. Bir satırı yerel olarak
  işlemek bir birim sayılır; başka bir makinede ölçülen saat süresine karşılık gelmez.
- **DH5.** **Görev**, bir işçiye verilen paydır: aynı işlemin satırların bir altkümesi üzerinde
  koşması. Veri Hatları ve Orkestrasyon kursundaki görev çizgedeki bir düğümdü, yani bir aşamanın
  belirli bir dönem için koşmasıydı; buradaki görev o düğümün **içinde** durur.
- **DH6.** Her işçiye iş vermenin sabit bir bedeli vardır: **`GOREV_SABIT = 50`**. Bu bir seçimdir
  ve ona duyarlı her sonuç değeriyle birlikte yazılır.
- **DH7.** Bir satırı ağdan taşımanın bedeli yerel işlemenin **üç katıdır** (`AG_BEDELI = 3`). Bu da
  bir seçimdir; bu derste karıştırma olmadığı için hiçbir sonuca girmez.
- **DH8.** Ölçülen birimler satır, işçi, görev, süre, toplam iş ve çarpıklıktır. Süre ile toplam iş
  tam sayıdır ve üzerlerine oran basılmaz.

#### Sürücü, Yürütücü ve Görev

Dağıtık bir koşumda üç rol vardır ve üçü de tek makineli hatta yoktu. **Sürücü** (driver) işin
planını tutan tek noktadır: hangi işlemin yapılacağını, verinin hangi anahtara göre bölüneceğini ve
sonuçların nerede toplanacağını o bilir. Sürücü veriyi kendisi işlemez; iş dağıtır ve sonucu
toplar. **Yürütücü** (executor) bir makine üzerinde koşan ve kendisine verilen payı işleyen
süreçtir. Bu modelde her yürütücü tek bir **işçi** olarak sayılır, yani işçi sayısı yürütücü
sayısıdır.

**Görev** üçüncü ve en kolay karıştırılan kavramdır. Veri Hatları ve Orkestrasyon kursunda görev
çizgedeki bir düğümdü: `ayikla@2026-07` bir görevdi ve kırk sekiz tanesi vardı. Burada görev
çizgeyle ilgili değildir; bir işçiye verilen **paydır**. Aynı `ayikla@2026-07` düğümü dört işçiye
bölündüğünde dört görev doğar ve dördü de aynı işlemi farklı satır altkümeleri üzerinde koşar.
Orkestrasyon görevleri **sıraya** dizer, dağıtık koşum onları **yan yana** koyar.

Bu üç rol aşağıdaki kurgu küme üzerinde kurulur. İlk blok Veri Mühendisliğine Giriş kursunda
tanımlanan kaynağı ve hattın ilk üç aşamasını aynen yeniden kurar, üzerine bölüşüm ve maliyet
işlevlerini ekler.

```python
# KURGUDUR. Sayac okumalari, hat ve kume ders icinde tanimlanir; gercek bir
# dagitik isleme catisi, kaynak yoneticisi ya da dosya sistemi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


# ---- KURGU kume: isci sayisi ve iki secim
ISCI = 4
AG_BEDELI = 3          # bir satiri agdan tasimanin bedeli , yerel islemin kaç katı
GOREV_SABIT = 50       # her isciye is vermenin sabit bedeli


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


def dagit(kayitlar, anahtar, isci=ISCI):
    """Satirlari anahtara gore isciye atar. Ayni anahtar hep ayni isciye gider."""
    kova = {i: [] for i in range(isci)}
    for k in kayitlar:
        kova[izno(k[anahtar]) % isci].append(k)
    return kova


def yuk(kova):
    n = [len(v) for v in kova.values()]
    return {"toplam": sum(n), "en_yuklu": max(n), "en_az": min(n),
            "carpiklik": round(max(n) / (sum(n) / len(n)), 4)}


def maliyet(kova, tasinan=0, isci=ISCI):
    """Sure = en yuklu iscinin isi (paralel). Toplam is ayrica sayilir."""
    n = [len(v) for v in kova.values()]
    return {"sure": max(n) + tasinan * AG_BEDELI // isci + GOREV_SABIT,
            "toplam_is": sum(n) + tasinan * AG_BEDELI,
            "tasinan": tasinan}
```

#### Bölüşüm Anahtarı İşçiyi Seçer

Sürücünün verdiği ilk karar hangi anahtara göre bölüneceğidir. `dagit` bir satırın anahtar
değerinden bir sayı üretir ve işçi sayısına göre kalanını alır; aynı anahtar değeri her zaman aynı
işçiye gider. Bu özellik bir gereklilikten doğar: aynı abonenin iki dönemi farklı işçilere düşerse
tüketim farkı hiç hesaplanamaz. Karar bedava görünür, çünkü hangi anahtar seçilirse seçilsin toplam
satır aynı kalır. İlk ölçüm bunun neden yanıltıcı olduğunu gösterir.

```python
S = satirlar()
print("hattan cikan satir:", len(S), " isci:", ISCI)
print()
print(f"{'anahtar':<9}{'isci yuku':<26}{'en yuklu':>9}{'en az':>7}{'carpiklik':>11}")
for a in ("abone", "donem", "bolge"):
    kova = dagit(S, a, ISCI)
    y = yuk(kova)
    print(f"{a:<9}{str([len(v) for v in kova.values()]):<26}"
          f"{y['en_yuklu']:>9}{y['en_az']:>7}{y['carpiklik']:>11.4f}")
```

```
hattan cikan satir: 2200  isci: 4

anahtar  isci yuku                  en yuklu  en az  carpiklik
abone    [539, 550, 561, 550]            561    539     1.0200
donem    [600, 600, 400, 600]            600    400     1.0909
bolge    [374, 0, 0, 1826]              1826      0     3.3200
```

Üç satırın toplamı aynıdır: her kurulumda 2200 satır dağıtılmıştır ve hiçbir satır kaybolmamıştır.
Değişen şey satırların nasıl yığıldığıdır. `abone` anahtarında iki yüz farklı değer dört kovaya
düştüğü için yükler 539 ile 561 arasında sıkışır; **çarpıklık 1,02**, yani en yüklü işçi ortalamanın
yüzde iki üzerindedir. `donem` anahtarında on iki değer dörde bölünür ve üç kova üçer dönem, biri
iki dönem alır: 600, 600, 400, 600.

`bolge` satırı bölüşümün sessiz kusurudur. Beş bölge dört işçiye dağıldığında **iki işçi hiç satır
almaz** ve bir işçi 1826 satırla kalır. Çarpıklık **3,32**'dir. Küme dört makine tutuyor, dördünün
de faturası işliyor, ama ikisi boş duruyor. Hiçbir hata verilmez, hiçbir satır düşmez ve sonuç
doğrudur; yanlış olan tek şey bölüşümdür ve bu yalnızca sayılırsa görünür. Çarpıklığın çözünürlüğü
işçi sayısına bağlıdır: dört işçide en küçük adım 0,0018'dir.

#### Süre ile Toplam İş

Yükler bir sayıya çevrildiğinde asıl ayrım ortaya çıkar. İşçiler yan yana koştuğu için işin bitmesi
en yavaş işçiyi bekler; bu **süredir** ve `maliyet` onu en yüklü işçinin payı artı 50 birimlik sabit
görev bedeli olarak hesaplar. Buna karşılık kümenin harcadığı **toplam iş** bütün işçilerin payının
toplamıdır ve kim beklerse beklesin ödenir. Aşağıdaki blok tek işçilik tabanı üç dörtlü kurulumla
karşılaştırır.

```python
print(f"{'kurulum':<20}{'gorev':>6}{'en yuklu pay':>14}{'sure':>7}{'toplam is':>11}")
for ad, anahtar, n in (("tek isci", "abone", 1), ("dort isci / abone", "abone", 4),
                       ("dort isci / donem", "donem", 4), ("dort isci / bolge", "bolge", 4)):
    kova = dagit(S, anahtar, n)
    m = maliyet(kova, 0, n)
    print(f"{ad:<20}{n:>6}{max(len(v) for v in kova.values()):>14}"
          f"{m['sure']:>7}{m['toplam_is']:>11}")
```

```
kurulum              gorev  en yuklu pay   sure  toplam is
tek isci                 1          2200   2250       2200
dort isci / abone        4           561    611       2200
dort isci / donem        4           600    650       2200
dort isci / bolge        4          1826   1876       2200
```

Son sütun dört satırda da **2200**'dür. Kümenin yaptığı iş hiç değişmemiştir, çünkü karıştırma yoktur
ve her satır tam bir kez işlenir. Buna rağmen süre 2250 ile 611 arasında değişir. Bir kurulumun
"2200 birimlik iş yaptığı" doğrudur ve dört kurulumu birbirinden **hiç ayırmaz**. Kursun kuralı bu
tablodan okunur: hangisinin ölçüldüğü yazılmayan bir hızlanma iddiası eksiktir.

Taban çizgisi tek işçidir: süre **2250**, toplam iş **2200**. Aradaki 50 birimlik fark sabit görev
bedelidir ve tek işçide bile ödenir. Dört işçi `abone` anahtarıyla süreyi **611**'e indirir; bu
kursun boyunca görülecek en temiz kazançtır ve sebebi de o kadar dardır: veri hiç hareket etmiyor,
her işçi kendi payını okuyup kendi payını yazıyor. `donem` anahtarında süre **650**'ye çıkar, çünkü
en yüklü işçi 561 yerine 600 satır tutar.

`bolge` satırı bir kez daha ayrı durur. Dört işçi çalıştırılmasına rağmen süre yalnız **1876**'dır;
tek işçiye göre kazanç dörtte bir değil, altıda birden azdır. İkisi boş oturan işçiler süreye hiç
dokunmaz, çünkü süre en yüklü işçiyi ölçer. Aynı toplam iş, aynı doğru sonuç, üç kat farklı süre.

Bu sayıların birimi üzerine bir uyarı gerekir. **Süre bu modelin kendi birimidir.** Bir satırı yerel
işlemek bir birim, bir satırı ağdan taşımak üç birim, bir işçiye iş vermek elli birimdir; ikinci ve
üçüncü değerler `AG_BEDELI` ve `GOREV_SABIT` olarak yazılmış **seçimlerdir**. 2250 sayısı bir
saniyeyi, bir milisaniyeyi ya da herhangi bir saat okumasını göstermez. Modelin verdiği şey
kurulumlar arasındaki **karşılaştırmadır**; tek bir kurulumun süresi kendi başına bir anlam taşımaz.

#### Sonuç Değişmez, Bedel Değişir

Bölüşüm yanlış olduğunda ne olur sorusunun yanıtı, bu kursta öğrenmesi en zor olanıdır: **hiçbir
şey**. Sonuç yanlış çıkmaz, satır kaybolmaz, hata verilmez. Sürücü işçilerden gelen ara özetleri
birleştirir ve elindeki tablo dört kurulumda da birebir aynıdır. Son blok bunu her kurulum için
bölge toplamlarını hesaplayarak gösterir.

```python
def bolge_toplami(kova):
    """Her isci kendi payini ozetler , surucu ara ozetleri birlestirir."""
    ara = []
    for pay in kova.values():
        t = {}
        for k in pay:
            t[k["bolge"]] = t.get(k["bolge"], 0) + k["m3"]
        ara.append(t)
    son = {}
    for t in ara:
        for b, v in t.items():
            son[b] = son.get(b, 0) + v
    return son


taban = bolge_toplami(dagit(S, "abone", 1))
print("taban sonuc:", taban)
print()
print(f"{'kurulum':<20}{'ara satir':>10}{'sure':>7}{'toplam is':>11}{'sonuc':>17}")
for ad, a, n in (("tek isci", "abone", 1), ("dort isci / abone", "abone", 4),
                 ("dort isci / donem", "donem", 4), ("dort isci / bolge", "bolge", 4)):
    kova = dagit(S, a, n)
    m = maliyet(kova, 0, n)
    ara = sum(len(set(k["bolge"] for k in pay)) for pay in kova.values())
    esit = "taban ile ayni" if bolge_toplami(kova) == taban else "farkli"
    print(f"{ad:<20}{ara:>10}{m['sure']:>7}{m['toplam_is']:>11}{esit:>17}")
```

```
taban sonuc: {'merkez': 14536, 'kuzey': 13338, 'dogu': 9695, 'bati': 9164, 'guney': 11721}

kurulum              ara satir   sure  toplam is            sonuc
tek isci                     5   2250       2200   taban ile ayni
dort isci / abone           20    611       2200   taban ile ayni
dort isci / donem           20    650       2200   taban ile ayni
dort isci / bolge            5   1876       2200   taban ile ayni
```

Son sütun dört kez aynı şeyi söyler. Beş bölge toplamı hangi bölüşümde hesaplanırsa hesaplansın
değişmez, çünkü toplama işlemi satırların hangi sırayla ve hangi makinede toplandığına duyarsızdır.
Bunun sonucu şudur: hattın çıktısına bakan bir denetim, iki işçinin boş oturduğu kurulumla en iyi
kurulumu **birbirinden ayıramaz**. Veri Mühendisliğine Giriş kursunda kurulan denetimlerin hepsi
yeşil kalır. Yanlış bölüşüm bir doğruluk kusuru değildir; **yalnızca sayılırsa görünür.**

Üçüncü sütundan önceki sayı sürücünün payını verir. `abone` ve `donem` bölüşümlerinde her işçinin
payında beş bölge de bulunur, bu yüzden sürücüye 20 ara satır gelir; `bolge` bölüşümünde her bölge
tek bir işçide durduğu için yalnız 5 ara satır gelir. Sürücünün birleştirme işi bu modelde toplam
işe eklenmez, ama gerçek bir kümede o da bir bedeldir ve en dengesiz bölüşüm burada en ucuz olanıdır.
Bir bölüşümü tek bir sayıya bakarak seçmenin neden yetmediğinin ikinci örneği budur.

#### Özet

- Dağıtık koşumun üç rolü vardır: **sürücü** planı tutar ve dağıtır, **yürütücü** kendisine verilen
  payı işler, **görev** o payın kendisidir. Bu modelde bir yürütücü bir **işçi** olarak sayılır.
- Buradaki görev orkestrasyondaki görevle aynı şey değildir: orada çizgedeki bir düğümdü, burada o
  düğümün içinde bir işçiye düşen paydır. Bir düğüm dört işçide dört görev doğurur.
- Bölüşüm anahtarı yükleri belirler: `abone` çarpıklığı **1,02**, `donem` **1,0909**, `bolge`
  **3,32** verir ve `bolge` anahtarında dört işçinin **ikisi hiç satır almaz**.
- Süre ile toplam iş ayrı sayılardır. Dört kurulumda toplam iş **2200**'de sabit kalırken süre
  **2250**, **611**, **650** ve **1876** olur; toplam iş kurulumları birbirinden ayırmaz.
- Tek işçilik taban süre **2250** ve toplam iş **2200**'dür; 50 birimlik fark her işçiye iş vermenin
  sabit bedelidir.
- Süre bu modelin kendi birimidir. `AG_BEDELI = 3` ve `GOREV_SABIT = 50` seçimlerdir ve sayılar
  ancak birbirleriyle karşılaştırıldığında anlam taşır.

#### Sonraki Adım

Bu derste satırlar bir listede hazır duruyordu ve `dagit` onları işçilere dağıttı. Gerçekte bir
işçinin işlediği satırlar bir yerden **okunur** ve o yer başka bir makine olabilir. Bölüşüm ne kadar
dengeli olursa olsun, her işçi payını ağ üzerinden çekiyorsa dağıtmanın kazancı okuma bedelinin
altında kalır. Sonraki ders veriyi bloklara ayırıp kümeye yerleştirir ve tek bir soruyu ölçer:
bir bloğu işleyen işçi onu zaten tutuyorsa ne kazanılır ve bloğun kaç kopyasının saklandığı bu
kazancı nasıl değiştirir.

### Dağıtık Dosya Sistemleri

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/dagitik-dosya-sistemleri

Veriyi bloklara ayırıp kümeye yerleştirmenin ve bir bloğu işleyen işçinin onu zaten tutup tutmadığının ölçülmesi: 2200 satır 100 satırlık 22 bloğa ayrıldığında tek kopyayla ve blok sırasına göre atamada 22 bloğun yalnız 5'i yerel okunuyor, süre 1850 ve toplam iş 5600 oluyor, çoğaltma katsayısı 2'de yerel blok 17'ye ve süre 1150'ye, 3'te 22'ye ve 650'ye iniyor ama saklanan blok 22'den 66'ya çıkıyor, dördüncü kopya okumaya hiçbir şey katmıyor; yerellik farkındalı atama tek kopyayla 22 bloğun 22'sini yerel okuyor ama yerleşim dengesiz olduğu için süre 650 yerine 750'de kalıyor.

Önceki derste satırlar bir listede hazır duruyordu ve `dagit` onları işçilere dağıttı. Dağıtmanın
bedeli sıfır sayıldı, çünkü liste zaten bellekteydi. Bu varsayım tek makinede doğrudur ve kümede
yanlıştır: veri makinelerin diskine dağılmış hâlde durur, bir işçinin işleyeceği satırlar başka bir
makinede olabilir ve o satırları görmek için ağdan çekmek gerekir.

Bu ders veriyi bloklara ayırıp kümeye yerleştirir ve tek bir soruyu ölçer: bir bloğu işleyen işçi
onu zaten tutuyorsa ne kazanılır. Yanıtın adı **veri yerelliğidir** (data locality) ve bedeli iki
ayrı yerden ödenir, biri süreden biri diskten.

- **DH9.** Blok yerleşimi ve çoğaltma **kurgudur**; gerçek bir dağıtık dosya sistemi ürünü, blok
  sunucusu ya da bulut depolama servisi çağrılmaz. Tohum **20260218**.
- **DH10.** Hattan çıkan **2200 satır** sırayla **100 satırlık** bloklara ayrılır; **22 blok** olur.
  Blok, bölüşümün çözünürlüğüdür: bir blok bölünmez ve bütünüyle tek bir işçiye gider.
- **DH11.** **Yerleşim kuralı:** bir bloğun ilk kopyası anahtarının izinden seçilir, kalan kopyalar
  halkada sonraki işçilere düşer. **Çoğaltma katsayısı** (`kopya`) bir bloğun kaç işçide durduğudur.
- **DH12.** Bir bloğu okuyan işçi onu tutuyorsa satır başına bedel **1**, tutmuyorsa **`AG_BEDELI`
  yani 3**'tür. Bu derste ölçülen her süre ve toplam iş bu seçime duyarlıdır.
- **DH13.** Çoğaltmanın **dayanıklılık** yanı, yani bir makine düştüğünde verinin kalması,
  Veritabanları müfredatının İlişkisel Veritabanı Yönetimi kursunda ve Sistem Tasarımı ve Dağıtık
  Sistemler müfredatında ölçüldü. **Yordam burada tekrarlanmaz**; bu ders çoğaltmayı yalnız
  **okumaya** olan etkisiyle sayar.
- **DH14.** Dosya düzeni, sütunlu biçim ve bölümleme Veri Depolama Mimarileri kursunda ölçüldü ve
  **tekrarlanmaz**. Buradaki blok bir dosya biçimi değil, yerleştirme birimidir.
- **DH15.** Taban çizgisi yine **tek işçidir**: bütün bloklar aynı işçide durur, yerellik tamdır,
  süre **2250**, toplam iş **2200**.

#### Blok ve Yerleşim

Dağıtık bir dosya sisteminin tek makineli bir dosya sisteminden farkı, bir dosyayı bütün olarak
saklamamasıdır. Dosya sabit boyutlu **bloklara** kesilir ve bloklar kümedeki makinelere dağıtılır.
Bunun iki sonucu vardır. Birincisi, tek bir makineye sığmayan bir dosya saklanabilir hâle gelir.
İkincisi, aynı dosyanın farklı parçaları **aynı anda** okunabilir, çünkü farklı disklerdedirler.

Blok bir bölünme birimidir, bir bölüm değildir. Blok sınırı satırların anahtarına bakmaz; dosyanın
kaçıncı satırında olduğuna bakar. Bu yüzden aynı abonenin satırları iki bloğa yayılabilir ve bir
blokta beş bölge birden bulunabilir. Yerleştirme kuralı da veriye bakmaz: bloğun sırasından bir iz
üretir ve o izle bir işçi seçer. Kopyalar halkada sonraki işçilere düşer, böylece bir işçinin
kaybı tek bir bloğun bütün kopyalarını götürmez.

```python
# KURGUDUR. Kaynak, hat ve dagitik dosya sistemi ders icinde tanimlanir;
# gercek bir dosya sistemi urunu ya da blok sunucusu cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


ISCI, AG_BEDELI, GOREV_SABIT = 4, 3, 50


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


# ---- KURGU dagitik dosya sistemi: blok , yerlesim , okuma
BLOK = 100


def bloklar(kayitlar, boyut=BLOK):
    """Satirlar sirayla sabit boyutlu bloklara kesilir."""
    return [kayitlar[i:i + boyut] for i in range(0, len(kayitlar), boyut)]


def yerlesim(blok_no, kopya, isci=ISCI):
    """Ilk kopya blogun izinden , kalanlar halkada sonraki isciye."""
    ilk = izno(f"blok{blok_no}") % isci
    return [(ilk + j) % isci for j in range(kopya)]


def okuma(bl, kopya, yerellik, isci=ISCI):
    """Her blogu bir isci okur. Blogu tutmayan isci satirlari agdan ceker."""
    is_yuku = {i: 0 for i in range(isci)}
    yerel = 0
    for b, blok in enumerate(bl):
        tutan = yerlesim(b, kopya, isci)
        okuyan = tutan[0] if yerellik else b % isci
        if okuyan in tutan:
            yerel += 1
            is_yuku[okuyan] += len(blok)
        else:
            is_yuku[okuyan] += len(blok) * AG_BEDELI
    return {"yerel": yerel, "uzak": len(bl) - yerel, "saklanan": len(bl) * kopya,
            "sure": max(is_yuku.values()) + GOREV_SABIT,
            "toplam_is": sum(is_yuku.values())}
```

#### Yerellik Bir Rastlantıdır

İlk kurulumda sürücü blokları işçilere **sırayla** verir: sıfırıncı blok sıfırıncı işçiye, birinci
blok birinci işçiye ve böyle devam eder. Bu atama yükü eşitler, çünkü her işçiye yaklaşık aynı
sayıda blok düşer. Yerleşimi ise hiç dikkate almaz; bir işçinin okuduğu bloğu tutuyor olması bir
rastlantıdır. Aşağıdaki blok o rastlantının kaç blokta gerçekleştiğini çoğaltma katsayısı boyunca
sayar.

```python
BL = bloklar(satirlar())
tek = okuma(BL, 1, False, 1)
print("blok:", len(BL), " her biri", BLOK, "satir")
print("tek isci tabani -> sure:", tek["sure"], " toplam is:", tek["toplam_is"])
print()
print(f"{'kopya':>6}{'yerel blok':>12}{'uzak blok':>11}{'saklanan':>10}{'sure':>7}{'toplam is':>11}")
for r in (1, 2, 3, 4):
    o = okuma(BL, r, False)
    print(f"{r:>6}{o['yerel']:>12}{o['uzak']:>11}{o['saklanan']:>10}"
          f"{o['sure']:>7}{o['toplam_is']:>11}")
```

```
blok: 22  her biri 100 satir
tek isci tabani -> sure: 2250  toplam is: 2200

 kopya  yerel blok  uzak blok  saklanan   sure  toplam is
     1           5         17        22   1850       5600
     2          17          5        44   1150       3200
     3          22          0        66    650       2200
     4          22          0        88    650       2200
```

İlk satır dağıtmanın gizli faturasını gösterir. Tek kopyayla yirmi iki bloğun yalnız **beşi** yerel
okunur; kalan on yedi blok ağdan geçer ve her satırı üç kez sayılır. Toplam iş 2200 yerine
**5600**, süre **1850** olur. Beş sayısı bir rastlantının beklenen değerine yakındır: dört işçide
okuyanla tutanın çakışma olasılığı dörtte birdir ve yirmi iki bloğun dörtte biri 5,5'tir. Önceki derste aynı dört işçi süreyi 611'e indirmişti, ama o ölçüm
okumayı bedava saymıştı. Okuma sayılınca dört işçili kurulum tek işçilik tabana göre yalnız
2250'den 1850'ye iner; kazanç dörtte bir değil, beşte birden azdır.

İkinci ve üçüncü satırlar çoğaltmanın okumaya ne kattığını verir. Her bloğun iki kopyası olduğunda
yerel okunan blok 5'ten **17**'ye çıkar ve süre **1150**'ye iner; üç kopyada yirmi iki bloğun
tamamı yerel okunur, toplam iş **2200**'e döner ve süre **650** olur. Ağ trafiği tamamen kaybolur.
Bu sayılar `AG_BEDELI = 3` seçimine bağlıdır: taşımanın bedeli 1 olsaydı dört satır da aynı toplam
işi verirdi ve çoğaltmanın okumaya hiçbir katkısı görünmezdi.

Dördüncü satır çoğaltmanın sınırıdır. Dört kopyada saklanan blok **88**'e çıkar, yani veri dört
katına, ama yerel blok da süre de üç kopyadakiyle **birebir aynı** kalır. Üçüncü kopyadan sonra
okumaya ödenen hiçbir şey yoktur; dördüncü kopyanın gerekçesi başka bir yerdedir ve o gerekçe
dayanıklılıktır. Dayanıklılık bu kursta ölçülmez, önkoşul kurslarda ölçüldü.

#### Yerellik mi, Denge mi

Yerelliği çoğaltmayla satın almanın bir alternatifi vardır: sürücü blokları sıraya göre değil,
**onları tutan işçiye** verir. Bu atama tek kopyayla bile tam yerellik sağlar. Karşılığında yükün
dengesini yerleştirme kuralına devreder ve o kural yükü eşitlemeye çalışmaz. Son blok iki atamayı
iki çoğaltma katsayısında karşılaştırır.

```python
print(f"{'atama':<22}{'kopya':>6}{'yerel':>7}{'saklanan':>10}{'sure':>7}{'toplam is':>11}")
for ad, y, r in (("blok sirasi", False, 1), ("blok sirasi", False, 3),
                 ("yerellik farkindali", True, 1), ("yerellik farkindali", True, 3)):
    o = okuma(BL, r, y)
    print(f"{ad:<22}{r:>6}{o['yerel']:>7}{o['saklanan']:>10}"
          f"{o['sure']:>7}{o['toplam_is']:>11}")
```

```
atama                  kopya  yerel  saklanan   sure  toplam is
blok sirasi                1      5        22   1850       5600
blok sirasi                3     22        66    650       2200
yerellik farkindali        1     22        22    750       2200
yerellik farkindali        3     22        66    750       2200
```

Üçüncü satır tek kopyayla tam yerelliği yakalar: yirmi iki bloğun yirmi ikisi yerel okunur, toplam
iş **2200**'e iner ve saklanan blok **22**'de kalır. Aynı sonucu blok sırasına göre atama ancak üç
kopyayla, yani **66 blok** saklayarak elde eder. Yerellik farkındalı atama üç katı diskten kurtarır.

Buna karşılık süresi **750**'dir, blok sırasına göre atamanın üç kopyalı hâlinden **100 birim**
fazla. Sebep tabloda görünmez ama yerleşim kuralındadır: bloklar işçilere eşit dağılmamıştır ve en
çok blok tutan işçi yedi blok, en az tutan dört blok okur. Yerellik farkındalı atama okuyanı
seçemez, çünkü seçerse yerellik bozulur. Dördüncü satır bunu bir kez daha yazar: çoğaltmayı üçe
çıkarmak yerellik farkındalı atamada süreyi hiç değiştirmez, çünkü ilk kopyayı tutan işçi
değişmemiştir.

İki satırın arasındaki seçim tek bir sayıya indirgenemez. Yüz birimlik süre için kırk dört fazla
blok saklanır, ya da kırk dört blok yerine yüz birim süre ödenir. Hangisinin daha ucuz olduğu
kümenin diskiyle işinin ne kadar sık koştuğuna bağlıdır ve bu karar ancak iki sayı da yazıldığında
verilebilir.

#### Blok Boyutu Bölüşümün Çözünürlüğüdür

Yerellik farkındalı atamanın 750'de kalmasının sebebi tabloda görünmez, çünkü tablo blokların
işçilere nasıl düştüğünü yazmaz. Son blok önce o dağılımı basar, sonra blok boyutunu değiştirerek
dengenin ne kadarının boyuttan geldiğini ölçer.

```python
S = satirlar()
tutan = {i: 0 for i in range(ISCI)}
for b in range(len(BL)):
    tutan[yerlesim(b, 1)[0]] += 1
print("ilk kopyayi tutan blok sayisi:", [tutan[i] for i in range(ISCI)])
print()
print(f"{'blok boyutu':>12}{'blok':>6}{'yerel':>7}{'sure':>7}{'toplam is':>11}")
for bo in (25, 50, 100, 220, 550):
    bl = bloklar(S, bo)
    o = okuma(bl, 1, True)
    print(f"{bo:>12}{len(bl):>6}{o['yerel']:>7}{o['sure']:>7}{o['toplam_is']:>11}")
```

```
ilk kopyayi tutan blok sayisi: [7, 4, 4, 7]

 blok boyutu  blok  yerel   sure  toplam is
          25    88     88    625       2200
          50    44     44    650       2200
         100    22     22    750       2200
         220    10     10    710       2200
         550     4      4    600       2200
```

İlk satır eksik kalan gerekçeyi verir. Yirmi iki blok dört işçiye **7, 4, 4, 7** olarak düşmüştür;
en çok blok tutan işçi yedi blok, yani 700 satır okur ve süre 700 artı 50 sabit bedelle **750**
olur. Yerleştirme kuralı yükü eşitlemeye çalışmaz, bloğun izine bakar; dengesizlik bir kusur değil,
kuralın doğal sonucudur.

Süpürme bu dengesizliğin blok boyutuyla ilişkisini gösterir. Yirmi beş satırlık bloklarda seksen
sekiz blok oluşur ve süre **625**'e iner, elli satırlıkta **650** olur: blok küçüldükçe her işçinin
payı ortalamaya yaklaşır, çünkü tek bir bloğun yanlış yere düşmesinin ağırlığı azalır. Yerel blok
sayısı her satırda blok sayısına eşittir ve toplam iş **2200**'de sabittir; blok boyutu ağ
trafiğine değil, yalnız dengeye dokunur.

Son iki satır tek yönlü bir kural çıkarmayı engeller. İki yüz yirmi satırlık on blok süreyi
**710**'a indirir, beş yüz elli satırlık dört blok ise **600**'e — yüz satırlık kurulumdan da, yirmi
beş satırlıktan da iyidir. Sebebi şudur: dört blok tesadüfen dört ayrı işçiye düşmüştür ve dağılım
1, 1, 1, 1 olmuştur. Küçük blok dengeyi **beklenen değerde** iyileştirir, tek bir kurulumda garanti
etmez. Bu modelde blok küçültmenin görünen bir bedeli de yoktur, çünkü sabit görev bedeli blok
başına değil işçi başına bir kez sayılır; gerçek bir kümede her blok bir görev doğurur ve o bedel
küçük bloklarda geri gelir.

#### Özet

- Dağıtık dosya sistemi veriyi sabit boyutlu **bloklara** keser ve kümeye yayar. 2200 satır 100
  satırlık **22 blok** olur; blok bölünmez ve bütünüyle tek bir işçiye gider.
- **Veri yerelliği**, bir bloğu okuyan işçinin onu zaten tutmasıdır. Yerelse satır başına bedel 1,
  değilse **`AG_BEDELI` yani 3**'tür; ölçülen bütün sayılar bu seçime duyarlıdır.
- Blok sırasına göre atamada tek kopyayla 22 bloğun yalnız **5'i** yerel okunur: süre **1850**,
  toplam iş **5600**. Çoğaltma 2'de yerel blok **17** ve süre **1150**, 3'te **22** ve **650** olur.
- Çoğaltmanın okumaya katkısı üçüncü kopyada biter: dört kopyada saklanan blok **88**'e çıkar, yerel
  blok ve süre değişmez. Dördüncü kopyanın gerekçesi dayanıklılıktır ve önkoşul kurslarda ölçüldü.
- Yerellik farkındalı atama tek kopyayla tam yerellik verir (**22** yerel blok, **22** saklanan
  blok) ama yerleşim dengesiz olduğu için süre **650** yerine **750**'de kalır. Yerellik ile denge
  aynı anda satın alınmaz; ikisi de yazılmadan atama seçilemez.
- Blok boyutu dengenin çözünürlüğüdür: 22 blok işçilere **7, 4, 4, 7** düşer, 25 satırlık bloklarda
  süre **625**'e iner. Ama kural tek yönlü değildir; 550 satırlık dört blok tesadüfen dengeli
  düşerek **600** verir. Küçük blok dengeyi beklenen değerde iyileştirir, garanti etmez.

#### Sonraki Adım

Bu iki derste her işçi kendi payını okudu ve kendi payını işledi; hiçbir satır bir işçiden ötekine
geçmedi. Bu ancak hesap satır satır yapıldığında mümkündür. Bölge başına toplam gibi bir sonuç
istendiğinde aynı bölgenin satırları farklı işçilere dağılmış olur ve toplanmadan önce bir araya
gelmeleri gerekir. Sonraki ders hesabı iki adıma ayıran bir modeli kurar ve tek bir soruyu sorar:
hangi adım yerel kalıyor, hangisi ağa dokunuyor ve aynı sonucu üretmenin bedeli bu ayrımla ne
kadar değişiyor.

### Eşle-İndirge Modeli

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/esle-indirge-modeli

Bir hesabın satır başına bağımsız işleyen bir adımla anahtar başına toplayan bir adıma ayrılmasının ve iki adımdan yalnız birinin ağa dokunmasının ölçülmesi: eşleme 2200 satırı yerel işleyip 0 satır taşıyor, süre 611 ve toplam iş 2200 kalıyor, aynı sonucu üreten indirgeme adımı abone düzeninden bölge anahtarına 1738 satır taşıyor ve toplam işi 7414'e, süreyi 1914'e çıkarıyor; her işçi kendi payını taşımadan önce indirgediğinde taşınan satır 20 ara satırdan yalnız 15'i oluyor, toplam iş 2245'e ve süre 622'ye iniyor ve üç kurulumun sunduğu beş bölge toplamı birebir aynı kalıyor.

Önceki iki derste her işçi kendi payını okudu ve kendi payını işledi; hiçbir satır bir işçiden
ötekine geçmedi. Bu, yapılan hesapların özel bir sınıftan olmasıyla mümkündü: her satır kendi
başına işlenebiliyordu. Tüketim farkı hesaplanırken bir abonenin iki dönemi gerekir, ama aynı abone
zaten tek bir işçidedir; ayıklama ve tekilleştirme de satırın kendisine bakar.

Bölge başına toplam gibi bir sonuç istendiğinde bu biter. Bir bölgenin satırları iki yüz aboneye
yayılmıştır, aboneye göre bölüşülmüş bir kümede dört işçinin dördüne de düşmüştür ve toplanmadan
önce bir araya gelmeleri gerekir. Bu ders hesabı iki adıma ayıran bir model kurar ve tek bir şeyi
sorar: hangi adım yerel kalıyor, hangisi ağa dokunuyor.

- **DH16.** Küme, hat ve iki adımlı model **kurgudur**; gerçek bir dağıtık işleme çatısı, akış
  motoru ya da bulut servisi çağrılmaz. Tohum **20260218**.
- **DH17.** **Eşle-indirge** bir model adıdır ve bir ürünü göstermez. Eşleme adımı her satıra
  bağımsız olarak bir işlev uygular ve **(anahtar, değer)** çiftleri üretir — bu derste satır
  başına bir çift; indirgeme adımı aynı anahtarın bütün değerlerini tek bir sonuca katlar.
- **DH18.** Eşleme adımının **satırlar arası bağımlılığı yoktur**; bu yüzden veriyi bulunduğu yerde
  işleyebilir ve ağa dokunmaz.
- **DH19.** İndirgeme adımı bir anahtarın **bütün** değerlerinin tek bir işçide toplanmasını ister.
  Veri o düzende değilse **karıştırma** doğar ve işçi değiştiren her satır ağdan geçer.
- **DH20.** Karıştırılan bir satırın bedeli **`AG_BEDELI` yani 3**'tür; bu bir seçimdir ve bu
  derste ölçülen her süre ile toplam iş ona duyarlıdır.
- **DH21.** Taban çizgisi yine **tek işçidir**: karıştırma yok, süre **2250**, toplam iş **2200**.
- **DH22.** Satırlar derse önceki derslerden geldiği düzende, yani **`abone` anahtarına göre
  bölüşülmüş** olarak girer. Karıştırmanın büyüklüğü bu başlangıç düzenine bağlıdır.
- **DH23.** Süre bu modelin kendi birimidir ve ham milisaniye değildir; sayılar ancak birbirleriyle
  karşılaştırıldığında anlam taşır.

#### İki Adımlı Model

Modelin kurduğu ayrım hesabın kendisinden çıkar. Bir hesabın bir satırını yapmak için başka
satırlara bakmak gerekiyor mu sorusunun iki yanıtı vardır ve bu iki yanıt iki adım demektir.
**Eşleme** (map) hayır diyen kısımdır: satırdan bir anahtar ve bir değer çıkarır, komşusuna hiç
bakmaz. **İndirgeme** (reduce) evet diyen kısımdır: bir anahtara ait bütün değerleri ister ve onlar
elinde olmadan tek bir doğru sayı üretemez.

Bu ayrımın değeri, hesabın hangi kısmının ağa mal olduğunu **önceden** söylemesidir. Eşleme adımına
kaç işçi eklenirse eklensin ağ trafiği doğmaz. İndirgeme adımının trafiği ise anahtarın seçimine ve
verinin girdiği düzene bağlıdır. Aşağıdaki blok modeli üç işlevle kurar: `esle` çift üretir,
`yerel_indirge` bir işçinin kendi payını katlar, `indirge` karıştırmadan sonra sonucu toplar.

```python
# KURGUDUR. Kaynak, hat ve kume ders icinde tanimlanir; gercek bir dagitik
# isleme catisi, akis motoru ya da bulut servisi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


ISCI, AG_BEDELI, GOREV_SABIT = 4, 3, 50


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


def dagit(kayitlar, anahtar, isci=ISCI):
    kova = {i: [] for i in range(isci)}
    for k in kayitlar:
        kova[izno(k[anahtar]) % isci].append(k)
    return kova


def karistir(kova, yeni_anahtar, isci=ISCI):
    """Yeniden bolustur. Isci degistiren satir agdan gecer."""
    yeni = {i: [] for i in range(isci)}
    tasinan = 0
    for eski, kayitlar in kova.items():
        for k in kayitlar:
            hedef = izno(k[yeni_anahtar]) % isci
            if hedef != eski:
                tasinan += 1
            yeni[hedef].append(k)
    return yeni, tasinan


def maliyet(kova, tasinan=0, isci=ISCI):
    """Sure = en yuklu iscinin isi (paralel). Toplam is ayrica sayilir."""
    n = [len(v) for v in kova.values()]
    return {"sure": max(n) + tasinan * AG_BEDELI // isci + GOREV_SABIT,
            "toplam_is": sum(n) + tasinan * AG_BEDELI,
            "tasinan": tasinan}


# ---- KURGU esle-indirge: iki adim
def esle(pay, anahtar_islevi, deger_islevi):
    """Her satirdan bir (anahtar , deger) cifti uretir. Yerel , komsuya bakmaz."""
    return [{"anahtar": anahtar_islevi(k), "deger": deger_islevi(k)} for k in pay]


def yerel_indirge(ciftler):
    """Bir iscinin kendi payindaki ciftleri anahtar basina toplar."""
    top = {}
    for c in ciftler:
        top[c["anahtar"]] = top.get(c["anahtar"], 0) + c["deger"]
    return [{"anahtar": a, "deger": v} for a, v in top.items()]


def indirge(kova):
    """Karistirmadan sonra her isci kendi anahtarlarini toplar , surucu birlestirir."""
    son = {}
    for pay in kova.values():
        for c in yerel_indirge(pay):
            son[c["anahtar"]] = son.get(c["anahtar"], 0) + c["deger"]
    return son
```

#### Eşleme Yerel Kalır

İlk ölçüm eşleme adımını tek başına sayar. Satırlar derse `abone` anahtarına göre bölüşülmüş olarak
girer ve eşleme her satırdan bölgeyi anahtar, tüketimi değer olarak çıkarır. Hiçbir işçi başka bir
işçinin satırına bakmaz, bu yüzden `karistir` hiç çağrılmaz ve taşınan satır sıfırdır.

```python
S = satirlar()
K = dagit(S, "abone", ISCI)
ESLENMIS = {i: esle(pay, lambda k: k["bolge"], lambda k: k["m3"]) for i, pay in K.items()}
print("tek isci tabani ->", maliyet(dagit(S, "abone", 1), 0, 1))
print("eslenen cift   :", sum(len(v) for v in ESLENMIS.values()))
print("esleme adimi   ->", maliyet(K, 0))
```

```
tek isci tabani -> {'sure': 2250, 'toplam_is': 2200, 'tasinan': 0}
eslenen cift   : 2200
esleme adimi   -> {'sure': 611, 'toplam_is': 2200, 'tasinan': 0}
```

İki bin iki yüz satırdan iki bin iki yüz çift çıkar; eşleme satır sayısını değiştirmez, satırın
biçimini değiştirir. Toplam iş tek işçilik tabandaki gibi **2200**'dür ve dört işçi süreyi
2250'den **611**'e indirir. Bu, kursun boyunca görülecek en temiz kazançtır ve sebebi eşlemenin
tanımıdır: satırlar arası bağımlılık olmadığı için bölüşüm anahtarı ne olursa olsun hesap doğrudur
ve hiçbir satır yer değiştirmez.

Buradan çıkan pratik sonuç şudur: bir hesabın ne kadarının eşleme adımına sığdığı, o hesabın ne
kadarının bedavaya paralelleşeceğini söyler. Süzme, alan çıkarma, birim çevirme ve satır başına
sınıflandırma bu adıma girer. Girmeyen her şey ikinci adıma kalır.

#### İndirgeme Ağa Dokunur

İkinci adım aynı anahtarın bütün değerlerini ister. Bölge anahtarı dört işçinin dördüne de
yayılmıştır, yani `merkez` toplamını hesaplayacak işçinin elinde `merkez` çiftlerinin yalnız dörtte
biri vardır. Karıştırma bu yüzden doğar. Aşağıdaki blok iki indirgeme kurulumunu karşılaştırır ve
her ikisinin sonucunu tek işçilik tabanla denetler.

```python
ONCEDEN = {i: yerel_indirge(c) for i, c in ESLENMIS.items()}
TABAN = indirge({0: esle(S, lambda k: k["bolge"], lambda k: k["m3"])})
print("ara satir , on-indirgemeden sonra:", sum(len(v) for v in ONCEDEN.values()))
print()
print(f"{'kurulum':<26}{'tasinan':>9}{'sure':>7}{'toplam is':>11}{'sonuc':>17}")
for ad, girdi in (("indirgeme , dogrudan", ESLENMIS),
                  ("indirgeme , on-indirgemeli", ONCEDEN)):
    y, t = karistir(girdi, "anahtar", ISCI)
    m = maliyet(K, t)
    esit = "taban ile ayni" if indirge(y) == TABAN else "farkli"
    print(f"{ad:<26}{t:>9}{m['sure']:>7}{m['toplam_is']:>11}{esit:>17}")
print()
print("taban sonuc:", TABAN)
```

```
ara satir , on-indirgemeden sonra: 20

kurulum                     tasinan   sure  toplam is            sonuc
indirgeme , dogrudan           1738   1914       7414   taban ile ayni
indirgeme , on-indirgemeli       15    622       2245   taban ile ayni

taban sonuc: {'merkez': 14536, 'kuzey': 13338, 'dogu': 9695, 'bati': 9164, 'guney': 11721}
```

Birinci satır iki adımın farkını yazar. Aynı iki bin iki yüz çift, eşleme adımında **0** satır
taşırken indirgeme adımında **1738** satır taşır; iki bin iki yüz çiftin yüzde 79'u işçi
değiştirir. Toplam iş **2200**'den **7414**'e çıkar ve süre 611'den **1914**'e. Ağdan geçen 1738
satırın bedeli satır başına üç birimdir, yani 5214 birim ve toplam işteki artışın tamamı budur.

Buradaki sayı hesabın zorluğundan gelmiyor. Bölge toplamı almak, tüketim farkı almaktan daha ağır
bir işlem değildir; ağır olan, verinin **yanlış anahtara göre** bölüşülmüş olmasıdır. Önceki derste
`bolge` anahtarıyla bölüşmenin çarpıklığı 3,32'ye çıkardığı ölçülmüştü. Bedava bir anahtar yoktur:
`abone` bölüşümü eşlemeyi dengeler ve indirgemeyi karıştırmaya zorlar, `bolge` bölüşümü
karıştırmayı sıfırlar ve iki işçiyi boş bırakır.

#### Ön-İndirgeme Aynı Sonucu Ucuza Üretir

İkinci satır modelin asıl kazancını verir. Her işçi kendi payındaki çiftleri **taşımadan önce**
kendi içinde toplarsa, dört işçiden çıkan iki bin iki yüz çift **20 ara satıra** iner: dört işçinin
her biri en çok beş bölge için birer kısmi toplam üretir. Karıştırma artık bu yirmi satırı taşır ve
bunların **15**'i işçi değiştirir. Süre **622**, toplam iş **2245** olur; doğrudan karıştırmaya göre
toplam iş **7414**'ten düşmüş, üç kata yakın bir fark kapanmıştır.

Son sütun üç kurulumda da aynı şeyi söyler. Beş bölge toplamı doğrudan karıştırmada da,
ön-indirgemede de tek işçilik tabanla **birebir aynıdır**. Bu, kursun en rahatsız edici gözlemidir:
7414 birimlik kurulumla 2245 birimlik kurulum aynı çıktıyı verir ve çıktıya bakan hiçbir denetim
ikisini ayırt edemez. Yanlış olan hesap değil, bedeldir; bedel ise yalnızca sayılırsa görünür.

Yirmi sayısının nereden geldiği kazancın sınırını da verir. Dört işçinin her biri en çok beş ayrı
bölge gördüğü için ön-indirgeme 2200 çifti 20 satıra sıkıştırabilmiştir; anahtarın **ayrık değer
sayısı** büyüdükçe sıkışma azalır. Son blok bunu dört indirgeme anahtarı üzerinde ölçer.

```python
ANAHTAR = (("bolge", lambda k: k["bolge"]),
           ("donem", lambda k: k["donem"]),
           ("bolge+donem", lambda k: k["bolge"] + "|" + k["donem"]),
           ("abone", lambda k: k["abone"]))
print(f"{'indirgeme anahtari':<20}{'ayrik':>7}{'ara satir':>11}"
      f"{'dogrudan':>10}{'on-indirgemeli':>16}{'toplam is':>11}")
for ad, f in ANAHTAR:
    E = {i: esle(pay, f, lambda k: k["m3"]) for i, pay in K.items()}
    O = {i: yerel_indirge(c) for i, c in E.items()}
    _, td = karistir(E, "anahtar", ISCI)
    _, t = karistir(O, "anahtar", ISCI)
    print(f"{ad:<20}{len(set(f(k) for k in S)):>7}{sum(len(v) for v in O.values()):>11}"
          f"{td:>10}{t:>16}{maliyet(K, t)['toplam_is']:>11}")
```

```
indirgeme anahtari    ayrik  ara satir  dogrudan  on-indirgemeli  toplam is
bolge                     5         20      1738              15       2245
donem                    11         44      1651              33       2299
bolge+donem              55        220      1653             165       2695
abone                   200        200         0               0       2200
```

Dördüncü ve üçüncü sütun aynı hesabın iki yazımıdır ve aradaki oran anahtarla birlikte kapanır.
Beş ayrık değerli `bolge` anahtarında taşınan satır 1738'den **15**'e iner; elli beş ayrık değerli
`bolge+donem` anahtarında 1653'ten **165**'e iner ve toplam iş 2245 yerine **2695** olur. Sıkışma
oranı ara satır sayısıyla belirlenir, o da anahtarın ayrık değer sayısını dört işçiyle çarpar. Ayrık
değer sayısı satır sayısına yaklaşırsa ön-indirgeme hiçbir şey sıkıştıramaz ve doğrudan
karıştırmayla aynı bedeli öder.

Son satır ayrı bir durumdur. İndirgeme anahtarı bölüşüm anahtarıyla aynı olduğunda, yani `abone`
anahtarına indirgendiğinde, doğrudan yazımda bile taşınan satır **0**'dır: her abonenin bütün
satırları zaten tek bir işçidedir. Karıştırma bir zorunluluk değil, indirgeme anahtarı ile verinin
girdiği düzenin **uyuşmamasının** bedelidir. Bu satırın toplam işi **2200**'dür, yani eşleme
adımının kendisiyle aynı.

Ön-indirgemenin bir koşulu vardır ve her hesap onu karşılamaz. İşlemin **sırası ve gruplaması
değiştirilebilir** olmalıdır: toplama böyledir, çünkü kısmi toplamların toplamı bütünün toplamına
eşittir. En küçük ve en büyük değer de böyledir. Ortanca değer değildir; bir işçinin payının
ortancası hesaplanıp öbürleriyle toplanamaz, çünkü ortanca bütün değerleri görmeyi ister. Tekil
değer sayımı da değildir: iki işçinin kendi paylarındaki tekil abone sayıları toplandığında ortak
aboneler iki kez sayılır. Bu hesaplarda ya bütün satırlar taşınır, ya da yaklaşık bir sonuç kabul
edilir. Model her hesabı ucuzlatmaz; hangilerini ucuzlattığını **söyler**.

#### Özet

- **Eşle-indirge** bir hesabı iki adıma ayırır: **eşleme** her satıra bağımsız uygulanır ve bir
  (anahtar, değer) çifti üretir, **indirgeme** bir anahtarın bütün değerlerini tek bir sonuca katlar.
- Eşleme adımı yereldir. 2200 satır 2200 çift üretir, taşınan satır **0**'dır, toplam iş **2200**'de
  kalır ve dört işçi süreyi 2250'den **611**'e indirir.
- İndirgeme adımı ağa dokunur. `abone` düzeninden `bolge` anahtarına geçerken **1738** satır işçi
  değiştirir; toplam iş **7414**'e, süre **1914**'e çıkar. Artışın tamamı taşınan satırın üç birimlik
  bedelidir.
- Her işçi kendi payını taşımadan önce indirgediğinde 2200 çift **20 ara satıra** iner, bunların
  **15**'i taşınır ve aynı sonuç **2245** toplam iş ile **622** süre karşılığında üretilir.
- Üç kurulumun sunduğu beş bölge toplamı birebir aynıdır. Çıktıya bakan bir denetim 7414 birimlik
  kurulumla 2245 birimliği **ayırt edemez**; fark yalnız bedeldedir.
- Ön-indirgeme yalnız sırası ve gruplaması değiştirilebilen işlemlerde geçerlidir. Toplam, en küçük
  ve en büyük değer uygundur; ortanca ve tekil değer sayımı değildir.

#### Sonraki Adım

Üç derste iş bölündü, yerleştirildi ve çalıştırıldı. Her ölçümde hesabın tamamı koştu: iki bin iki
yüz satırın hepsi eşlendi, hepsi taşındı, hepsi indirgendi. Oysa bir raporun yalnız bir bölgeyi ya
da son üç dönemi istediği durumda bu satırların çoğuna hiç dokunulmaması gerekirdi ve hangi hesabın
**gerçekten gerekli** olduğu hiç sorulmadı. İşçilerin kümeye nasıl paylaştırıldığı da sorulmadı;
dört işçi her ölçümde hazır varsayıldı. Sonraki iki ders bu iki soruyu ölçer: sonuçta kullanılmayan
bir hesap ne kadar satırı hiç dokunulmadan bırakır ve işçi sayısını artırmanın sabit bedeli
nerede kazancı yer.

### Dağıtık Veri Kümeleri

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/dagitik-veri-kumeleri

Bir dağıtık veri kümesinin bölümlerden ve henüz koşmamış bir plandan oluşması ile tembel değerlendirmenin elediği hesabın sayılması: on bir bölüm dört işçiye üçer üçer ve bir kez ikili dağılırken istekli kurulum beş satırlık bir sonuç için 44 bölüm açıp 6556 satır okuyor ve 3727 ara satır yazıyor, aynı sonucu tembel kurulum tek bölüm açarak 200 satırla veriyor ve 2000 satır hiç okunmadan kalıyor, yüz satırlık sonuçta okunan satır üçe katlanırken üç bölüm üç ayrı işçide koştuğu için süre 250'de kalıyor, bütün sonucu isteyen sayma eyleminde tembel kurulum 2200 satır okuyor ve fark yalnız kullanılmayan daldan ile ara satır yazımından geliyor, planın kurulması hiçbir satır okumadığı için plandaki tür hatası ancak ilk eylemde ve 200 satır okunduktan sonra görünüyor.

Önceki üç derste iş bölündü ve çalıştırıldı: satırlar bir anahtara göre işçilere dağıtıldı,
blokların nerede durduğu okumanın nereye gideceğini belirledi, eşleme ile indirgeme birbirinden
ayrıldı. Üçünün de ortak bir varsayımı vardı ve hiçbir yerde yazılmadı: sürücünün yazdığı her
hesap koşar. Bir dönüşüm tanımlandığı anda küme onu işletir, sonucunu bir yere yazar ve
sonrakini bekler.

Bu ders o varsayımı kaldırır ve tek bir soru sorar: yazılan hesabın ne kadarı sonuçta **gerçekten
kullanılıyor**. Yanıt iki sayıyla verilir — hiç açılmayan bölüm ve hiç okunmayan satır. Bu iki
sayıyı mümkün kılan yapı **dağıtık veri kümesidir** (distributed dataset): bölümlerden ve **henüz
koşmamış bir plandan** oluşur.

- **DH41.** Kaynak, hat ve küme **kurgudur**; gerçek bir dağıtık işleme çatısı ya da kaynak
  yöneticisi çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **DH42.** **Süre bu modelin kendi birimidir**, ham milisaniye değildir: en yüklü işçinin okuduğu
  satır sayısı artı görev sabiti.
- **DH43.** **Bölüm verinin birimidir, işçi hesabın birimidir.** Bölüm anahtarı `donem`; tüketim
  aşaması ilk dönemi düşürdüğü için **11 bölüm** kalır, her biri **200 satır**.
- **DH44.** Bölümler işçilere sırayla verilir (0, 1, 2, 3, 0, …); üç işçi üçer, biri iki bölüm
  tutar.
- **DH45.** **Dönüşüm** yeni bir küme tanımlar ve sonuç döndürmez; **eylem** sürücüye bir sonuç
  döndürür ve hesabı başlatan tek şeydir.
- **DH46.** Sayılan birimler: açılan bölüm, okunan satır, yazılan ara satır, süre. Bu derste
  **karıştırma yoktur**, bu yüzden toplam iş okunan satıra eşittir.
- **DH47.** Taban çizgisi **istekli değerlendirmedir**: her dönüşüm tanımlandığı anda koşar ve ara
  sonucunu yazar.
- **DH48.** Çözünürlük: bölüm **11**, bölüm başına satır **200**, toplam **2200**. Sunum üzerinde
  oran basılmaz, değerin kendisi yazılır.

#### Dönüşüm ile Eylem

Dağıtık bir kümede iki tür çağrı vardır ve aralarındaki fark bu dersin tamamıdır. **Dönüşüm**
(transformation) mevcut kümeden yeni bir küme tanımlar: süzme, eşleme, yeniden bölümleme. Geri
döndürdüğü şey veri değil, **tarif**tir. **Eylem** (action) ise sürücüye bir sonuç döndürür:
sayma, ilk satırları getirme, toplama. Yalnız eylem hesabı başlatır.

**Tembel değerlendirme** (lazy evaluation) bu ayrımın sonucudur. Dönüşümler bir plana yazılır,
ilk eylem gelene kadar hiçbir bölüm açılmaz. Eylem geldiğinde plan bir bütün olarak bilinmektedir
ve küme yalnız o sonucun gerektirdiği bölümleri koşturur. Karşıtı **istekli değerlendirmedir**:
her dönüşüm yazıldığı satırda koşar, ara sonucunu yazar ve bir sonraki dönüşüm onu yeniden okur.

İlk blok kurgu kaynağı ve hattın ilk üç aşamasını kurar. Buradan çıkan 2200 satır kursun tek
somut eksenidir.

```python
# KURGUDUR. Sayac okumalari, hattin ilk uc asamasi ve kurgu kume ders icinde
# tanimlanir; gercek bir dagitik isleme catisi ya da kaynak yoneticisi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


ISCI = 4
GOREV_SABIT = 50       # her isciye is vermenin sabit bedeli , bir secimdir


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v
```

#### Bölüm Verinin Birimi, İşçi Hesabın Birimi

Bölüm sayısı ile işçi sayısı aynı şey değildir ve bu dersin ölçtüğü kazanç tam olarak bu
ayrımdan doğar. **Bölüm** verinin bölünmüş bir parçasıdır ve tek başına okunabilir. **İşçi** o
parçaları okuyan hesap birimidir ve birden çok bölüm tutabilir. Burada 11 bölüm dört işçiye
dağılır: üç işçi üçer, biri iki bölüm alır. Bölüm işçiden çok olduğu için bir eylem **bölümlerin
yalnız bir kısmını** açabilir ve açılmayan bölümün satırları hiç okunmaz.

Bölümün depolama tarafındaki kullanımı — hangi bölümün dosya düzenine bakılarak hiç
okunmayacağına karar verilmesi — Veri Depolama Mimarileri kursunda ölçüldü ve burada
tekrarlanmaz. Buradaki eleme hesap tarafındadır: bölümü eleyen şey dosya düzeni değil, **eylemin
ne istediğidir**.

```python
S = satirlar()
BOLUM = {d: [k for k in S if k["donem"] == d] for d in DONEM}
BOLUM = {d: v for d, v in BOLUM.items() if v}
AD = sorted(BOLUM)
SAHIP = {a: i % ISCI for i, a in enumerate(AD)}
SAYAC = {}


def sifirla():
    SAYAC.update({"bolum": 0, "okunan": 0, "ara": 0,
                  "isci": {i: 0 for i in range(ISCI)}})


def oku(ad, veri):
    SAYAC["bolum"] += 1
    SAYAC["okunan"] += len(veri)
    SAYAC["isci"][SAHIP[ad]] += len(veri)
    return veri


class Tembel:
    """Donusum plana yazilir , kosmaz. Hesap ancak bir eylemle baslar."""

    def __init__(self, bolum, plan=()):
        self.bolum, self.plan = bolum, tuple(plan)

    def sec(self, f):
        return Tembel(self.bolum, self.plan + (("sec", f),))

    def esle(self, f):
        return Tembel(self.bolum, self.plan + (("esle", f),))

    def _kos(self, ad):
        cikti = []
        for k in oku(ad, self.bolum[ad]):
            for tur, f in self.plan:
                if tur == "sec" and not f(k):
                    k = None
                    break
                if tur == "esle":
                    k = f(k)
            if k is not None:
                cikti.append(k)
        return cikti

    def say(self):
        return sum(len(self._kos(a)) for a in AD)

    def ilk(self, n):
        c = []
        for a in AD:
            c += self._kos(a)
            if len(c) >= n:
                break
        return c[:n]


class Istekli(Tembel):
    """Her donusum tanimlandigi anda kosar ve ara sonucunu yazar."""

    def _uygula(self, f, sec):
        yeni = {}
        for a in AD:
            v = [k for k in oku(a, self.bolum[a]) if f(k)] if sec \
                else [f(k) for k in oku(a, self.bolum[a])]
            SAYAC["ara"] += len(v)
            yeni[a] = v
        return Istekli(yeni)

    def sec(self, f):
        return self._uygula(f, True)

    def esle(self, f):
        return self._uygula(f, False)

    def _kos(self, ad):
        return self.bolum[ad]


print("bolum:", len(BOLUM), " bolum basina satir:", sorted(set(len(v) for v in BOLUM.values())))
print("isci basina bolum:", [sum(1 for a in AD if SAHIP[a] == i) for i in range(ISCI)])
```

```
bolum: 11  bolum basina satir: [200]
isci basina bolum: [3, 3, 3, 2]
```

İki sınıf da aynı arayüzü sunar; ayrım yalnız hesabın **ne zaman** koştuğudur. `Tembel` sınıfında
`sec` yeni bir plan döndürür, `Istekli` sınıfında aynı çağrı bütün bölümleri okur.

#### Tembelliğin Elediği Hesap

Ölçülen iş sürücünün yazdığı sıradan bir betiktir: mesken abonelerinin okumaları süzülür, satır
dört alana indirgenir ve 30 m3 üstündekiler seçilir. Betik ayrıca bir `isyeri` kümesi de tanımlar
ve **hiçbir eylem onu istemez** — bu, elde tutulan hesabın ölçüsüdür. Dört kurulum aynı betiği
koşturur ve eylem değişir: `ilk(5)` beş satır ister, `ilk(100)` yüz satır, `say` bütün sonucu.

```python
ALAN = ("abone", "donem", "bolge", "m3")


def betik(K):
    """Surucunun yazdigi is: mesken abonelerinin 30 m3 ustundeki okumalari."""
    mesken = K.sec(lambda k: k["tip"] == "mesken")
    K.sec(lambda k: k["tip"] == "isyeri")          # hicbir eylem bunu istemiyor
    return mesken.esle(lambda k: {a: k[a] for a in ALAN}).sec(lambda k: k["m3"] > 30)


def olc(kip, eylem):
    sifirla()
    y = betik(Tembel(BOLUM) if kip == "tembel" else Istekli(BOLUM))
    n = y.say() if eylem == "say" else len(y.ilk(int(eylem)))
    return (SAYAC["bolum"], SAYAC["okunan"], SAYAC["ara"],
            max(SAYAC["isci"].values()) + GOREV_SABIT, n)


print(f"{'kip':<9}{'eylem':>7}{'acilan bolum':>14}{'okunan':>8}"
      f"{'ara satir':>11}{'sure':>6}{'sonuc':>7}")
for kip, eylem in (("istekli", "5"), ("tembel", "5"), ("tembel", "100"),
                   ("istekli", "say"), ("tembel", "say")):
    b, o, ara, sure, n = olc(kip, eylem)
    print(f"{kip:<9}{eylem:>7}{b:>14}{o:>8}{ara:>11}{sure:>6}{n:>7}")
```

```
kip        eylem  acilan bolum  okunan  ara satir  sure  sonuc
istekli        5            44    6556       3727  1838      5
tembel         5             1     200          0   250      5
tembel       100             3     600          0   250    100
istekli      say            44    6556       3727  1838    449
tembel       say            11    2200          0   650    449
```

İlk iki satır dersin bulgusudur ve **sonuç sütunu ikisinde de aynıdır**. İstekli kurulum beş
satırlık sonuç için 44 bölüm açıyor, 6556 satır okuyor, 3727 ara satır yazıyor; süre
1838. Aynı beş satırı tembel kurulum **tek bölüm** açarak, **200 satır** okuyarak ve hiç ara satır
yazmadan veriyor; süre 250. Geriye kalan **10 bölüm hiç açılmıyor ve 2000 satır hiç okunmuyor.**

İstekli kurulumun 6556 satırı iki yerden geliyor. Birincisi: kaynağın 2200 satırı **iki kez**
okunuyor, çünkü `mesken` ve `isyeri` süzmeleri ayrı ayrı bütün bölümlerden geçiyor. İkincisi:
`isyeri` dalı 2200 satır okuyup 1122 ara satır yazıyor ve **sonuca hiç girmiyor**. Tembel
kurulumda o dal bir plandan ibarettir ve plan koşmaz.

Üçüncü satır kursun kuralını tek başına taşıyor. `ilk(100)` üç bölüm açıyor, okunan satır 200'den
600'e çıkıyor — **toplam iş üçe katlanıyor** — ama süre **250'de kalıyor**. Nedeni DH44'tür: sıralı
dağıtım yüzünden ilk üç bölüm üç ayrı işçidedir ve üçü aynı anda koşar. En yüklü işçi yine 200
satır okur. **Hangisinin ölçüldüğü yazılmayan bir hızlanma iddiası burada eksik kalır**: aynı iki
kurulum arasında toplam iş üç kat, süre bir kat.

Son iki satır tembelliğin sınırını çiziyor. `say` bütün sonucu istediği için tembel kurulum da 11
bölümün hepsini açıyor ve 2200 satır okuyor. Fark artık atlanan bölümden değil, **kullanılmayan
daldan ve yazılmayan 3727 ara satırdan** geliyor: 6556'ya karşı 2200 satır, 1838'e karşı 650
süre.

#### Bölüm Boyutu Tembelliğin Çözünürlüğüdür

Tembellik bir bölümü atlayabilir, bölümün içinden bir satırı atlayamaz. Öyleyse atlanabilen en
küçük parça bölümün kendisidir ve **bölüm sayısı bir karardır**. Aynı 2200 satır dört, on bir ve
kırk dört bölüme konur; ölçülen iş ve eylem değişmez.

```python
KAYIT = (BOLUM, AD, SAHIP)


def yeniden_bolumle(sayi):
    """Bolum sayisi bir karardir: tembelligin atlayabildigi en kucuk birim bir bolumdur."""
    global BOLUM, AD, SAHIP
    BOLUM = {i: S[i * len(S) // sayi:(i + 1) * len(S) // sayi] for i in range(sayi)}
    AD, SAHIP = sorted(BOLUM), {i: i % ISCI for i in range(sayi)}


print(f"{'bolum':>6}{'satir/bolum':>13}{'ilk(5) acilan':>15}{'ilk(5) okunan':>15}"
      f"{'ilk(5) sure':>13}{'say sure':>10}")
for sayi in (4, 11, 44):
    yeniden_bolumle(sayi)
    b, o, _, sure, _ = olc("tembel", "5")
    _, _, _, tam, _ = olc("tembel", "say")
    print(f"{sayi:>6}{len(S) // sayi:>13}{b:>15}{o:>15}{sure:>13}{tam:>10}")
BOLUM, AD, SAHIP = KAYIT
```

```
 bolum  satir/bolum  ilk(5) acilan  ilk(5) okunan  ilk(5) sure  say sure
     4          550              1            550          600       600
    11          200              1            200          250       650
    44           50              1             50          100       600
```

Açılan bölüm üç kurulumda da **birdir**; değişen, o bölümün kaç satır taşıdığıdır. Beş satırlık
sonuç için okunan satır 550'den 50'ye iner ve süre 600'den 100'e. **Tembelliğin kazancı bölüm
boyutuyla ölçülür**: kaba bölünmüş bir kümede tembellik yazılıdır ama eleyecek bir şey bulamaz.

Son sütun ters yönü gösteriyor. Bütün sonucu isteyen `say` eyleminde 11 bölüm **650**, dört ve
kırk dört bölüm **600** süre veriyor. Neden bölüm sayısının işçi sayısına bölünmemesidir: 11 bölüm
dört işçiye [3, 3, 3, 2] dağıldığı için en yüklü işçi 600 satır okurken bir işçi 400 satırda boş
kalıyor. Bölümü inceltmenin görünmeyen bir bedeli de var — bu modelde süre hesabına `GOREV_SABIT`
yalnız **bir kez** ekleniyor, oysa kırk dört bölüm kırk dört kez iş verilmesi demektir.

#### Tembelliğin Ertelediği Hata

Planın koşmaması bir kazanç olduğu kadar bir gecikmedir. Sürücü on satırlık bir dönüşüm zinciri
yazdığında hiçbir satır okunmadığı için zincirin **doğru olup olmadığı da sınanmaz**. Aşağıdaki
plan bir sayıyı bir metne bölmeye kalkışıyor ve tanımlandığı yerde hiçbir şey olmuyor.

```python
sifirla()
bozuk = Tembel(BOLUM).esle(lambda k: dict(k, oran=k["m3"] / k["birim"]))
print("plan kuruldu, okunan satir:", SAYAC["okunan"])
try:
    bozuk.ilk(1)
except TypeError as e:
    print("eylem cagrilinca:", type(e).__name__, "-", e)
print("hataya kadar okunan satir:", SAYAC["okunan"])
```

```
plan kuruldu, okunan satir: 0
eylem cagrilinca: TypeError - unsupported operand type(s) for /: 'int' and 'str'
hataya kadar okunan satir: 200
```

Plan kurulurken okunan satır **sıfırdır**, yani hata plan yazılırken görünmez. İlk eylemde
görünür ve o ana kadar **200 satır** okunmuştur. İstekli kurulumda aynı hata `esle` çağrısının
yazıldığı satırda düşerdi. Eleme kazancı ile hata gecikmesi **aynı mekanizmadan** gelir: hesabın
ertelenmesi. Aynı mekanizmanın ikinci bir bedeli daha vardır — plan bir sonuç olmadığı için aynı
küme üzerinde iki eylem çağrıldığında zincir iki kez koşar; bu bedelin ölçümü kursun ikinci
konusuna aittir.

#### Özet

- Dağıtık veri kümesi **bölümlerden ve henüz koşmamış bir plandan** oluşur; dönüşüm plana yazılır,
  hesabı yalnız eylem başlatır ve plan kurulurken okunan satır **sıfırdır**.
- Bölüm verinin, işçi hesabın birimidir: 11 bölüm dört işçiye **[3, 3, 3, 2]** biçiminde dağılır ve
  bir eylem bölümlerin yalnız bir kısmını açabilir.
- Beş satırlık sonuçta istekli kurulum **44 bölüm açıp 6556 satır** okurken tembel kurulum **tek
  bölüm açıp 200 satır** okuyor; **10 bölüm hiç açılmıyor, 2000 satır hiç okunmuyor**.
- `ilk(100)` okunan satırı 200'den 600'e çıkarıyor ama süre **250'de kalıyor**; bütün sonucu
  isteyen `say` eyleminde kazanç kullanılmayan daldan ve yazılmayan **3727 ara satırdan** gelir
  (6556'ya karşı 2200 satır, 1838'e karşı 650 süre).
- Tembelliğin atlayabildiği en küçük birim bir bölümdür: aynı `ilk(5)` dört bölümlü kümede **550**,
  kırk dört bölümlü kümede **50** satır okur; buna karşılık bölüm sayısı işçi sayısına
  bölünmediğinde tam tarama süresi 600'den **650**'ye çıkar.
- Plandaki tür hatası ancak ilk eylemde ve **200 satır okunduktan sonra** görünür; eleme kazancı
  ile hata gecikmesi **aynı mekanizmadan** gelir.

#### Sonraki Adım

Bu ders hangi hesabın koşacağına karar verdi, ama o hesabı **kimin** koşturacağına hiç
dokunmadı. Bölümler işçilere sırayla verildi, işçi sayısı dört kabul edildi ve süre hesabında
`GOREV_SABIT` sessizce eklendi. Gerçek bir kümede bu üç şeyin hiçbiri sürücünün elinde değildir:
işi kaç işçiye böleceğine, o işçilerin kaçının aynı anda koşacağına ve bekleyen bir işin ne zaman
sıra alacağına **kaynak yöneticisi** karar verir. Sonraki ders o kararı ölçer ve iki düğmeyi
ayırır: görev sayısını büyütmek ile eşzamanlı görev sayısını büyütmek aynı şey değildir, çünkü her
görevin **50 birimlik sabit bedeli** vardır ve bu bedel görev sayısıyla birlikte büyür. Ölçülecek
sayı süre kazancının hangi görev sayısında bu bedele yenildiğidir.

### Kaynak Yöneticileri

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/kaynak-yoneticileri

Bir işin küme üzerinde zamanlanmasında görev sayısı ile eşzamanlı görev sayısının ayrı iki düğme olduğunun ve her görevin elli birimlik sabit bedelinin nereye gittiğinin sayılması: tek görev tek yuvada süre 2250 ve sabit bedel 50 iken on altı görev on altı yuvada süre 215'e iniyor ama sabit bedel 800'e çıkıyor, eşzamanlılığı büyütmek sabit bedeli hiç değiştirmezken görev sayısını büyütmek onu görev başına elli birim artırıyor, aynı eşzamanlılıkta işi daha çok göreve bölmek süreyi 611'den 950'ye yükseltiyor, süre kazancı sekiz görevden on altıya geçerken 121'de kalıp eklenen 400 birimlik sabit bedele yeniliyor ve aynı kümede iki iş çalıştığında öncelikli ile paylaşımlı politika bitiş sıralarını değiştiriyor ama ödenen 600 birimlik sabit bedeli değiştirmiyor.

Önceki ders hangi hesabın koşacağına karar verdi ve koşmayanı saydı, ama o hesabı **kimin**
koşturacağına hiç dokunmadı. Bölümler işçilere sırayla verildi, işçi sayısı dört kabul edildi ve
süre hesabına `GOREV_SABIT` sessizce bir kez eklendi. Gerçek bir kümede bu üç şeyin hiçbiri
sürücünün elinde değildir.

Bu ders arada duran bileşeni ölçer: **kaynak yöneticisi** (resource manager). Sürücü bir iş
gönderir, kaynak yöneticisi işi kaç göreve böleceğine, kaç görevin aynı anda koşacağına ve
bekleyen bir işin ne zaman sıra alacağına karar verir. Ölçülen sayı şudur: her görevin
**50 birimlik sabit bedeli** varken görev sayısını ve eşzamanlılığı büyütmek süreyi nereye kadar
düşürür ve **kazanç nerede bu bedele yenilir.**

- **DH49.** Küme, iş ve kaynak yöneticisi **kurgudur**; gerçek bir kaynak yöneticisi, küme
  yöneticisi ya da bulut servisi çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **DH50.** **Süre bu modelin kendi birimidir**, ham milisaniye değildir: en yüklü yuvanın
  bitirdiği iştir.
- **DH51.** **`GOREV_SABIT = 50` bir seçimdir** ve bu dersin bulgusu doğrudan ona dayanır; her
  sonuç değeriyle birlikte yazılır. Bedel **görev başına** ödenir: bir işçiye iki görev verilirse
  iki kez ödenir.
- **DH52.** **Görev** işçiye verilen paydır. **Yuva** (slot) kümede aynı anda koşabilen bir görev
  yeridir; bir yuva boşalınca sıradaki görevi alır. **Eşzamanlı görev sayısı** yuva sayısıdır.
- **DH53.** Bölüşüm anahtarı `abone` ve bu derste **karıştırma yoktur**; satır işi her kurulumda
  **2200**'dür. Yanlış anahtarın işçi eklemeyi anlamsızlaştırdığı durum bu kursun ikinci konusuna
  aittir.
- **DH54.** Kaynak yöneticisi büyük görevi önce koyar ve her görevi **en boş yuvaya** yerleştirir.
- **DH55.** Sayılan birimler: görev, eşzamanlı görev, süre, satır işi, **sabit bedel toplamı** ve
  **ödenen toplam** (satır işi artı sabit bedel). Taban çizgisi **tek görev, tek yuvadır**.
- **DH56.** Kaynak isteği, sınırı ve kuyruk yordamı Konteyner Orkestrasyonu kursunda, otomatik
  ölçekleme Bulutta Hesaplama ve Ağ kursunda ölçüldü ve **tekrarlanmaz**. Buradaki soru yalnız
  görev sayısı ile yuva sayısının süreye ve sabit bedele ne yaptığıdır.
- **DH57.** Çözünürlük: satır **2200**, görev en çok **32**, yuva en çok **16**. Süre ve sabit
  bedel tam sayıdır, oran basılmaz.

#### Kaynak Yöneticisi Neye Karar Verir

Sürücünün gönderdiği şey bir hesap planıdır; kaç makinede koşacağını söylemez. O kararı kaynak
yöneticisi verir ve karar üç parçalıdır. Birincisi **görev sayısı**: iş kaç paya bölünecek.
İkincisi **yuva sayısı**: bu payların kaçı aynı anda koşacak. Üçüncüsü **sıra**: küme başka
işlerle doluysa bu iş ne zaman başlayacak.

Üç kararın da ortak bir bedeli vardır. Bir göreve iş vermek bedava değildir: plan gönderilir,
yuva ayrılır, işçi tarafında bir yürütücü hazırlanır. Bu modelde o bedelin adı `GOREV_SABIT` ve
değeri **50**'dir. Değer bir seçimdir ama seçildiği anda bir sonuç doğurur: **görev sayısını
büyütmek sabit bedeli görev başına 50 artırır**, yuva sayısını büyütmek onu hiç değiştirmez.
İki düğme aynı düğme değildir.

```python
# KURGUDUR. Kaynak, hattin ilk uc asamasi, kume ve kaynak yoneticisi ders icinde
# tanimlanir; gercek bir kaynak yoneticisi ya da kume yoneticisi cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


ISCI = 4
AG_BEDELI = 3          # bir satiri agdan tasimanin bedeli , yerel islemin kac kati
GOREV_SABIT = 50       # bir goreve is vermenin sabit bedeli , bir secimdir


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


def dagit(kayitlar, anahtar, isci=ISCI):
    kova = {i: [] for i in range(isci)}
    for k in kayitlar:
        kova[izno(k[anahtar]) % isci].append(k)
    return kova


def maliyet(kova, tasinan=0, isci=ISCI):
    """Sure = en yuklu iscinin isi (paralel). Toplam is ayrica sayilir."""
    n = [len(v) for v in kova.values()]
    return {"sure": max(n) + tasinan * AG_BEDELI // isci + GOREV_SABIT,
            "toplam_is": sum(n) + tasinan * AG_BEDELI,
            "tasinan": tasinan}
```

#### Görev, Yuva ve Sabit Bedel

Zamanlayıcı gerçek bir kaynak yöneticisinin çekirdeğini taşır: görevler büyükten küçüğe sıraya
konur, her görev en boş yuvaya yerleştirilir ve her yerleşim kendi sabit bedelini öder. Süre en
yüklü yuvanın bitirdiği iştir.

```python
S = satirlar()


def paylar(kayitlar, gorev):
    """Is 'gorev' paya bolunur; ayni anahtar hep ayni paya gider."""
    return sorted((len(v) for v in dagit(kayitlar, "abone", gorev).values()), reverse=True)


def zamanla(pay, eszamanli, sabit=GOREV_SABIT):
    """Kaynak yoneticisi: ayni anda en cok 'eszamanli' gorev kosar. Bosalan yuva
    siradaki gorevi alir , ve her gorev kendi sabit bedelini oder."""
    yuva = [0] * eszamanli
    for p in pay:
        i = yuva.index(min(yuva))
        yuva[i] += p + sabit
    return {"gorev": len(pay), "sure": max(yuva), "satir_isi": sum(pay),
            "sabit": len(pay) * sabit, "odenen": sum(pay) + len(pay) * sabit}


print("maliyet (4 isci , karistirma yok):", maliyet(dagit(S, "abone", 4)))
print("zamanla (4 gorev , 4 yuva)       :", zamanla(paylar(S, 4), 4))
print("taban  (1 gorev , 1 yuva)        :", zamanla(paylar(S, 1), 1))
```

```
maliyet (4 isci , karistirma yok): {'sure': 611, 'toplam_is': 2200, 'tasinan': 0}
zamanla (4 gorev , 4 yuva)       : {'gorev': 4, 'sure': 611, 'satir_isi': 2200, 'sabit': 200, 'odenen': 2400}
taban  (1 gorev , 1 yuva)        : {'gorev': 1, 'sure': 2250, 'satir_isi': 2200, 'sabit': 50, 'odenen': 2250}
```

İlk iki satır aynı süreyi veriyor: **611**. Bu bir denetimdir, kursun `maliyet` işlevi ile bu
dersin zamanlayıcısı dört görev dört yuvada aynı sonucu üretiyor. Fark üçüncü sayıdadır.
`maliyet` sabit bedeli süreye **bir kez** ekler, çünkü orada her işçiye tam bir görev düşer;
`zamanla` onu ayrı bir kalem olarak sayar ve dört görevin toplam sabit bedelini **200** olarak
yazar. Taban çizgisi son satırdır: tek görev, tek yuva, süre **2250**, sabit bedel **50**.

#### İki Düğme Aynı Düğme Değildir

Aşağıdaki matris görev sayısını satırlara, yuva sayısını sütunlara koyar. Her hücre süredir; sol
sütun her satırın ödediği sabit bedel toplamıdır ve **yuva sayısından bağımsızdır**.

```python
ESZ = (1, 2, 4, 8, 16)
print(f"{'gorev':>6}{'sabit bedel':>13}", "".join(f"{'esz=' + str(e):>9}" for e in ESZ))
for g in (4, 8, 16, 32):
    p = paylar(S, g)
    print(f"{g:>6}{g * GOREV_SABIT:>13}", "".join(f"{zamanla(p, e)['sure']:>9}" for e in ESZ))
```

```
 gorev  sabit bedel     esz=1    esz=2    esz=4    esz=8   esz=16
     4          200      2400     1200      611      611      611
     8          400      2600     1300      650      336      336
    16          800      3000     1500      750      386      215
    32         1600      3800     1900      950      475      243
```

Satır boyunca okuyunca yuva düğmesi görünür: on altı görevlik iş bir yuvada **3000**, on altı
yuvada **215** sürede biter ve ödenen sabit bedel iki uçta da **800**'dür. Eşzamanlılığı
büyütmek sabit bedele dokunmaz; yalnız aynı bedelin daha kısa sürede ödenmesini sağlar.

Sütun boyunca okuyunca ikinci düğme görünür ve ters yönde çalışır. Dört yuvada iş dört göreve
bölünürse süre **611**, sekize bölünürse **650**, on altıya **750**, otuz ikiye **950**. İş
değişmedi, satır işi hâlâ 2200. Yükselen tek şey sabit bedeldir: 200'den 1600'e. **Yuva sayısını
aşan her görev bir 50 öder ve karşılığında hiçbir paralellik satın almaz**, çünkü koşacak boş yer
yoktur.

İki düğme birbirine bağlıdır ve tablonun en pahalı köşesi bunu söyler. On altı yuvayı
kullanabilmek için işi en az on altı göreve bölmek gerekir, yani **215** süreye inmenin ön koşulu
**800** birimlik sabit bedeldir. Otuz iki göreve bölmek aynı on altı yuvada süreyi 215'ten
**243**'e yükseltir ve sabit bedeli **1600**'e çıkarır. Doygunluk da aynı yerde: dört görevlik iş
sekiz ya da on altı yuvada da **611** sürede biter, fazladan yuvalar boş durur.

#### Süre Kazancının Yenildiği Yer

Matrisin köşegeni en sık verilen kararı temsil eder: her göreve bir yuva, yani işi kaç işçiye
böleceğini seçmek. Kazancın nerede yenildiğini görmek için iki sütun yan yana konur — kazanılan
süre ve eklenen sabit bedel.

```python
print(f"{'gorev=esz':>10}{'carpiklik':>11}{'sure':>7}{'sure kazanci':>14}"
      f"{'eklenen sabit':>15}{'odenen':>8}")
onceki = None
for g in (1, 2, 4, 8, 16):
    p, z = paylar(S, g), zamanla(paylar(S, g), g)
    c = round(max(p) / (sum(p) / len(p)), 4)
    kaz = "-" if onceki is None else str(onceki[0] - z["sure"])
    ek = "-" if onceki is None else str(z["sabit"] - onceki[1])
    print(f"{g:>10}{c:>11}{z['sure']:>7}{kaz:>14}{ek:>15}{z['odenen']:>8}")
    onceki = (z["sure"], z["sabit"])
```

```
 gorev=esz  carpiklik   sure  sure kazanci  eklenen sabit  odenen
         1        1.0   2250             -              -    2250
         2        1.0   1150          1100             50    2300
         4       1.02    611           539            100    2400
         8       1.04    336           275            200    2600
        16        1.2    215           121            400    3000
```

İlk üç adım tartışmasızdır: tek görevden iki göreve geçmek süreyi **1100** birim düşürüyor ve
karşılığında **50** birim sabit bedel ekliyor. İkiden dörde kazanç 539, bedel 100. Dörtten sekize
kazanç 275, bedel 200. Kazanç her adımda yarılanırken bedel her adımda ikiye katlanıyor, yani iki
sayı birbirine doğru koşuyor.

Sekizden on altıya geçişte karşılaşıyorlar ve sıra tersine dönüyor: süre kazancı **121**, eklenen
sabit bedel **400**. **Süre kazancının yenildiği yer sekiz ile on altı görev arasındadır.** Bu,
sürenin düşmediği anlamına gelmez; süre 336'dan 215'e gerçekten iniyor. Anlamı şudur: ödenen
toplam 2600'den 3000'e çıkarken satır işi hiç değişmiyor, yani eklenen 400 birimin tamamı iş
değil **iş verme bedelidir**. Hangisinin ölçüldüğü yazılmadan bu satıra bakan biri saf bir
hızlanma görür.

Çarpıklık sütunu ikinci bir uyarı taşıyor. On altı görevde `abone` anahtarı bile **1,2**
çarpıklık üretiyor: en yüklü pay 165 satır, en hafifi 110. Pay küçüldükçe eşitliği bozulur ve
sabit bedel büyür; iki etki aynı yöne çalışır.

#### Aynı Kümede İki İş

Kaynak yöneticisinin asıl işi tek bir işi hızlandırmak değil, **kümeyi paylaştırmaktır**. Sekiz
yuvalık bir kümeye iki iş gelir: A bütün satırları işleyen sekiz görevlik iş, B yalnız mesken
abonelerini işleyen dört görevlik iş. Öncelikli politikada A kümenin tamamını alır ve B sırasını
bekler; paylaşımlı politikada küme ikiye bölünür.

```python
MESKEN = [k for k in S if k["tip"] == "mesken"]
A, B = paylar(S, 8), paylar(MESKEN, 4)
print("is A satir:", sum(A), " gorev:", len(A), "  is B satir:", sum(B), " gorev:", len(B))
for kip in ("oncelikli", "paylasimli"):
    if kip == "oncelikli":                       # once A butun kumeyi alir
        a = zamanla(A, 8)["sure"]
        b = a + zamanla(B, 8)["sure"]
    else:                                        # kume ikiye bolunur
        a, b = zamanla(A, 4)["sure"], zamanla(B, 4)["sure"]
    print(f"{kip:<12} A biter {a:>5}  B biter {b:>5}  ikisi {max(a, b):>5}"
          f"  sabit bedel {(len(A) + len(B)) * GOREV_SABIT:>5}")
```

```
is A satir: 2200  gorev: 8   is B satir: 1078  gorev: 4
oncelikli    A biter   336  B biter   683  ikisi   683  sabit bedel   600
paylasimli   A biter   650  B biter   347  ikisi   650  sabit bedel   600
```

Politika bitiş sırasını baştan aşağı değiştiriyor. Öncelikli kurulumda A **336**'da biter ve B
**683**'e kadar bekler; paylaşımlı kurulumda A **650**'ye gecikirken B **347**'de biter. İkisinin
birden bittiği an 683'ten 650'ye iner, yani paylaşımlı politika kümeyi biraz daha dolu tutar.
Üstün olan bir politika yoktur; seçilen şey hangi işin geciktiğidir.

Son sütun bu dersin toplayıcı cümlesidir: **sabit bedel iki politikada da 600'dür.** Zamanlama
politikası kimin ne zaman biteceğini belirler, **ödenen sabit bedeli belirlemez**. O bedel yalnız
görev sayısının işlevidir ve on iki görev on iki kez 50 öder. Kaynak yöneticisini değiştirerek
kazanılabilecek şey sıradır, iş değil.

#### Özet

- Kaynak yöneticisi üç şeye karar verir: **görev sayısı**, **yuva sayısı** ve **sıra**; ilk ikisi
  ayrı düğmelerdir ve sabit bedele farklı davranırlar.
- Yuva sayısını büyütmek sabit bedeli **hiç değiştirmez**: on altı görevlik iş bir yuvada 3000,
  on altı yuvada 215 sürede biter ve ödenen sabit bedel iki uçta da **800**'dür.
- Görev sayısını büyütmek sabit bedeli görev başına **50** artırır: dört yuvada süre 611'den
  **950**'ye çıkarken satır işi 2200'de kalır, çünkü yuva sayısını aşan görev paralellik satın
  almaz.
- Her göreve bir yuva verildiğinde süre kazancı her adımda yarılanır, eklenen sabit bedel her
  adımda ikiye katlanır; **sekiz ile on altı görev arasında kazanç 121'de kalıp eklenen 400 birime
  yenilir** ve ödenen toplam 2600'den 3000'e çıkar.
- On altı görevde `abone` anahtarı bile **1,2** çarpıklık üretir; payların eşitsizliği ile sabit
  bedelin büyümesi aynı yöne çalışır.
- Aynı kümede iki iş koşarken politika bitiş sırasını değiştirir (A 336 ve B 683 yerine A 650 ve
  B 347) ama **ödenen sabit bedeli değiştirmez**: iki kurulumda da **600**.

#### Sonraki Adım

Beş derste iş bölündü. Bir küme kuruldu ve sürücü, yürütücü ve görev adlarıyla ayrıldı; bloklar
yerleştirildi ve okumanın hangi işçiye düştüğü sayıldı; eşleme ile indirgeme birbirinden ayrıldı;
hesabın hangi parçasının hiç koşmayacağı ölçüldü; ve işin kaç göreve bölüneceğine kimin karar
verdiği yazıldı. Bütün bu derslerde tek bir sayı hep aynı kaldı ve hiç sorgulanmadı: **satır işi
2200**. İşi böldük, dağıttık, zamanladık, ama işin kendisine hiç dokunmadık.

Oysa dağıtık bir işin yavaşlığı çoğu zaman satır sayısından gelmez. Bir bölüşüm anahtarından
öbürüne geçmek satırları ağ üzerinden taşır ve o taşıma bu modelde yerel işlemin **üç katı**
bedelle sayılır; iki bağıntıyı birleştirmenin iki yolu vardır ve hangisinin ucuz olduğu boyuta
göre değişir; bir anahtar payları eşit bölmediğinde işçilerin bir bölümü boş durur. Sonraki konu bu üç kaynağı ayrı ayrı sayar ve ilk soruyu şöyle sorar: aynı iş, aynı işçi
sayısıyla, yalnız bölüşüm anahtarı değiştiği için kaç satır ağdan geçiyor ve toplam iş 2200'den
nereye çıkıyor.

## Başarım

Dağıtık bir işin nerede yavaşladığının ve hızlandırma girişimlerinin ne zaman işi kötüleştirdiğinin sayılması: aynı düzende karıştırmanın 1738 satır taşıyıp süreyi 611'den 1914'e ve toplam işi 2200'den 7414'e çıkarması, olgu tarafı bölgeyle bölünmüşken yayınlı birleştirmenin 1600 satırlık boyuta kadar kazanması ve kesişme noktasının 1600 ile 4000 arasında bulunması, abone anahtarında 1,02 olan çarpıklığın bölge anahtarında 3,32'ye çıkması ve dört işçinin ikisinin boş kalmasıyla işçi eklemenin anlamsızlaşması, ara sonucun önbelleğe alınmasıyla iki sorgunun toplamının 5500'den 3300'e inmesi, kontrol noktası aralığının altıdan üçe indirilmesinin aynı düşme noktasında yeniden hesaplanan satırı 400'de bırakması, tek işçide 2250 olan sürenin ikinci işçiyle 3691'e çıkması ve süre inerken toplam işin 5830'dan 8635'e çıkmasıyla hangi sayının ölçüldüğünün yazılması zorunluluğu.

### Karıştırma Maliyeti

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/karistirma-maliyeti

Dağıtık bir işin nerede yavaşladığının ilk kez sayılması: aynı 2200 satır abone anahtarıyla bölünmüşken abone anahtarına yeniden bölüştürüldüğünde taşınan satır 0 kalıyor, süre 611 ve toplam iş 2200 oluyor; aynı satırlar bolge anahtarına karıştırıldığında 1738 satır ağdan geçiyor, süre 1914'e ve toplam iş 7414'e çıkıyor, üstelik üç kurulum da birebir aynı sonucu üretiyor. Ağ bedeli 1, 3, 6 ve 12 değerleriyle süpürüldüğünde taşınan satır hiç değişmiyor ve karıştırmasız kurulumun 611 ile 2200'ü de sabit kalıyor, buna karşılık karıştırmalı kurulumun süresi 1045'ten 5825'e ve toplam işi 3938'den 23056'ya taşınıyor; üç anahtarın sıralaması dört değerde de aynı çıkıyor, dolayısıyla sıralama seçime duyarsız, büyüklük tümüyle seçimin cinsinden. Karıştırmanın bedeli hesabın kendisinden değil verinin yerinden oynatılmasından geliyor.

Beş derste iş bölündü. Sürücü çalışmayı görevlere ayırdı, işçiler bölümlerini aldı, kaynak
yöneticisi görevleri sıraya soktu ve dağıtık kurulum tek makinedeki hattın ürettiği sonucun
birebir aynısını üretti. Bölüşümün **çalıştığı** ölçüldü; **nerede yavaşladığı** hiç sorulmadı.

Bu ders o soruyu sorar ve yanıtını tek bir yerde arar: bir satır bir işçiden başka bir işçiye
geçtiğinde ne oluyor. Dağıtık bir işte hesabın kendisi her yerde aynıdır — toplama toplamadır,
karşılaştırma karşılaştırmadır. Ayrışan tek şey **verinin nerede durduğudur** ve veriyi yerinden
oynatmak yerinde işlemekten pahalıdır. Ölçülen şey bu derste o farktır.

- **DB1.** Kaynak, tüketim satırları ve küme **kurgudur** ve ders içinde üretilir. Hiçbir dağıtık
  işleme çatısı, kaynak yöneticisi ya da dağıtık dosya sistemi çağrılmaz. Tohum 20260218.
- **DB2.** **Süre** bu modelin **kendi birimidir**, ham milisaniye değildir. Süre **en yüklü
  işçinin işidir**, çünkü işçiler paralel çalışır ve iş en geç bitenle biter. **Toplam iş** bütün
  işçilerin işinin toplamıdır. İkisi ayrı ölçülür ve ayrı yönlere gidebilir.
- **DB3.** **Karıştırma (shuffle)** satırları yeni bir anahtara göre yeniden bölüştürmektir. İşçi
  değiştiren satır ağdan geçer ve **taşınan satır** olarak sayılır; işçisi değişmeyen satır ağa
  hiç dokunmaz.
- **DB4.** Ağdan geçen bir satırın bedeli yerel işlemin `AG_BEDELI` katıdır, öntanımlı değeri
  **3**'tür ve **bu bir seçimdir**. Bu ders onu 1, 3, 6 ve 12 değerleriyle süpürür.
- **DB5.** Her işçiye iş vermenin sabit bedeli `GOREV_SABIT` = **50**'dir ve o da bir seçimdir.
  Aynı işçi sayısındaki iki kurulum karşılaştırılırken bu sabit ikisine de eşit eklenir,
  dolayısıyla farkın içine girmez.
- **DB6.** Bölüşüm anahtarın karmasıyla yapılır: **aynı anahtar değeri her zaman aynı işçiye
  gider**. İndirgeme bunu gerektirir; bir anahtarın satırları tek bir işçide toplanmazsa o
  anahtarın toplamı hesaplanamaz.
- **DB7.** **Bölüm (partition)** burada bir işçiye verilen satır kümesidir. Bölümleme yordamı
  Veritabanları müfredatındaki İlişkisel Veritabanı Yönetimi kursunda ve bu müfredattaki Veri
  Depolama Mimarileri kursunda ölçüldü; **tekrarlanmaz**. **Parçalama (sharding)** ayrı bir
  kavramdır ve bölümlemeyle **karıştırılmaz**.
- **DB8.** Taban çizgisi **tek işçidir**: karıştırma yok, süre **2250**, toplam iş **2200**.
- **DB9.** Bu dersteki bütün kurulumlar **birebir aynı sonucu** üretir; ölçülen tek şey bedeldir.

#### Karıştırmanın İki Sayısı

Aşağıdaki blok hattan çıkan 2200 tüketim satırını `abone` anahtarıyla dört işçiye böler, sonra
aynı satırları üç ayrı anahtara göre yeniden bölüştürür ve her seferinde kaç satırın işçi
değiştirdiğini sayar.

```python
# KURGUDUR. Olcum aginin sayac okumalari , bes asamali hat ve kume ders icinde
# uretilir; hicbir dagitik isleme catisi , kaynak yoneticisi ya da dosya sistemi
# cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def satirlar():
    """Hattan cikan tuketim satirlari. Saglam kaynakta ayikla ve tekille asamalari
    satir dusurmez; tuketim onceki donemin endeksinden cikarilarak bulunur."""
    ham, cikti = kaynak_uret(), []
    tablo = {(k["abone"], k["donem"]): k for k in ham}
    for k in ham:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is not None:
            cikti.append(dict(k, m3=k["endeks"] - onceki["endeks"]))
    return cikti


ISCI = 4
AG_BEDELI = 3          # bir satiri agdan tasimanin bedeli , yerel islemin kac kati
GOREV_SABIT = 50       # her isciye is vermenin sabit bedeli


def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


def dagit(kayitlar, anahtar, isci=ISCI):
    """Satirlari anahtara gore isciye atar. Ayni anahtar hep ayni isciye gider."""
    kova = {i: [] for i in range(isci)}
    for k in kayitlar:
        kova[izno(k[anahtar]) % isci].append(k)
    return kova


def karistir(kova, yeni_anahtar, isci=ISCI):
    """Yeniden bolustur. Isci degistiren satir agdan gecer."""
    yeni = {i: [] for i in range(isci)}
    tasinan = 0
    for eski, kayitlar in kova.items():
        for k in kayitlar:
            hedef = izno(k[yeni_anahtar]) % isci
            if hedef != eski:
                tasinan += 1
            yeni[hedef].append(k)
    return yeni, tasinan


def maliyet(kova, tasinan=0, isci=ISCI):
    """Sure = en yuklu iscinin isi (paralel). Toplam is ayrica sayilir."""
    n = [len(v) for v in kova.values()]
    return {"sure": max(n) + tasinan * AG_BEDELI // isci + GOREV_SABIT,
            "toplam_is": sum(n) + tasinan * AG_BEDELI,
            "tasinan": tasinan}


TUM = satirlar()
TEK = maliyet(dagit(TUM, "abone", 1), 0, 1)
KOVA = dagit(TUM, "abone")
print(f"hattan cikan satir {len(TUM)} , isci {ISCI} , ag bedeli {AG_BEDELI} ,"
      f" gorev sabiti {GOREV_SABIT}")
print(f"tek iscilik taban: sure {TEK['sure']} , toplam is {TEK['toplam_is']} , tasinan 0")
print(f"baslangic duzeni abone , isci yuku {[len(v) for v in KOVA.values()]}")
print("\nyeni anahtar   tasinan  yerinde   sure  toplam is   yeni isci yuku")
for hedef in ("abone", "donem", "bolge"):
    yeni, t = karistir(KOVA, hedef)
    m = maliyet(KOVA, t)
    print(f"{hedef:<12} {t:>9} {len(TUM) - t:>8} {m['sure']:>6} {m['toplam_is']:>10}"
          f"   {[len(v) for v in yeni.values()]}")
```

```
hattan cikan satir 2200 , isci 4 , ag bedeli 3 , gorev sabiti 50
tek iscilik taban: sure 2250 , toplam is 2200 , tasinan 0
baslangic duzeni abone , isci yuku [539, 550, 561, 550]

yeni anahtar   tasinan  yerinde   sure  toplam is   yeni isci yuku
abone                0     2200    611       2200   [539, 550, 561, 550]
donem             1651      549   1849       7153   [600, 600, 400, 600]
bolge             1738      462   1914       7414   [374, 0, 0, 1826]
```

İlk satır bir sınır çizer: karıştırmasız bir dağıtık iş **mümkündür**. `abone` düzenindeki
satırlar yine `abone` anahtarına bölüştürüldüğünde **hiçbir satır işçi değiştirmez**. Taşınan
satır 0, süre 611, toplam iş 2200. Toplam iş tek işçilik tabanla birebir aynıdır — dört işçi
aynı 2200 birimlik işi paylaşmıştır, ne eksik ne fazla. Süre 2250'den 611'e inmiştir ve inişin
tamamı paylaşmadan gelir.

Üçüncü satır aynı verinin, aynı işçi sayısında, aynı sonucu üretmek için ödediği bambaşka bir
bedeli gösterir. `bolge` anahtarına geçmek 2200 satırın **1738**'ini ağdan geçirir; yalnız 462
satır yerinde kalır. Süre 611'den **1914**'e, toplam iş 2200'den **7414**'e çıkar. Hesap
değişmemiştir: aynı toplamlar, aynı bölgeler, aynı çıktı. Değişen tek şey satırların **nerede
durduğudur** ve o taşınma, işin kendisinden büyük bir kalem yazmıştır. Toplam işin 5214 birimi,
yani üçte ikisinden fazlası, hiçbir hesap yapmayan saf hareket bedelidir.

Sürenin neyden oluştuğu burada açık yazılmalıdır. `bolge` satırındaki 1914 üç kalemin
toplamıdır: en yüklü işçinin 561 satırı, taşınan 1738 satırın ağ bedeliyle çarpılıp işçi sayısına
bölünmesinden gelen 1303 birim ve 50 birimlik görev sabiti. Taşıma bedelinin işçi sayısına
bölünmesi bir modelleme seçimidir ve ağ trafiğinin işçiler arasında **eşit paylaşıldığını**
varsayar. Gerçek bir kümede tek bir işçi taşımanın büyük payını üstlenebilir ve süre bundan
yüksek çıkar. Model bu yüzden karıştırmanın bedelini olduğundan **az** gösterir; buradaki fark
bir alt sınırdır.

Ortadaki satır taşınan satırın anahtardan anahtara değiştiğini gösterir: `donem` anahtarına
geçmek 1651 satır taşır, `bolge`'ye geçmek 1738. Fark küçüktür ama nedeni açıktır. Bir satırın
yerinde kalması, eski anahtarının ve yeni anahtarının **aynı işçiye düşmesi** demektir. Dört
işçi varken bunun beklenen payı dörtte bir, yani 550 satırdır. `donem`'de yerinde kalan 549 satır
tam bu payı verir; `bolge`'de kalan 462 satır ondan azdır, çünkü bölgeler işçilere eşit
dağılmamıştır. Karıştırmanın taşımadığı satır, kurtarılan satır değil, **tesadüfen aynı yere
düşen** satırdır.

Son sütun karıştırmanın ikinci yüzünü açar ve bir sonraki iki dersin konusunu kurar. `abone`
anahtarında dört işçinin yükü 539 ile 561 arasındadır; `donem` anahtarında 400 ile 600;
`bolge` anahtarında ise **374 ile 1826** arasında ve **iki işçi tamamen boştur**. Karıştırma
yalnız veriyi taşımaz, **yeni bir dengesizlik de üretir** — sürenin en yüklü işçiye bağlı olması
bu dengesizliği doğrudan faturaya yazar.

Tablonun yazılmayan sütunu doğruluktur. Üç kurulumun ürettiği bölge toplamları **birebir
aynıdır**; hangi anahtarla bölüşüldüğünden bağımsız olarak her satır hesaba tam bir kez girer.
Bu kursun üçüncü iddiasıdır: **sonuç her kurulumda aynıdır, değişen yalnız bedeldir.** Çıktıyı
denetleyen bir sınama üç kurulumu da geçer ve `bolge` bölüşümünün 5214 birimlik hareket bedeli
hakkında **hiçbir şey söylemez**. Yanlış bölüşüm bir hata değil bir fiyattır ve fiyat ancak
sayılırsa görünür.

#### Ağ Bedeli Bir Seçimdir

Yukarıdaki bütün sayıların içinde `AG_BEDELI = 3` duruyor. Bu sayı bir ölçüm değildir; ağdan bir
satır geçirmenin yerel bir satırı işlemeye göre kaç katına mal olduğunu tek birime indirmek için
verilmiş bir karşılıktır. Gerçek bir kümede bu oran ağın hızına, satırın boyuna ve serileştirmenin
maliyetine göre değişir. O yüzden sonucun ona ne kadar bağlı olduğu yazılmadan hiçbir şey
ölçülmüş sayılmaz. Aşağıdaki blok aynı üç karıştırmayı dört ayrı ağ bedeliyle tekrarlar.

```python
# Bu blok ilk bloktaki satirlar() , izno() , dagit() , karistir() , TUM , KOVA ,
# ISCI ve GOREV_SABIT tanimlarini surdurur. Degisen tek sey AG_BEDELI'dir.
def maliyet_ag(kova, tasinan, ag, isci=ISCI):
    """maliyet() ile ayni hesap , yalniz ag bedeli disaridan veriliyor."""
    n = [len(v) for v in kova.values()]
    return {"sure": max(n) + tasinan * ag // isci + GOREV_SABIT,
            "toplam_is": sum(n) + tasinan * ag}


HEDEF = [(h, karistir(KOVA, h)[1]) for h in ("abone", "donem", "bolge")]
print("ag bedelinden bagimsiz olanlar: tasinan satir " +
      " , ".join(f"{h} {t}" for h, t in HEDEF))
print("\nag bedeli" + "".join(f"{h + ' sure/toplam is':>26}" for h, _ in HEDEF)
      + "   bolge/abone sure")
for ag in (1, 3, 6, 12):
    m = [maliyet_ag(KOVA, t, ag) for _, t in HEDEF]
    print(f"{ag:>9}" + "".join(f"{str(x['sure']) + '/' + str(x['toplam_is']):>26}"
                               for x in m)
          + f"{round(m[2]['sure'] / m[0]['sure'], 2):>19}")
```

```
ag bedelinden bagimsiz olanlar: tasinan satir abone 0 , donem 1651 , bolge 1738

ag bedeli      abone sure/toplam is      donem sure/toplam is      bolge sure/toplam is   bolge/abone sure
        1                  611/2200                 1023/3851                 1045/3938               1.71
        3                  611/2200                 1849/7153                 1914/7414               3.13
        6                  611/2200                3087/12106                3218/12628               5.27
       12                  611/2200                5564/22012                5825/23056               9.53
```

Tablonun ilk satırı, yani başlıktan önceki satır, süpürmenin ön koşuludur: **taşınan satır ağ
bedelinden bağımsızdır.** 0, 1651 ve 1738 dört değerde de aynıdır, çünkü bir satırın işçi
değiştirip değiştirmediği anahtarın karmasına bağlıdır, ağın hızına değil. Ağ bedeli yalnızca o
sayının **kaç birim yazdığını** belirler. Ölçümün kendisi ile ölçümün fiyatlandırılması bu
tabloda ayrılmıştır.

İkinci duyarsızlık `abone` sütunundadır: **611 ve 2200 dört satırda da değişmez.** Hiç satır
taşımayan bir kurulum ağ bedelini hiç ödemez, dolayısıyla o seçime tamamen kayıtsızdır. Bu, bir
karar kuralı verir: ağın maliyeti belirsizse, **belirsizliğe dokunmayan plan** güvenli olandır.
Karıştırmasız kurulumun değeri hızlı olmasından çok, tahmininin **seçime bağlı olmamasıdır**.

Üçüncü sütun duyarlı olanı verir. `bolge` karıştırmasının süresi 1045'ten **5825**'e, toplam işi
3938'den **23056**'ya taşınır. Son sütun bunu tek sayıya indirir: karıştırmalı kurulum
karıştırmasız olana göre ağ bedeli 1'de **1,71** kat, 3'te **3,13** kat, 6'da **5,27** kat, 12'de
**9,53** kat pahalıdır. Ağ bedelini iki katına çıkarmak farkı iki katına çıkarmaz, çünkü sürenin
içinde bedelden bağımsız bir taban vardır (en yüklü işçinin 561 satırı ve 50 birimlik görev
sabiti); büyüyen yalnız taşıma payıdır.

Süpürmenin **değiştirmediği** son şey sıralamadır. Dört ağ bedelinin hepsinde `abone` en ucuz,
`donem` ortada, `bolge` en pahalıdır. Bunun nedeni sürenin taşınan satırda tekdüze artmasıdır:
`AG_BEDELI` pozitif olduğu sürece daha çok taşıyan plan daha pahalıdır ve sıralama değişmez.
Dolayısıyla "hangi anahtar daha az taşır" sorusu seçime **duyarsız**, "ne kadar pahalıya mal
olur" sorusu seçime **tümüyle duyarlıdır**. Bir sonraki derste bu ayrımın sonucu değiştirdiği bir
karar görülecek: orada iki plandan biri taşınan satır öder, öbürü ödemez ve hangisinin kazandığı
doğrudan `AG_BEDELI`'nin değerine bağlanır.

#### Özet

- Karıştırma satırları yeni bir anahtara göre yeniden bölüştürmektir; ölçüsü **taşınan
  satırdır**. `abone` düzeninden `abone`'ye geçmek 0 satır taşır, `donem`'e geçmek 1651,
  `bolge`'ye geçmek 1738 satır.
- Aynı 2200 satır, aynı dört işçi ve aynı sonuç için süre **611**'den **1914**'e, toplam iş
  **2200**'den **7414**'e çıkar. Toplam işin 5214 birimi hiçbir hesap yapmayan saf hareket
  bedelidir; hesap üç kurulumda da aynıdır.
- Karıştırmasız bir dağıtık iş mümkündür ve toplam işi tek işçilik tabanla birebir aynı kalır
  (2200); süre 2250'den 611'e yalnız paylaşmayla iner.
- Karıştırma ikinci bir bedel daha üretir: yeni işçi yükü `abone`'de 539–561, `bolge`'de
  374–1826'dır ve iki işçi boş kalır. Süre en yüklü işçiye bağlı olduğu için bu dengesizlik
  doğrudan faturaya girer.
- Ağ bedeli 1, 3, 6 ve 12 süpürüldüğünde taşınan satır ve karıştırmasız kurulumun 611/2200'ü hiç
  değişmez; karıştırmalı kurulum 1045/3938'den 5825/23056'ya taşınır ve fark 1,71 kattan 9,53
  kata çıkar. Sıralama dört değerde de aynıdır: büyüklük seçimin cinsindendir, sıralama değildir.

#### Sonraki Adım

Bu dersin bütün ölçümleri **tek bir veri kümesi** üzerinde yapıldı: 2200 satır bölüştürüldü,
yeniden bölüştürüldü ve taşınan satır sayıldı. Oysa dağıtık işlerin çoğu tek bir kümeyi değil,
**iki kümeyi** karşı karşıya getirir — büyük bir olgu tarafı ile küçük bir boyut tarafı. İki
tarafın da aynı anahtara göre bölüştürülmüş olması gerekir, yoksa eşleşecek satırlar farklı
işçilerde durur. Bunu sağlamanın iki ayrı yolu vardır ve bedelleri bambaşka cinstendir: küçük
tarafın **her işçiye kopyalanması** ya da **iki tarafın da karıştırılması**. Sonraki ders bu iki
stratejiyi boyut tarafı büyütülerek karşılaştırır ve hangisinin ne zaman kazandığını, kazananın
değiştiği **kesişme noktasını** sayarak arar.

### Ölçekte Birleştirme

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/olcekte-birlestirme

İki kümeyi karşı karşıya getiren bir işin iki ayrı stratejiyle ölçülmesi ve kazananın değiştiği noktanın sayılması: olgu tarafı bolge anahtarıyla bölünmüşken yayınlı birleştirme boyut tarafını her işçiye kopyalıyor ve 50 satırlık boyutta 1926, 400'de 2276, 1600'de 3476 süre yazıyor; bölümlü birleştirme ise boyut ne kadar küçük olursa olsun olgu tarafının 1738 satırını taşımak zorunda olduğu için 3179'un altına inemiyor. Yayınlının süresi boyut satırı başına bir birim, bölümlününki on altıda dokuz birim büyüdüğü için iki doğru kesişiyor: 2978 ve 2979'da süre eşitleniyor, 2980'de bölümlü öne geçiyor ve 4000'de fark 5876'ya karşı 5429 oluyor. Aynı kesişme noktası toplam iş ölçüldüğünde de 2980 çıkıyor. Karar ağ bedeline duyarlıdır: bedel 1 iken kesişme noktası 535'e iniyor, 6 ve 12'de bölümlü hiçbir boyutta öne geçemiyor. İki strateji her boyutta aynı satırları eşleştiriyor.

Bir önceki dersin bütün ölçümleri **tek bir veri kümesi** üzerinde yapıldı: 2200 satır bölüştü,
yeniden bölüştü ve taşınan satır sayıldı. Dağıtık işlerin çoğu ise tek bir kümeyi değil **iki
kümeyi** karşı karşıya getirir: büyük bir olgu tarafı ile ona bağlanan bir boyut tarafı.
Birleştirmenin çalışması için eşleşecek iki satırın **aynı işçide** bulunması gerekir, yoksa
karşılaşamazlar.

Bunu sağlamanın iki yolu vardır ve bedelleri farklı cinstendir. Küçük taraf **her işçiye
kopyalanabilir** — o zaman hiçbir satır taşınmaz ama küçük taraf işçi sayısı kadar çoğalır. Ya da
**iki taraf da** birleştirme anahtarına göre karıştırılır — o zaman kopya olmaz ama iki tarafın
da satırları ağdan geçer. Bu ders iki stratejiyi boyut tarafı büyütülerek karşılaştırır ve
kazananın değiştiği noktayı arar.

- **DB10.** Kaynak, tüketim satırları, boyut tarafı ve küme **kurgudur** ve ders içinde üretilir.
  Hiçbir dağıtık işleme çatısı, sorgu motoru ya da dosya sistemi çağrılmaz. Tohum 20260218.
- **DB11.** Olgu tarafı hattan çıkan **2200** tüketim satırıdır ve bir önceki dersin en dengesiz
  düzeninde, `bolge` anahtarıyla bölünmüş durur. Birleştirme anahtarı `abone`'dir. **Olgu ve
  boyut bağıntısı** ayrımı Veritabanları müfredatındaki İlişkisel Veritabanı Yönetimi kursunda
  kuruldu; **tekrarlanmaz**.
- **DB12.** Boyut tarafı bir **abone dizinidir**: `A000`'dan başlayan anahtarlar. Büyüklüğü
  süpürülür; 50 satırlık bir dizin abonelerin bir alt kümesini, 4000 satırlık bir dizin hattın
  gördüğünden çok daha geniş bir kayıt kütüğünü temsil eder.
- **DB13.** **Yayınlı birleştirme (broadcast join)**: boyut tarafı **her işçiye kopyalanır**.
  Taşınan satır 0, kopya `boyut x işçi`. Bir kopyanın bedeli **bir birimdir** ve ağ bedeliyle
  çarpılmaz; bu bir model seçimidir ve dersin sonucunu doğrudan belirler.
- **DB14.** **Bölümlü birleştirme (partitioned join)**: iki taraf da birleştirme anahtarına göre
  **karıştırılır**. Kopya 0, taşınan satırın her biri `AG_BEDELI` katı bedel yazar.
- **DB15.** Boyut tarafı işe **tek bir işçide, bölüştürülmemiş** girer. Bölümlü stratejide bu
  yüzden onun da işçi değiştiren satırları taşınana eklenir.
- **DB16.** Süre en yüklü işçinin işidir, **toplam iş** ayrı sayılır ve süre bu modelin **kendi
  birimidir**. `GOREV_SABIT` = 50 aynı işçi sayısındaki iki stratejiye de eşit eklenir,
  dolayısıyla karşılaştırmanın içine girmez.
- **DB17.** Taban çizgisi **tek işçidir**: süre **2250**, toplam iş **2200**.
- **DB18.** İki strateji her boyut büyüklüğünde **aynı satırları eşleştirir**; ölçülen tek şey
  bedeldir.

#### İki Strateji, İki Cins Bedel

Aşağıdaki blok olgu tarafını `bolge` ile böler, boyut tarafını altı ayrı büyüklükte üretir ve her
büyüklükte iki stratejinin bedelini yan yana yazar. Eşleşen satır sütunu iki strateji için de
aynıdır ve tek kez basılır.

```python
# KURGUDUR. Olcum aginin sayac okumalari , tuketim satirlari ve kume ders icinde
# uretilir; hicbir dagitik isleme catisi ya da dosya sistemi cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ISCI, AG_BEDELI, GOREV_SABIT = 4, 3, 50
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    """Hattan cikan tuketim satirlari (olgu tarafi). Saglam kaynakta ayikla ve
    tekille asamalari satir dusurmez; tuketim donem farkindan bulunur."""
    ham = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            ham.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                        "endeks": e, "birim": "m3"})
    tablo, cikti = {(k["abone"], k["donem"]): k for k in ham}, []
    for k in ham:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is not None:
            cikti.append(dict(k, m3=k["endeks"] - onceki["endeks"]))
    return cikti


def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


def dagit(kayitlar, anahtar, isci=ISCI):
    """Satirlari anahtara gore isciye atar. Ayni anahtar hep ayni isciye gider."""
    kova = {i: [] for i in range(isci)}
    for k in kayitlar:
        kova[izno(k[anahtar]) % isci].append(k)
    return kova


def karistir(kova, yeni_anahtar, isci=ISCI):
    """Yeniden bolustur. Isci degistiren satir agdan gecer."""
    yeni, tasinan = {i: [] for i in range(isci)}, 0
    for eski, kayitlar in kova.items():
        for k in kayitlar:
            hedef = izno(k[yeni_anahtar]) % isci
            if hedef != eski:
                tasinan += 1
            yeni[hedef].append(k)
    return yeni, tasinan


def yayinli_birlestirme(kova, boyut, isci=ISCI):
    """Boyut her isciye kopyalanir. Karistirma yok , kopya var."""
    kopya = len(boyut) * isci
    n = [len(v) for v in kova.values()]
    return {"tasinan": 0, "kopya": kopya,
            "sure": max(n) + kopya // isci + GOREV_SABIT,
            "toplam_is": sum(n) + kopya}


def bolumlu_birlestirme(kova, boyut, anahtar="abone", isci=ISCI):
    """Iki taraf da anahtara gore karistirilir."""
    _, tasinan = karistir(kova, anahtar, isci)
    boyut_tasinan = sum(1 for a in boyut if izno(a) % isci != 0)
    n = [len(v) for v in kova.values()]
    t = tasinan + boyut_tasinan
    return {"tasinan": t, "kopya": 0,
            "sure": max(n) + t * AG_BEDELI // isci + GOREV_SABIT,
            "toplam_is": sum(n) + t * AG_BEDELI}


def boyut_uret(n):
    """Abone dizini: n anahtar. Tek bir iscide , bolusturulmemis duruyor."""
    return [f"A{i:03d}" for i in range(n)]


TUM = satirlar()
KOVA = dagit(TUM, "bolge")
print(f"olgu tarafi {len(TUM)} satir , bolge anahtariyla bolunmus ,"
      f" isci yuku {[len(v) for v in KOVA.values()]}")
print(f"tek iscilik taban: sure {len(TUM) + GOREV_SABIT} , toplam is {len(TUM)}")
print(f"birlestirme anahtari abone , isci {ISCI} , ag bedeli {AG_BEDELI}")
print("\nboyut  eslesen  kopya  y.sure  y.toplam   tasinan  b.sure  b.toplam   kazanan")
for n in (50, 200, 400, 800, 1600, 4000):
    b = boyut_uret(n)
    y, p = yayinli_birlestirme(KOVA, b), bolumlu_birlestirme(KOVA, b)
    es = sum(1 for k in TUM if k["abone"] in set(b))
    print(f"{n:>5} {es:>8} {y['kopya']:>6} {y['sure']:>7} {y['toplam_is']:>9}"
          f" {p['tasinan']:>9} {p['sure']:>7} {p['toplam_is']:>9}"
          f"   {'yayinli' if y['sure'] < p['sure'] else 'bolumlu'}")
```

```
olgu tarafi 2200 satir , bolge anahtariyla bolunmus , isci yuku [374, 0, 0, 1826]
tek iscilik taban: sure 2250 , toplam is 2200
birlestirme anahtari abone , isci 4 , ag bedeli 3

boyut  eslesen  kopya  y.sure  y.toplam   tasinan  b.sure  b.toplam   kazanan
   50      539    200    1926      2400      1775    3207      7525   yayinli
  200     2189    800    2076      3000      1888    3292      7864   yayinli
  400     2200   1600    2276      3800      2038    3404      8314   yayinli
  800     2200   3200    2676      5400      2338    3629      9214   yayinli
 1600     2200   6400    3476      8600      2938    4079     11014   yayinli
 4000     2200  16000    5876     18200      4738    5429     16414   bolumlu
```

Eşleşen satır sütunu tablonun ön koşuludur. Aynı boyut büyüklüğünde iki strateji **aynı satırları
eşleştirir**: 50 satırlık dizinde 539, 200 satırlıkta 2189, 400'den sonra 2200. Sütunun boyutla
büyümesinin nedeni dizinin gitgide daha çok aboneyi kapsamasıdır; 400 satırdan sonra bütün
aboneler kapsandığı için sabitlenir. Ölçülen şey doğruluk değildir, çünkü doğruluk iki stratejide
de aynıdır.

İlk satır bölümlü stratejinin en önemli özelliğini verir: **bir tabanı vardır.** Boyut yalnız 50
satır olsa bile bölümlü birleştirme olgu tarafının **1738 satırını** taşımak zorundadır, çünkü
olgu tarafı `bolge` ile bölünmüşken birleştirme `abone` üzerinden yapılır. Bu yüzden bölümlü
sürenin bir tabanı vardır: boyut sıfıra inse bile **3179**, tablodaki en küçük boyut olan 50
satırda **3207**. Yayınlı stratejinin ise tabanı yoktur; boyut küçüldükçe bedeli sıfıra yaklaşır
ve 50 satırda süre yalnız **1926**'dır. Küçük boyutta
yayınlı birleştirmenin kazanması bir eğilim değil, yapısal bir sonuçtur.

İki sütunun büyüme hızı kararı belirler. Yayınlı stratejide boyutun her satırı işçi sayısı kadar
kopyalanır, kopyalar işçilere bölündüğü için süreye **satır başına bir birim** ekler: 1876 artı
boyut satırı. Bölümlü stratejide boyutun dörtte üçü işçi değiştirir, her taşınan satır ağ
bedeliyle çarpılır ve işçilere bölünür — süreye eklenen **satır başına on altıda dokuz birimdir**.
Yayınlı daha dik büyür ama sıfırdan başlar; bölümlü daha yavaş büyür ama 1303 birimlik bir
yükseklikten. İki doğrunun kesişmesi kaçınılmazdır ve kesiştiği yer bu dersin aradığı sayıdır.

Toplam iş sütunu aynı hikâyeyi başka bir birimle anlatır. 1600 satırlık boyutta yayınlı 8600,
bölümlü 11014 birim iş yapar; 4000'de sıralama ters döner (18200'e karşı 16414). Kursun kuralı
süre ile toplam işin ayrı yönlere gidebileceğini söyler; **bu karşılaştırmada gitmezler**, çünkü
her iki stratejinin de süresi toplam işinin işçi sayısına bölünmüş hâlidir ve bölen ikisinde de
aynıdır. Kuralın buradaki karşılığı şudur: ikisinin uyuştuğu **da gösterilmelidir**, yoksa
hangisinin ölçüldüğü bilinmez.

#### Dönüş Noktası

Altı büyüklük dönüşün 1600 ile 4000 arasında olduğunu söyler, yerini söylemez. Aşağıdaki blok
boyut büyüklüğünü tek tek tarar. Tarama hızlı olsun diye boyut tarafının taşınan satırı bir önek
toplamıyla tutulur; ilk satır bu kısayolun ilk bloktaki işlevlerle aynı sonucu verdiğini
doğrular.

```python
# Bu blok ilk bloktaki izno() , dagit() , karistir() , yayinli_birlestirme() ,
# bolumlu_birlestirme() , boyut_uret() , TUM ve KOVA tanimlarini surdurur.
ONEK = [0]                       # ONEK[n]: ilk n boyut satirinin kaci isci degistirir
for a in boyut_uret(4000):
    ONEK.append(ONEK[-1] + (1 if izno(a) % ISCI else 0))
_, OLGU_TASINAN = karistir(KOVA, "abone")
YUK = [len(v) for v in KOVA.values()]


def olcum(n, ag=AG_BEDELI):
    """Ilk bloktaki iki islevle ayni hesap; tek fark ag bedelinin disaridan
    verilmesi ve boyut tarafinin onek toplamiyla hizlandirilmasidir.
    Doner: yayinli sure , bolumlu sure , yayinli toplam is , bolumlu toplam is."""
    t = OLGU_TASINAN + ONEK[n]
    return (max(YUK) + n + GOREV_SABIT, max(YUK) + t * ag // ISCI + GOREV_SABIT,
            sum(YUK) + n * ISCI, sum(YUK) + t * ag)


d = boyut_uret(4000)
print(f"dogrulama , boyut 4000: islevlerle {yayinli_birlestirme(KOVA, d)['sure']}"
      f"/{bolumlu_birlestirme(KOVA, d)['sure']} , onek toplamiyla"
      f" {olcum(4000)[0]}/{olcum(4000)[1]}")
print("\nag bedeli 3'te donus noktasinin iki yani")
for n in (2978, 2979, 2980, 2981):
    y, b, _, _ = olcum(n)
    print(f"boyut {n}   yayinli {y}   bolumlu {b}   "
          f"{'yayinli' if y < b else 'bolumlu' if b < y else 'esit'}")
ilk_is = next(n for n in range(1, 4001) if olcum(n)[3] < olcum(n)[2])
print(f"\ntoplam is olculdugunde bolumlunun one gectigi ilk boyut: {ilk_is}")
print("\nag bedeli   sure olculdugunde bolumlunun one gectigi ilk boyut (4000'e kadar)")
for ag in (1, 3, 6, 12):
    ilk = next((n for n in range(1, 4001) if olcum(n, ag)[1] < olcum(n, ag)[0]), None)
    print(f"{ag:>9}   {ilk if ilk else 'hic'}")
```

```
dogrulama , boyut 4000: islevlerle 5876/5429 , onek toplamiyla 5876/5429

ag bedeli 3'te donus noktasinin iki yani
boyut 2978   yayinli 4854   bolumlu 4854   esit
boyut 2979   yayinli 4855   bolumlu 4855   esit
boyut 2980   yayinli 4856   bolumlu 4855   bolumlu
boyut 2981   yayinli 4857   bolumlu 4856   bolumlu

toplam is olculdugunde bolumlunun one gectigi ilk boyut: 2980

ag bedeli   sure olculdugunde bolumlunun one gectigi ilk boyut (4000'e kadar)
        1   535
        3   2980
        6   hic
       12   hic
```

Kesişme noktası **2980**'dir. 2978 ve 2979'da iki strateji **eşittir**, 2980'de bölümlü bir birim
öne geçer. Kesişimin keskin olmaması modelin tam sayı bölmesinden gelir: yayınlı süre satır
başına tam bir birim, bölümlü süre on altıda dokuz birim büyüdüğü için iki doğru birkaç satır
boyunca aynı tam sayıda buluşur. Kararın pratik okuması "boyut üç bine yaklaşınca strateji değişir"
değil, **yaklaşık üç binde iki stratejinin farkı ölçülemez hâle gelir**; farkın anlamlı olduğu
tek yer uçlardır. 50 satırda yayınlı **1281** birim öndedir, 4000 satırda bölümlü **447** birim.

Toplam iş satırı dönüşün aynı yerde olduğunu doğrular: **2980**. Bu tesadüf değildir. Yayınlı
stratejinin toplam işi boyut başına dört birim, süresi bir birim büyür; bölümlü stratejinin
toplam işi boyut başına 2,25 birim, süresi bunun dörtte biri kadar büyür. İki ölçünün
oranı ikisinde de işçi sayısıdır, dolayısıyla sıralamaları aynı noktada döner. Bu, kursun
kuralının bir istisnası değil, **kuralın neden yazılması gerektiğinin** kanıtıdır: uyuştukları
ancak ikisi de ölçüldüğünde bilinir.

Son tablo kararın hangi seçime asıldığını gösterir ve bu dersin en sert bulgusudur. Ağ bedeli 1
olduğunda kesişme noktası **535**'e iner; 2980 satırlık bir dizin için verilen karar, aynı veriyle,
aynı işçi sayısıyla, yalnız bir sabit değiştiği için **tersine döner**. Ağ bedeli 6 ve 12
olduğunda ise bölümlü birleştirme taranan **hiçbir** boyut büyüklüğünde öne geçemez. Bunun nedeni
okunabilir: bölümlü süre boyut satırı başına ağ bedelinin on altıda üçü kadar büyür, yayınlı süre
bir birim; ağ bedeli beşte kaldığı sürece bölümlü daha yavaş büyür, altıya çıkınca daha hızlı
büyür ve bir daha asla yetişemez. Yani "büyük boyutta bölümlü kazanır" cümlesi ağ bedeli 3 için
doğru, 6 için **yanlıştır**.

Buradan çıkan kural şudur: bir birleştirme stratejisi veriye bakılarak seçilemez. İki taraf da
aynı satırları eşleştirir, aynı sonucu verir ve doğruluk denetimi ikisini de geçer. Seçim boyut
tarafının büyüklüğüne, olgu tarafının hangi anahtarla bölünmüş olduğuna ve ağ bedeline bakılarak
yapılır; **üç girdiden biri yazılmadan verilen karar ölçülmemiş sayılır.**

#### Özet

- Yayınlı birleştirme boyut tarafını her işçiye kopyalar (taşınan 0), bölümlü birleştirme iki
  tarafı da karıştırır (kopya 0). Bedeller ayrı cinstendir: biri kopya, öbürü taşınan satır.
- Bölümlü stratejinin bir tabanı vardır: olgu tarafı `bolge` ile bölünmüşken birleştirme `abone`
  üzerinden yapıldığı için 1738 satır her koşulda taşınır ve süre **3179**'un altına inmez; 50
  satırlık boyutta **3207**'dir. Yayınlının tabanı yoktur; 50 satırlık boyutta süre **1926**'dır.
- Yayınlı süre boyut satırı başına bir birim, bölümlü süre on altıda dokuz birim büyür. İki doğru
  kesişir: 2978 ve 2979'da süreler eşittir, **2980**'de bölümlü öne geçer. 4000'de fark 5876'ya
  karşı **5429**'dur.
- Toplam iş ölçüldüğünde kesişme noktası yine **2980** çıkar, çünkü iki ölçünün oranı her iki
  stratejide de işçi sayısıdır. Uyuştukları ancak ikisi de ölçüldüğünde bilinir.
- Karar ağ bedeline asılıdır: bedel 1 iken kesişme noktası **535**, 3 iken **2980**, 6 ve 12 iken
  bölümlü hiçbir boyutta öne geçemez. İki strateji her boyutta aynı satırları eşleştirdiği için
  doğruluk denetimi bu farkın hiçbirini göstermez.

#### Sonraki Adım

Bu dersin bütün tablolarında bir sayı sessizce duruyordu: olgu tarafının işçi yükü
`[374, 0, 0, 1826]` olarak basılmıştı. Dört işçinin **ikisi hiç satır almamıştı** ve bütün süre
hesapları en yüklü işçinin 1826
satırı üzerinden yapılmıştı. Yayınlı stratejinin 1876 birimlik tabanı da, bölümlü stratejinin
3207'lik tabanı da bu tek sayıdan besleniyordu. İki ders boyunca yavaşlığın kaynağı **hareket**
olarak arandı; oysa bu düzende işçilerin yarısı hiç çalışmıyordu ve bu, hareketten bağımsız bir
kayıptır. Sonraki ders bölüşümün kendisine bakar: bir anahtarın işçiler arasında ne kadar
dengesiz dağıldığını **çarpıklık** olarak ölçer, aynı işi doğru ve yanlış anahtarla bölüştürür ve
dengesiz bir bölüşümde **işçi eklemenin** ne işe yaradığını sayar.

### Veri Çarpıklığı

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/veri-carpikligi

Dengesiz bölüşümün ölçülmesi ve işçi eklemenin neden bir çözüm olmadığının sayılması: aynı 2200 satır abone anahtarıyla bölüştürüldüğünde dört işçinin yükü 539 ile 561 arasında kalıyor ve çarpıklık 1,02 çıkıyor, bolge anahtarıyla bölüştürüldüğünde yük 374, 0, 0 ve 1826 oluyor, çarpıklık 3,32'ye çıkıyor ve iki işçi hiç satır almıyor. Yanlış anahtarla işçi sayısı artırıldığında çarpıklık 1,66'dan 3,32'ye, 6,64'e ve 6,88'e çıkıyor, boş işçi sayısı 0'dan 13'e yükseliyor; tek işçide 2250 olan süre iki işçiyle 3691'e çıkıyor ve tabanın altına ancak on altı işçide iniyor, bu arada toplam iş 2200'den 8635'e taşınıyor. Doğru anahtarla ise hiçbir işçi sayısında satır taşınmıyor, toplam iş 2200'de sabit kalıyor ve süre 215'e kadar iniyor. Üç bölüşüm de bölge toplamlarının birebir aynısını üretiyor.

Bir önceki dersin bütün tablolarında bir sayı sessizce duruyordu: olgu tarafının işçi yükü
`[374, 0, 0, 1826]`. Dört işçinin **ikisi hiç satır almamıştı** ve bütün süre hesapları en yüklü
işçinin 1826 satırı üzerinden yapılmıştı. İki ders boyunca yavaşlığın kaynağı **hareket** olarak
arandı; oysa bu düzende işçilerin yarısı hiç çalışmıyordu ve bu, hareketten bağımsız bir
kayıptır.

Bu ders bölüşümün kendisine bakar. Ölçülen şey satırların işçilere ne kadar dengesiz dağıldığı,
bu dengesizliğin nereden geldiği ve dengesiz bir bölüşümde **işçi eklemenin** ne işe yaradığıdır.
Sonuncusu bu kursun ikinci iddiasının en açık örneğidir.

- **DB19.** Kaynak, tüketim satırları ve küme **kurgudur** ve ders içinde üretilir. Hiçbir
  dağıtık işleme çatısı, kaynak yöneticisi ya da dosya sistemi çağrılmaz. Tohum 20260218.
- **DB20.** **Veri çarpıklığı (data skew)** en yüklü işçinin işçi başına düşen ortalamaya
  oranıdır. 1,00 kusursuz dengedir; büyüdükçe bir işçi ötekilerden fazla iş yapıyor demektir.
- **DB21.** Süre **en yüklü işçinin işidir**, dolayısıyla çarpıklık doğrudan süreye yazılır.
  Süre bu modelin **kendi birimidir**. **Toplam iş** ayrı sayılır.
- **DB22.** Bölüşüm anahtarın karmasıyla yapılır ve **aynı anahtar değeri hep aynı işçiye gider**.
  Bunun iki sonucu vardır: bir anahtar değerinin satırları **bölünemez** ve iki ayrı değer
  **aynı işçiye çakışabilir**.
- **DB23.** İndirgeme bütün tablolarda `abone` anahtarı üzerinden yapılır; değişen tek şey
  satırların **hangi anahtarla bölüştürüldüğüdür**. `abone` bu yüzden doğru, `bolge` yanlış
  bölüşüm anahtarıdır.
- **DB24.** `AG_BEDELI` = 3 ve `GOREV_SABIT` = 50 birer **seçimdir**. Bu dersin çarpıklık sayıları
  ikisinden de bağımsızdır; süre ve toplam iş sayıları değildir.
- **DB25.** Taban çizgisi **tek işçidir**: karıştırma yok, çarpıklık 1,00, süre **2250**, toplam
  iş **2200**.
- **DB26.** Bütün bölüşümler **birebir aynı sonucu** üretir; ölçülen tek şey bedeldir.
- **DB27.** Kümenin çözünürlüğü: satır 2200, işçi en çok 16. Dört işçide çarpıklığın adımı
  0,0018'dir; bu ölçüde okunan farklar bu adımın çok üstündedir.

#### Çarpıklık Bölüşümün Ölçüsüdür

Aşağıdaki blok aynı 2200 satırı üç ayrı anahtarla dört işçiye böler, her bölüşümün çarpıklığını
hesaplar ve üçünün ürettiği bölge toplamlarını karşılaştırır.

```python
# KURGUDUR. Olcum aginin sayac okumalari , tuketim satirlari ve kume ders icinde
# uretilir; hicbir dagitik isleme catisi ya da kaynak yoneticisi cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ISCI, AG_BEDELI, GOREV_SABIT = 4, 3, 50
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    """Hattan cikan tuketim satirlari. Saglam kaynakta ayikla ve tekille asamalari
    satir dusurmez; tuketim onceki donemin endeksinden cikarilarak bulunur."""
    ham = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            ham.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                        "endeks": e, "birim": "m3"})
    tablo, cikti = {(k["abone"], k["donem"]): k for k in ham}, []
    for k in ham:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is not None:
            cikti.append(dict(k, m3=k["endeks"] - onceki["endeks"]))
    return cikti


def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


def dagit(kayitlar, anahtar, isci=ISCI):
    """Satirlari anahtara gore isciye atar. Ayni anahtar hep ayni isciye gider."""
    kova = {i: [] for i in range(isci)}
    for k in kayitlar:
        kova[izno(k[anahtar]) % isci].append(k)
    return kova


def yuk(kova):
    """Carpiklik: en yuklu iscinin ortalamaya orani."""
    n = [len(v) for v in kova.values()]
    return {"toplam": sum(n), "en_yuklu": max(n), "en_az": min(n),
            "carpiklik": round(max(n) / (sum(n) / len(n)), 4)}


def karistir(kova, yeni_anahtar, isci=ISCI):
    """Yeniden bolustur. Isci degistiren satir agdan gecer."""
    yeni, tasinan = {i: [] for i in range(isci)}, 0
    for eski, kayitlar in kova.items():
        for k in kayitlar:
            hedef = izno(k[yeni_anahtar]) % isci
            if hedef != eski:
                tasinan += 1
            yeni[hedef].append(k)
    return yeni, tasinan


def maliyet(kova, tasinan=0, isci=ISCI):
    """Sure = en yuklu iscinin isi (paralel). Toplam is ayrica sayilir."""
    n = [len(v) for v in kova.values()]
    return {"sure": max(n) + tasinan * AG_BEDELI // isci + GOREV_SABIT,
            "toplam_is": sum(n) + tasinan * AG_BEDELI,
            "tasinan": tasinan}


def bolge_toplam(kova):
    """Her isci once kendi payinin bolge toplamini cikarir , sonra birlestirilir."""
    son = {}
    for kayitlar in kova.values():
        ara = {}
        for k in kayitlar:
            ara[k["bolge"]] = ara.get(k["bolge"], 0) + k["m3"]
        for b, v in ara.items():
            son[b] = son.get(b, 0) + v
    return tuple(sorted(son.items()))


TUM = satirlar()
print(f"hattan cikan satir {len(TUM)} , isci {ISCI} , ag bedeli {AG_BEDELI}")
print("bolge basina satir: "
      + " , ".join(f"{b} {sum(1 for k in TUM if k['bolge'] == b)}" for b in BOLGE))
print("bolge hangi isciye dusuyor: " + " , ".join(f"{b} {izno(b) % ISCI}" for b in BOLGE))
print("\nanahtar  farkli deger   isci yuku                en yuklu  en az  carpiklik")
for a in ("abone", "bolge", "donem"):
    k, y = dagit(TUM, a), yuk(dagit(TUM, a))
    print(f"{a:<8} {len(set(x[a] for x in TUM)):>12}   {str([len(v) for v in k.values()]):<24}"
          f"{y['en_yuklu']:>6} {y['en_az']:>6} {y['carpiklik']:>10}")
SONUC = {a: bolge_toplam(dagit(TUM, a)) for a in ("abone", "bolge", "donem")}
print(f"\nuc bolusumun urettigi sonuc tek mi: {len(set(SONUC.values())) == 1}")
print("bolge toplami: " + " , ".join(f"{b} {v}" for b, v in SONUC["abone"]))
```

```
hattan cikan satir 2200 , isci 4 , ag bedeli 3
bolge basina satir: dogu 374 , bati 341 , kuzey 506 , guney 440 , merkez 539
bolge hangi isciye dusuyor: dogu 0 , bati 3 , kuzey 3 , guney 3 , merkez 3

anahtar  farkli deger   isci yuku                en yuklu  en az  carpiklik
abone             200   [539, 550, 561, 550]       561    539       1.02
bolge               5   [374, 0, 0, 1826]         1826      0       3.32
donem              11   [600, 600, 400, 600]       600    400     1.0909

uc bolusumun urettigi sonuc tek mi: True
bolge toplami: bati 9164 , dogu 9695 , guney 11721 , kuzey 13338 , merkez 14536
```

Son iki satır bütün dersin ön koşuludur. Üç bölüşüm de **birebir aynı** bölge toplamlarını
üretir; hangi anahtarla bölüşüldüğünden bağımsız olarak her satır hesaba tam bir kez girer. Bu
kursun üçüncü iddiasıdır ve buradaki biçimi en sert olanıdır: **sonucu denetleyen hiçbir sınama
`bolge` bölüşümündeki dengesizliği göremez.** Çarpıklık bir doğruluk sorunu değil, yalnızca
sayıldığında görünen bir bedeldir.

İlk tablo çarpıklığın nereden geldiğini gösterir. `abone` anahtarında 200 farklı değer vardır,
dört işçiye 539 ile 561 arasında dağılırlar ve çarpıklık **1,02**'dir. `donem` anahtarında 11
farklı değer vardır — hattın ilk dönemi tüketim hesaplanamadığı için düşmüştür — ve 11 değer dört
işçiye 3, 3, 2, 3 olarak bölündüğü için yük 600, 600, 400, 600 ve çarpıklık **1,0909** olur.
`bolge` anahtarında ise yalnız **5** farklı değer vardır ve çarpıklık **3,32**'ye çıkar.

Üçüncü satırın nedeni ikinci satırda yazılıdır. Beş bölgenin **dördü de üçüncü işçiye düşer**:
341 artı 506 artı 440 artı 539 tam olarak 1826 eder. Geriye kalan tek bölge `dogu`, tek başına
sıfırıncı işçidedir ve iki işçi hiç satır almaz. Burada iki ayrı mekanizma üst üste biner.
Birincisi **anahtarın azlığıdır**: beş farklı değer en çok beş işçiyi doldurabilir. İkincisi
**karma çakışmasıdır**: beş değerden dördü aynı kovaya düşmüştür.

Bunun ayırt edilmesi gereken bir sonucu var. Bölgeler kendi içlerinde çok dengesiz değildir; en
küçüğü 341, en büyüğü 539 satır taşır ve aralarındaki oran ikinin altındadır. Yani veri, kendi
başına, çarpık değildir. Çarpıklığı üreten şey **anahtar ile işçi sayısının birlikte
davranışıdır**. "Veri çarpıktı" cümlesi bu yüzden eksiktir; çarpıklık verinin bir özelliği değil,
**bölüşümün** bir özelliğidir ve bölüşüm bir karardır.

#### İşçi Eklemek Bir Çözüm Değildir

Dengesiz bir bölüşümde akla gelen ilk düzeltme daha çok işçidir. Aşağıdaki blok aynı süpürmeyi
iki anahtarla yapar: yanlış olanla (`bolge`) ve doğru olanla (`abone`). İndirgeme her ikisinde de
`abone` üzerindendir.

```python
# Bu blok ilk bloktaki satirlar() , izno() , dagit() , yuk() , karistir() ,
# maliyet() ve TUM tanimlarini surdurur. Indirgeme her iki tabloda da abone
# anahtari uzerinden yapiliyor; degisen tek sey bolusum anahtari.
def supur(bolusum):
    print(f"bolusum anahtari {bolusum} , indirgeme abone")
    print("isci  carpiklik  en yuklu  bos isci  tasinan   sure  toplam is")
    for isci in (1, 2, 4, 8, 16):
        k = dagit(TUM, bolusum, isci)
        _, t = karistir(k, "abone", isci)
        m, y = maliyet(k, t, isci), yuk(k)
        print(f"{isci:>4} {y['carpiklik']:>10} {y['en_yuklu']:>9}"
              f" {sum(1 for v in k.values() if not v):>9}"
              f" {t:>8} {m['sure']:>6} {m['toplam_is']:>10}")


supur("bolge")
print()
supur("abone")
```

```
bolusum anahtari bolge , indirgeme abone
isci  carpiklik  en yuklu  bos isci  tasinan   sure  toplam is
   1        1.0      2200         0        0   2250       2200
   2       1.66      1826         0     1210   3691       5830
   4       3.32      1826         2     1738   3179       7414
   8       6.64      1826         6     1958   2610       8074
  16       6.88       946        13     2145   1398       8635

bolusum anahtari abone , indirgeme abone
isci  carpiklik  en yuklu  bos isci  tasinan   sure  toplam is
   1        1.0      2200         0        0   2250       2200
   2        1.0      1100         0        0   1150       2200
   4       1.02       561         0        0    611       2200
   8       1.04       286         0        0    336       2200
  16        1.2       165         0        0    215       2200
```

İlk tablonun ikinci satırı bu kursun **ikinci iddiasıdır**: tek işçide süre **2250** iken, ikinci
işçi eklendiğinde **3691**'e **çıkar**. İşçi eklemek işi kötüleştirmiştir. Nedeni okunabilir:
tek işçide karıştırma yoktur, çünkü bütün satırlar zaten aynı yerdedir. İkinci işçi ortaya
çıktığı anda indirgeme anahtarı bölüşüm anahtarından ayrışır, 1210 satır ağdan geçer ve toplam iş
2200'den **5830**'a sıçrar. Paralellik bedava değildir ve bu düzende **ilk adımı negatiftir**.

Süre ancak dörtten sonra inmeye başlar, ama tek işçilik tabanın altına ancak **on altı işçide**
iner (1398). Dört işçide 3179, sekiz işçide 2610 — her ikisi de 2250'nin üstündedir. Yani
`bolge` anahtarıyla bölüştürülmüş bir iş, sekiz işçilik bir kümede tek bir işçiden **hâlâ
yavaştır**. Bu tablo yazılmadan "kümeyi büyüttük, hızlandı" cümlesinin doğru olup olmadığı
bilinemez.

En yüklü işçi sütunu bunun mekanizmasını verir ve tablonun en çarpıcı sayısı odur: iki, dört ve
sekiz işçide en yüklü işçinin yükü **1826'da sabittir**. İşçi sayısı dört katına çıkarken o işçi
tek bir satır bile devretmez, çünkü ona düşen dört bölge her üç bölüşümde de aynı kovada kalır.
Süredeki iniş (3691, 3179, 2610) yükün azalmasından değil, yalnızca taşıma payının daha çok
işçiye bölünmesinden gelir. Yük ilk kez on altı işçide kırılır ve **946**'ya iner.

Çarpıklık sütunu işçi eklendikçe **kötüleşir**: 1,66, 3,32, 6,64, 6,88. Boş işçi sayısı 0'dan
2'ye, 6'ya ve **13**'e çıkar. On altı işçilik kümede yalnız üç işçi satır almaktadır. Bunun
sınırı anahtarın kendisinde yazılıdır: `bolge` beş farklı değer taşır, dolayısıyla kaç işçi
eklenirse eklensin en çok **beş** işçi satır alabilir. Bir anahtar değerinin satırları
bölünemediği için en yüklü işçi de en büyük bölgenin altına, yani **539** satırın altına inemez.
Süre bu yüzden kaç işçi eklenirse eklensin görev sabitiyle birlikte **589**'un altına inemez.
İkinci tablonun on altıncı satırı bunu bir karşılaştırmaya çevirir: `abone` anahtarıyla süre
zaten **215**'tir ve o eşiğin çok altındadır. **Bölüşüm anahtarı yanlışsa işçi sayısı bir çözüm
değildir**, çünkü yanlış anahtar kümeye bir tavan koyar.

Çarpıklığın artarken sürenin azalması ilk bakışta çelişkili görünür ve ayrı ayrı okunması
gerekir. Çarpıklık bir **orandır**: işçi sayısı arttıkça ortalama küçülür, en yüklü işçinin yükü
ise aynı hızda küçülmez, dolayısıyla oran büyür. Süre bir **mutlak sayıdır** ve taşıma payı
işçilere dağıldığı için düşer. İkisi aynı şeyi söylemez: süre **işin ne zaman
bittiğini**, çarpıklık **kaç işçinin boşa ayrıldığını** ölçer. On altı işçide 1398 süre, on üç
işçinin boş beklemesi ve **8635** birimlik toplam iş karşılığında alınmıştır.

İki tablo yan yana konduğunda kursun kuralı görünür. `bolge` tablosunda süre 3691'den 1398'e
inerken toplam iş 5830'dan **8635**'e çıkar; iki ölçü ters yönlere gider ve "dört kat hızlandı"
gibi bir cümle hangisinin ölçüldüğü yazılmadan **eksiktir**. `abone` tablosunda ise toplam iş
beş satırda da **2200**'de durur, çünkü taşınan satır her işçi sayısında sıfırdır. Doğru
anahtarın kazandırdığı şey yalnız süre değildir: kümeye eklenen her işçinin **karşılığında bir
şey ürettiği** tek düzen budur.

#### Özet

- Veri çarpıklığı en yüklü işçinin ortalamaya oranıdır: `abone` bölüşümünde **1,02**, `donem`
  bölüşümünde **1,0909**, `bolge` bölüşümünde **3,32**. Son düzende dört işçinin **ikisi boştur**
  ve yük `[374, 0, 0, 1826]`'dır.
- Çarpıklık verinin değil bölüşümün özelliğidir. Bölgeler 341 ile 539 satır arasındadır, yani
  veri kendi başına dengelidir; çarpıklığı üreten şey anahtarın yalnız **5** farklı değer taşıması
  ve bu beş değerden **dördünün aynı işçiye çakışmasıdır** (341 + 506 + 440 + 539 = 1826).
- İşçi eklemek işi kötüleştirebilir: tek işçide süre **2250**, iki işçide **3691**'dir, çünkü
  ikinci işçiyle birlikte 1210 satır ağdan geçer ve toplam iş 2200'den 5830'a çıkar. Süre tek
  işçilik tabanın altına ancak on altı işçide iner.
- Yanlış anahtarla işçi eklendikçe çarpıklık 1,66'dan **6,88**'e, boş işçi 0'dan **13**'e çıkar.
  Beş farklı değerli bir anahtar en çok beş işçi doldurabilir ve en büyük bölge 539 satır olduğu
  için süre **589**'un altına hiçbir işçi sayısında inemez; `abone` anahtarıyla süre on altı
  işçide zaten **215**'tir.
- Üç bölüşüm de bölge toplamlarının birebir aynısını üretir. Doğruluğu denetleyen hiçbir sınama
  bu farkı göstermez; `bolge` tablosunda süre 3691'den 1398'e inerken toplam iş 5830'dan
  **8635**'e çıkar, `abone` tablosunda toplam iş beş satırda da 2200'de durur.

#### Sonraki Adım

Üç derste yavaşlığın kaynağı iki başlıkta toplandı: satırların **hareketi** ve işçilerin
**dengesizliği**. Her iki ölçüde de tek bir hesabın tek bir kez yapıldığı varsayıldı; aynı ara
sonuca iki ayrı sorunun ihtiyaç duyması ya da bir işçinin iş bitmeden düşmesi hiç konuşulmadı.
Oysa dağıtık bir işte aynı hesabın iki kez yapılması sık rastlanan bir kalemdir ve bir işçinin
düşmesi, o ana kadar yapılmış işin ne kadarının geri isteneceği sorusunu doğurur. Sonraki ders
ara sonucun saklanmasını ölçer: aynı veriyi iki kez hesaplamakla bir kez hesaplayıp tutmak
arasındaki farkı sayar ve tutmanın kendi bedelini yazar.

### Önbellekleme ve Kalıcılık

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/onbellekleme-ve-kalicilik

Aynı ara sonucun birden çok sorgu tarafından okunduğunda iki kez hesaplanmasının ve önbelleğin tutulmadığı durumda geri gelen bedelin sayılması: 2200 satırlık ara sonucu paylaşan iki sorgunun toplamı önbelleksiz 5500, önbellekli 3300'dür, ama tek sorguda ikisi de 2750'dir ve önbellek hiçbir şey kazandırmaz. Ara sonuç dört işçinin belleğinde 539, 550, 561 ve 550 satır olarak durur; işçiler düştükçe toplam 3300'den 3850, 4411, 4961 ve 5500'e çıkar ve hiç tutulmayan önbellek tam olarak önbelleksiz kurulumun bedelini öder, oysa plan 3300'e göre yapılmıştır. Tek bir işçinin düşmesi toplamı 3839 ile 3861 arasına taşır. Aynı önbellek karma toplamda yüzde 40, toplam işte yüzde 25, sürede yüzde 23,91 kazanç gösterir ve üç sayının hangisinin ölçüldüğü yazılmadan hızlanma iddiası eksiktir.

Önceki üç ders yavaşlığın kaynağını iki başlıkta topladı: veri **hareket ediyordu** ve yük
**dengesiz dağılmıştı**. Karıştırma 1738 satırı ağdan geçirip toplam işi 2200'den 7414'e çıkardı,
birleştirme stratejisi seçimi kesişme noktasını 1600 ile 4000 satır arasına koydu, `bolge` anahtarı
dört işçinin ikisini boş bıraktı ve çarpıklığı 3,32'ye taşıdı. Üçünde de ölçülen şey tek bir işin
**bir kez** koşmasıydı.

Bir küme üzerinde ara sonuçlar bir kez okunmaz. Aynı 2200 satırlık tüketim tablosunu bir sorgu bölge
toplamı için, bir başkası abone dökümü için ister. Bu ders iki soruyu sorar: aynı hesabın iki kez
yapılması ne kadar tutuyor ve o hesabı bir kez yapıp saklamaya karar verdiğinizde **saklanan şey
gerçekten orada duruyor mu**.

- **DB41.** Kaynak, hattın ilk üç aşaması ve dağıtık kurulum **kurgudur** ve ders içinde üretilir.
  Hiçbir dağıtık işleme çatısı, küme yöneticisi ya da önbellek ürünü çağrılmaz. Tohum 20260218.
- **DB42.** Önbellek isabeti, tahliye politikası ve önbellek boyutlandırma yordamı Veritabanları
  müfredatındaki Bellek İçi Depolar ve Önbellek Sistemleri kursunda, çağrı önbelleği ise Yapay Zekâ
  Mühendisliği müfredatındaki Model API'leri ile Uygulama Geliştirme kursunda ölçüldü;
  **tekrarlanmaz**. Buradaki tek ölçü **ara sonucun yeniden kullanılmasıdır**.
- **DB43. Süre ve toplam iş bu modelin kendi birimidir**, ham milisaniye değildir.
- **DB44.** `iki_sorgu` hazırlığı **toplam iş** olarak (bütün satırlar), sorgu adımını **işçi başına
  pay** olarak sayar. Bu karışık bir ölçüdür; dersin son bölümü onu ikiye ayırır.
- **DB45.** Taban çizgisi **önbelleksiz** kurulumdur: ara sonuç her sorguda yeniden hesaplanır.
  `AG_BEDELI = 3` ve `GOREV_SABIT = 50` birer seçimdir.
- **DB46.** Ara sonucun bellekte tutulması **iş** olarak sayılmaz; bedeli **tutulan satırdır** ve
  o satırlar tutuldukları işçiyle birlikte yiter.

#### Aynı Hesabın İki Kez Yapılması

İlk blok kaynağı, hattın tüketime kadarki üç aşamasını ve iki sorgunun ölçüsünü kurar. `iki_sorgu`
kursun ortak tanımından gelir; `onbellekli` onun genellemesidir ve iki uçta aynı sayıları verir.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin ilk uc asamasi ve dagitik
# kurulum ders icinde uretilir; hicbir dagitik isleme catisi , kume yoneticisi
# ya da onbellek urunu cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


ISCI, AG_BEDELI, GOREV_SABIT = 4, 3, 50


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def iki_sorgu(kayitlar, onbellek=False, isci=ISCI):
    hazirlik = len(kayitlar)
    if onbellek:
        return {"hesaplama": hazirlik, "sorgu": 2 * (hazirlik // isci),
                "toplam": hazirlik + 2 * (hazirlik // isci)}
    return {"hesaplama": 2 * hazirlik, "sorgu": 2 * (hazirlik // isci),
            "toplam": 2 * hazirlik + 2 * (hazirlik // isci)}


# ---- Ayni olcunun genellemesi: q sorgu , tutulan satir kadar ara sonuc ayakta.
def onbellekli(n, q, tutulan, isci=ISCI):
    """Hazirlik toplam is , sorgu adimi isci basina pay olarak sayilir."""
    hazirlik = n + (q - 1) * (n - tutulan)
    return {"hazirlik": hazirlik, "sorgu": q * (n // isci),
            "toplam": hazirlik + q * (n // isci)}


ARA = satirlar()
N = len(ARA)
print(f"ara sonuc {N} satir , isci {ISCI}")
print(f"iki sorgu  onbelleksiz {iki_sorgu(ARA)['toplam']} "
      f"({iki_sorgu(ARA)['hesaplama']} hazirlik + {iki_sorgu(ARA)['sorgu']} sorgu)"
      f"  onbellekli {iki_sorgu(ARA, True)['toplam']} "
      f"({iki_sorgu(ARA, True)['hesaplama']} + {iki_sorgu(ARA, True)['sorgu']})")
print(f"genelleme  {onbellekli(N, 2, 0)['toplam']} ve {onbellekli(N, 2, N)['toplam']}")
print("\nsorgu sayisi  onbelleksiz  onbellekli  fark")
for q in (1, 2, 3, 4):
    a, b = onbellekli(N, q, 0)["toplam"], onbellekli(N, q, N)["toplam"]
    print(f"{q:>12}  {a:>11}  {b:>10}  {a - b:>4}")
```

```
ara sonuc 2200 satir , isci 4
iki sorgu  onbelleksiz 5500 (4400 hazirlik + 1100 sorgu)  onbellekli 3300 (2200 + 1100)
genelleme  5500 ve 3300

sorgu sayisi  onbelleksiz  onbellekli  fark
           1         2750        2750     0
           2         5500        3300  2200
           3         8250        3850  4400
           4        11000        4400  6600
```

Önbelleksiz kurulumda hazırlık **iki kez** yapılır: 4400 birimlik hesaplamanın yarısı, ilk sorgunun
zaten ürettiği bir tabloyu yeniden üretmektir. Önbellek bu ikinci üretimi siler ve toplam 5500'den
3300'e iner. Kazanılan 2200, ara sonucun **kendi satır sayısıdır** — daha fazlası değil.

İlk satır bunun sınırını gösterir. Tek sorguda önbelleksiz ve önbellekli kurulum **aynı 2750'yi**
öder; kazanç sıfırdır, çünkü yeniden kullanılan bir hesap yoktur. Önbellek dördüncü sorguda 6600
kazandırır, ama ilkinde yalnız 2200 satırı bellekte tutar ve karşılığında hiçbir şey vermez.
**Önbelleğin kazancı ara sonucun büyüklüğünden değil, kaç kez okunduğundan gelir.** Bir kez okunan
sonucu önbelleğe almak, bedeli olan ve kazancı olmayan bir karardır.

#### Önbellek Tutulmadığında

Buraya kadarki iki sayı önbelleğin **orada durduğunu** varsaydı. İkinci blok o varsayımı kaldırır.
Ara sonuç `abone` anahtarıyla dört işçiye dağılmıştır ve her işçi kendi payını kendi belleğinde
tutar; bir işçi düştüğünde yalnız o pay yiter.

```python
# Bu blok ilk bloktaki ARA , N , ISCI , GOREV_SABIT ve onbellekli tanimlarini surdurur.
def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


def dagit(kayitlar, anahtar, isci=ISCI):
    kova = {i: [] for i in range(isci)}
    for k in kayitlar:
        kova[izno(k[anahtar]) % isci].append(k)
    return kova


KOVA = dagit(ARA, "abone")
PAY = [len(KOVA[i]) for i in range(ISCI)]
print(f"ara sonucun isci paylari {PAY}")
print("ayakta isci  tutulan satir  iki sorgunun toplami  onbelleksize gore")
for ayakta in (4, 3, 2, 1, 0):
    t = sum(PAY[:ayakta])
    top = onbellekli(N, 2, t)["toplam"]
    print(f"{ayakta:>11}  {t:>13}  {top:>20}  {top - 5500:>15}")
print("tek isci duserse toplam "
      + " , ".join(str(onbellekli(N, 2, N - p)["toplam"]) for p in PAY))


# ---- Ayni kurulum , iki ayri olcu: sure (isci basina) ve toplam is.
def sure_is(n, q, tutulan, isci=ISCI):
    hazirlik = n + (q - 1) * (n - tutulan)
    return {"sure": hazirlik // isci + q * (n // isci) + q * GOREV_SABIT,
            "toplam_is": hazirlik + q * n}


bos, dolu = sure_is(N, 2, 0), sure_is(N, 2, N)
print("\nolcu          onbelleksiz  onbellekli  kazanc")
for ad, a, b in (("karma toplam", 5500, 3300),
                 ("toplam is", bos["toplam_is"], dolu["toplam_is"]),
                 ("sure", bos["sure"], dolu["sure"])):
    print(f"{ad:<12}  {a:>11}  {b:>10}  {round((a - b) / a * 100, 2):>5} %")
```

```
ara sonucun isci paylari [539, 550, 561, 550]
ayakta isci  tutulan satir  iki sorgunun toplami  onbelleksize gore
          4           2200                  3300            -2200
          3           1650                  3850            -1650
          2           1089                  4411            -1089
          1            539                  4961             -539
          0              0                  5500                0
tek isci duserse toplam 3839 , 3850 , 3861 , 3850

olcu          onbelleksiz  onbellekli  kazanc
karma toplam         5500        3300   40.0 %
toplam is            8800        6600   25.0 %
sure                 2300        1750  23.91 %
```

İlk tablonun son satırı dersin çekirdeğidir: hiç tutulmayan bir önbellek **tam olarak 5500** öder,
yani önbelleksiz kurulumun bedelini. Ne bir birim fazla ne bir birim eksik. Ama planı yapan kişi
3300'e bakarak karar vermiştir; iş bittiğinde çıkan sonuç aynıdır, sorgular doğrudur, hiçbir denetim
ateşlemez ve tek fark **ödenen bedeldir**. Bu, kursun üçüncü iddiasının bu derse düşen biçimidir:
**yanlış giden şey çıktıda değil, yalnız sayıda görünür.**

Ara satırlar kazancın nasıl eridiğini gösterir. Tutulan satır azaldıkça toplam doğrusal olarak
tırmanır ve kazanç her zaman **tutulan satır kadardır**: 2200 satır ayaktaysa 2200, 1089 satır
ayaktaysa 1089. Dörtte üçü ayakta kalan bir önbellek kazancın dörtte üçünü verir. Tek bir işçinin
düşmesi bile toplamı 3300'den 3839 ile 3861 arasına taşır; hangi işçinin düştüğü 22 birimlik bir
fark yaratır, çünkü paylar 539 ile 561 arasında eşit değildir.

**Kalıcılık** bu tablonun altındaki karardır. Ara sonucu bellekte tutmak onu işçinin ömrüne bağlar;
kalıcı bir yere yazmak, işçi düştüğünde tutulan satırı 2200'de bırakır. Karşılığında yazma işi
eklenir ve o iş, satırların ne kadarının gerçekten yeniden okunacağından bağımsız olarak **kesin
biçimde** ödenir. Önbelleğin kazancı olasılıklı, kalıcılığın bedeli kesindir.

#### Önbelleğin Düzeni

Buraya kadar önbellek bir **satır kümesi** gibi ele alındı. Dağıtık bir kümede öyle değildir: ara
sonuç işçilere bir anahtara göre dağılmış hâlde durur ve o düzen, onu okuyacak sorgunun karıştırmaya
ihtiyaç duyup duymayacağını belirler. Üçüncü blok iki sorguyu adlandırır — biri bölge toplamı ister,
öbürü abone dökümü — ve önbelleğin hangi düzende tutulduğunu değiştirir.

```python
# Bu blok ilk iki bloktaki ARA , N , KOVA , ISCI , AG_BEDELI , izno ve dagit
# tanimlarini surdurur.
def karistir(kova, yeni_anahtar, isci=ISCI):
    yeni = {i: [] for i in range(isci)}
    tasinan = 0
    for eski, kayitlar in kova.items():
        for k in kayitlar:
            hedef = izno(k[yeni_anahtar]) % isci
            if hedef != eski:
                tasinan += 1
            yeni[hedef].append(k)
    return yeni, tasinan


BOLGELI, ab_bo = karistir(KOVA, "bolge")
_, bo_ab = karistir(BOLGELI, "abone")
print(f"abone duzeninden bolgeye tasinan {ab_bo} , bolge duzeninden aboneye {bo_ab}")
print("onbellek duzeni     tutulan  4 sorguda tasinan  ag isi")
for ad, tutulan, tasinan in (("abone", N, 2 * ab_bo), ("bolge", N, 2 * bo_ab),
                             ("iki kopya", 2 * N, ab_bo)):
    print(f"{ad:<18}  {tutulan:>7}  {tasinan:>17}  {tasinan * AG_BEDELI:>6}")
```

```
abone duzeninden bolgeye tasinan 1738 , bolge duzeninden aboneye 1738
onbellek duzeni     tutulan  4 sorguda tasinan  ag isi
abone                  2200               3476   10428
bolge                  2200               3476   10428
iki kopya              4400               1738    5214
```

İki yön de **aynı 1738 satırı** taşır; hangi düzende saklandığı, hangi sorgunun bedava olacağını
seçmekten başka bir şey yapmaz. Dört sorgunun ikisi bölge, ikisi abone soruyorsa tek düzenli
önbellek 3476 satır taşır ve `AG_BEDELI = 3` ile 10428 birimlik ağ işi doğurur. İki kopya tutmak bu
sayıyı 5214'e indirir, çünkü ikinci kopya bir kez kurulur ve sonra hiç karıştırılmaz.

Bedel bellekte görünür: tutulan satır 2200'den **4400**'e çıkar ve bir önceki bölümün tablosuna göre
düşen her işçi artık iki kopyadan da pay götürür. Önbellek burada bir satır kümesi değil, **bir satır
kümesi artı bir düzendir**; ikinci düzeni saklamak ikinci bir önbellek tutmaktır.

#### Hangi Sayı İndi

İkinci bloğun son tablosu tek bir önbellek kararını üç ölçüyle yazar ve üçü de doğrudur. Karma toplam yüzde 40
kazanç gösterir, çünkü hazırlığı bütün satırlarla, sorguyu işçi başına payla sayar. Toplam iş yüzde
25 gösterir: 8800'den 6600'e inen sayıda sorgu adımı da bütün satırlarla sayılmıştır ve önbellek ona
dokunmaz. Süre yüzde 23,91 gösterir; işçi başına düşen hazırlık 1100'den 550'ye iner, ama her sorgu
kendi `GOREV_SABIT = 50` bedelini yine öder.

Aynı değişikliğin üç ayrı hızlanma sayısı vardır ve aralarındaki en büyük fark yüzde 16'dır. Bir
kurulum için "önbellek işi yüzde kırk hızlandırdı" cümlesi, hangi sayının indiğini yazmadıkça
doğrulanabilir bir iddia değildir. **Süre ile toplam iş ayrı ölçülür**; ikisi bu derste aynı yöne
gitti, ama bu bir kural değil, bu kurulumun sonucudur.

#### Özet

- 2200 satırlık ara sonucu paylaşan iki sorgunun toplamı önbelleksiz **5500**, önbellekli
  **3300**'dür; kazanılan 2200, ara sonucun kendi satır sayısıdır.
- Tek sorguda iki kurulum da **2750** öder. Önbelleğin kazancı ara sonucun büyüklüğünden değil, kaç
  kez okunduğundan gelir; bir kez okunan sonucu önbelleğe almak bedeli olan ve kazancı olmayan bir
  karardır.
- Ara sonuç dört işçide 539, 550, 561 ve 550 satır olarak durur. İşçiler düştükçe toplam 3300'den
  3850, 4411, 4961 ve **5500**'e çıkar; hiç tutulmayan önbellek tam olarak önbelleksiz kurulumun
  bedelini öder.
- Kazanç her zaman tutulan satır kadardır ve tek bir işçinin düşmesi toplamı 3839 ile 3861 arasına
  taşır. Kalıcı yazma tutulan satırı 2200'de bırakır, karşılığında yazma bedeli kesin ödenir.
- Önbellek bir satır kümesi değil, bir satır kümesi artı bir düzendir: iki yön de aynı **1738**
  satırı taşır, dört sorguda tek düzen 3476 satır ve 10428 birimlik ağ işi doğurur, iki kopya bunu
  5214'e indirirken tutulan satırı 2200'den 4400'e çıkarır.
- Aynı önbellek karma toplamda yüzde **40**, toplam işte yüzde **25**, sürede yüzde **23,91** kazanç
  gösterir; hangisinin ölçüldüğü yazılmayan hızlanma iddiası eksiktir.

#### Sonraki Adım

Bu dersin son tablosu bir işçinin düşmesini yalnız **önbelleğin yitmesi** olarak saydı: kaybolan şey
saklanmış bir ara sonuçtu ve yeniden hesaplanabiliyordu. Oysa sürekli akan bir işte işçinin
belleğinde duran şey ara sonuç değil, **hesabın o ana kadarki durumudur** ve o durum yeniden
hesaplanmak istendiğinde nereden başlanacağı belli değildir. Sonraki ders düşen işçinin **ne kadarını
geri istediğini** sayar: durumu hiç kaydetmeyen bir işte kaç satırın yeniden hesaplandığını, kayıt
sıklığını artırmanın bu sayıyı ne kadar indirdiğini ve aynı satırın iki kez sayılmasını önlemenin
neye mal olduğunu.

### Akış Motorlarında Durum

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/akis-motorlarinda-durum

Sürekli akan bir işte düşen bir işçinin ne kadarını geri istediğinin ve denetim noktası sıklığını artırmanın bu sayıyı gerçekten indirip indirmediğinin sayılması: 2200 satırlık akan işte 2026-09 döneminde düşme olduğunda denetim noktası yokken 1600 satır yeniden hesaplanır, aralık 6'da 400, aralık 3'te yine 400, aralık 1'de 0. Aralığı yarıya indirmek kurtarmayı yarıya indirmez, çünkü düşmeden önceki son nokta yerinden oynamamıştır; aralık 4 aynı düşmede 0 verir. On bir düşme noktasının tamamı tarandığında aralık 6 ortalama 545,5, aralık 3 ise 218,2 satır yeniden hesaplatır, ama kesin bedel 400'den 800'e çıkar; en kötü duruma göre aralık 4 ile 3 beraberdir, ortalamaya göre aralık 4 kazanır. Düşme olasılığı 1'den 0,5'e indiğinde en iyi seçim denetim noktası tutmamaya döner. Bölümlü durumda düşen tek işçi 1600 yerine 408, 400 yerine 102 satır geri ister ve tam bir kez tam olarak o satır kadar durum tutmayı gerektirir.

Bir önceki ders düşen bir işçiyi yalnız **önbelleğin yitmesi** olarak saydı: kaybolan şey saklanmış
bir ara sonuçtu, yeniden hesaplanabiliyordu ve iki sorgunun toplamı 3300'den 5500'e çıkıyordu.
Toplu bir işte doğru olan budur, çünkü girdi hâlâ yerinde durur.

Sürekli akan bir işte durum başkadır. İşçinin belleğinde duran şey bir ara sonuç değil, hesabın o ana
kadarki **durumudur**: hangi abonenin hangi dönemine kadar gelindiği, hangi kısmi toplamın nerede
kaldığı. Düşme olduğunda bu durum kaybolur ve iş bir yerden **yeniden** başlamak zorundadır. Bu ders
tek bir soruyu sayar: düşen işçi geriye ne kadarını istiyor ve o sayıyı küçültmek için ödenen bedel
gerçekten karşılığını veriyor mu.

- **DB47.** Kaynak, hat ve dağıtık kurulum **kurgudur** ve ders içinde üretilir. Hiçbir akış motoru,
  küme yöneticisi ya da durum deposu çağrılmaz. Tohum 20260218.
- **DB48.** Denetim noktası, yinelemeye dayanıklılık ve tam bir kez semantiğinin yordamı Veri
  Mühendisliği ve MLOps müfredatındaki Veri Hatları ve Orkestrasyon kursunun Veri Alma konusunda ve
  Backend Geliştirme müfredatındaki Önbellekleme, Kuyruklar ve Eşzamansız İşleme kursunda ölçüldü;
  **tekrarlanmaz**. Oradaki sayılar burada **girdidir**: en az bir kez 2200 satır ve 19 yinelenme, en
  çok bir kez 2162 satır. Buradaki tek ölçü **kurtarmanın bedelidir**.
- **DB49.** Akış işi dönem dönem ilerler ve **düşme `2026-09` döneminde** olur; iş düşmeden önceki
  son denetim noktasından sürer. Kodda `kontrol` alanı denetim noktası sayısını taşır.
- **DB50.** Bir denetim noktası o anki durumu kalıcı kılar; durum **abone başına bir giriş**, yani
  `DURUM_SATIRI = 200` satırdır. Bu bir seçimdir ve aşağıdaki bütün toplamlar ona duyarlıdır.
- **DB51.** Ortalama alınırken düşmenin on bir dönemin **herhangi birinde eşit olasılıkla** olduğu
  varsayılır; bu da bir seçimdir. Satır bu modelin kendi birimidir, ham milisaniye değildir.
- **DB52.** Taban çizgisi **denetim noktası tutmayan** kurulumdur: nokta 0, kesin bedel 0, düşme
  sonrası baştan başlanır.

#### Kurtarmanın Bedeli

İlk blok kaynağı, hattın tüketime kadarki üç aşamasını ve `kurtarma` ölçüsünü kurar. `kurtarma`
düşme dönemine kadarki satırların kaçının yeniden hesaplanacağını sayar.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin ilk uc asamasi ve akan is ders
# icinde uretilir; hicbir akis motoru , kume yoneticisi ya da durum deposu
# cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


ISCI, AG_BEDELI, GOREV_SABIT, DURUM_SATIRI = 4, 3, 50, 200


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def kurtarma(kayitlar, kontrol_araligi, dusen_donem="2026-09", isci=ISCI):
    """Denetim noktasi yoksa bastan; varsa son noktadan."""
    i = DONEM.index(dusen_donem)
    if kontrol_araligi is None:
        yeniden = sum(1 for k in kayitlar if DONEM.index(k["donem"]) <= i)
        return {"kontrol": 0, "yeniden": yeniden}
    son = (i // kontrol_araligi) * kontrol_araligi
    yeniden = sum(1 for k in kayitlar
                  if son < DONEM.index(k["donem"]) <= i)
    return {"kontrol": len(DONEM) // kontrol_araligi, "yeniden": yeniden}


AKAN = satirlar()
print(f"akan is {len(AKAN)} satir , dusme 2026-09")
print("aralik  denetim noktasi  yeniden hesaplanan satir")
for a in (None, 6, 3, 1):
    s = kurtarma(AKAN, a)
    print(f"{str(a):>6}  {s['kontrol']:>15}  {s['yeniden']:>24}")
```

```
akan is 2200 satir , dusme 2026-09
aralik  denetim noktasi  yeniden hesaplanan satir
  None                0                      1600
     6                2                       400
     3                4                       400
     1               12                         0
```

Dört satırın üçü beklenendir. Denetim noktası tutmayan iş düşme dönemine kadarki **1600 satırın
tamamını** yeniden hesaplar; aralık 6 bunu 400'e indirir; aralık 1 her dönemi kaydettiği için sıfıra
indirir. Üçüncü satır beklenen değildir: **aralık 6'dan aralık 3'e inmek yeniden hesaplanan satırı
hiç değiştirmez.** Aralık ikiye bölünmüş, tutulan nokta sayısı 2'den 4'e çıkmış ve kurtarma 400'de
kalmıştır. Ödenen iki katına çıkmış, alınan aynı kalmıştır.

#### Aralığı Yarıya İndirmek

Nedeni tek bir hesapta durur. Düşme `2026-09`, yani on ikinci dizinin sekizinci sırasındadır. Aralık
6'da düşmeden önceki son nokta altıncı dönemdir; aralık 3'te de altıncı dönemdir, çünkü sekizin üçe
bölümünden kalan iki, son noktayı geriye altıya taşır. **Aralık yarıya indi ama düşmeden önceki son
nokta yerinden oynamadı.** İkinci blok bu davranışı on bir düşme noktasının tamamında tarar.

```python
# Bu blok ilk bloktaki AKAN , DONEM , DURUM_SATIRI ve kurtarma tanimlarini surdurur.
ARALIK = (None, 6, 4, 3, 2, 1)
print("dusme    " + "  ".join(f"{str(a):>5}" for a in ARALIK))
for d in DONEM[1:]:
    print(f"{d}  " + "  ".join(f"{kurtarma(AKAN, a, d)['yeniden']:>5}" for a in ARALIK))
print("\naralik  nokta  kesin bedel  en kotu  en kotu toplam  ortalama  ortalama toplam")
for a in ARALIK:
    y = [kurtarma(AKAN, a, d)["yeniden"] for d in DONEM[1:]]
    k = kurtarma(AKAN, a)["kontrol"] * DURUM_SATIRI
    o = sum(y) / len(y)
    print(f"{str(a):>6}  {kurtarma(AKAN, a)['kontrol']:>5}  {k:>11}  {max(y):>7}"
          f"  {max(y) + k:>14}  {round(o, 1):>8}  {round(o + k, 1):>15}")
```

```
dusme     None      6      4      3      2      1
2026-02    200    200    200    200    200      0
2026-03    400    400    400    400      0      0
2026-04    600    600    600      0    200      0
2026-05    800    800      0    200      0      0
2026-06   1000   1000    200    400    200      0
2026-07   1200      0    400      0      0      0
2026-08   1400    200    600    200    200      0
2026-09   1600    400      0    400      0      0
2026-10   1800    600    200      0    200      0
2026-11   2000    800    400    200      0      0
2026-12   2200   1000    600    400    200      0

aralik  nokta  kesin bedel  en kotu  en kotu toplam  ortalama  ortalama toplam
  None      0            0     2200            2200    1200.0           1200.0
     6      2          400     1000            1400     545.5            945.5
     4      3          600      600            1200     327.3            927.3
     3      4          800      400            1200     218.2           1018.2
     2      6         1200      200            1400     109.1           1309.1
     1     12         2400        0            2400       0.0           2400.0
```

Matris kazancın nereden geldiğini adıyla gösterir: **düşmenin son noktaya olan uzaklığından.**
`2026-07`'de aralık 6 sıfır verir, çünkü düşme tam noktanın üstüne gelmiştir; aynı aralık
`2026-12`'de 1000 verir. Aralık 4, `2026-09`'da 0 verirken aralık 3 orada 400 verir — daha sık nokta
tutan kurulum, o düşmede daha kötüdür. Altı sütunun hiçbiri öbürünü her satırda yenmez.

Ortalama sütunu bu gürültüyü siler ve düzenli davranır: 1200,0 → 545,5 → 327,3 → 218,2 → 109,1 → 0.
**Ortalamada aralığı yarıya indirmek kurtarmayı yaklaşık yarıya indirir; tek bir düşmede indirmez.**
Bir kurulum tek bir düşme yaşar, ortalama yaşamaz; kararı ortalamayla veren, gerçekleşen düşmeyle
karşılaşır.

Son iki sütun bedeli ekler. Denetim noktası tutmak `DURUM_SATIRI = 200` üzerinden kesin bedel öder:
aralık 6'da 400, aralık 3'te 800, aralık 1'de 2400. En kötü duruma göre en ucuz kurulum **1200 ile
aralık 4 ve aralık 3**, ortalamaya göre **927,3 ile aralık 4**. İki ölçüt iki farklı yanıt verir ve
"aralığı yarıya indir" kuralı ikisinde de en iyi değildir.

#### Kesin Bedel ve Olasılıklı Kazanç

Yukarıdaki toplamlar sessiz bir varsayım taşır: her koşumda bir düşme olduğu. Üçüncü blok bu
varsayımı gevşetir ve aynı kararı dört düşme olasılığında yeniden verir. Ardından işi dört işçiye
bölüp düşen tek işçinin ne kadarını geri istediğini sayar.

```python
# Bu blok ilk iki bloktaki AKAN , DONEM , ISCI , DURUM_SATIRI , ARALIK ve
# kurtarma tanimlarini surdurur.
def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


def dagit(kayitlar, anahtar, isci=ISCI):
    kova = {i: [] for i in range(isci)}
    for k in kayitlar:
        kova[izno(k[anahtar]) % isci].append(k)
    return kova


print("dusme olasiligi  " + "  ".join(f"{str(a):>6}" for a in ARALIK) + "   en iyi")
for p in (1.0, 0.75, 0.5, 0.25):
    s = []
    for a in ARALIK:
        y = [kurtarma(AKAN, a, d)["yeniden"] for d in DONEM[1:]]
        s.append(p * sum(y) / len(y) + kurtarma(AKAN, a)["kontrol"] * DURUM_SATIRI)
    print(f"{p:>15}  " + "  ".join(f"{round(v, 1):>6}" for v in s)
          + f"   {str(ARALIK[s.index(min(s))]):>6}")

KOVA = dagit(AKAN, "abone")
print("\naralik  butun is bastan  bolumlu durumda en yuklu isci  tam bir kez durumu")
for a in (None, 6, 3, 1):
    g = kurtarma(AKAN, a)["yeniden"]
    per = max(kurtarma(KOVA[i], a)["yeniden"] for i in range(ISCI))
    print(f"{str(a):>6}  {g:>16}  {per:>31}  {g:>19}")
```

```
dusme olasiligi    None       6       4       3       2       1   en iyi
            1.0  1200.0   945.5   927.3  1018.2  1309.1  2400.0        4
           0.75   900.0   809.1   845.5   963.6  1281.8  2400.0        6
            0.5   600.0   672.7   763.6   909.1  1254.5  2400.0     None
           0.25   300.0   536.4   681.8   854.5  1227.3  2400.0     None

aralik  butun is bastan  bolumlu durumda en yuklu isci  tam bir kez durumu
  None              1600                              408                 1600
     6               400                              102                  400
     3               400                              102                  400
     1                 0                                0                    0
```

İlk tablo aralık kararının **iş yüküne değil, düşme sıklığına** bağlı olduğunu gösterir. Her koşumda
düşme varsa en iyi seçim aralık 4'tür; dörtte üç olasılıkta aralık 6'ya kayar; olasılık yarıya
indiğinde en iyi seçim **hiç nokta tutmamaktır**. Sebebi tek cümlededir: **denetim noktasının bedeli
kesindir, kazancı olasılıklıdır.** Kesin bedel her koşumda ödenir; kazanç yalnız düşme olduğunda ve
yalnız düşmenin son noktaya uzaklığı kadar gerçekleşir.

#### Düşen İşçi Ne Kadarını Geri İstiyor

İkinci tablo dersin açılış sorusunu yanıtlar. İş `abone` anahtarıyla dört işçiye bölünmüşse ve durum
her işçide **kendi bölümü için** tutuluyorsa, düşen tek işçi bütün işi değil yalnız kendi payını geri
ister: 1600 yerine **408**, 400 yerine **102** satır. Bölümlü durum kurtarmayı dörtte bire indirir —
ama yalnız durumun bölümlü tutulması koşuluyla. Durum tek bir yerde toplanmışsa ya da motor düşme
üzerine bütün işi yeniden başlatıyorsa geri istenen sayı yine 1600 ile 400'dür. **Bölüşüm kurtarmayı
ancak durum da bölünmüşse ucuzlatır.**

Son sütun **tam bir kez** semantiğinin bedelidir ve sayısı şaşırtıcı biçimde tanıdıktır. Kurtarma
sırasında yeniden hesaplanan satırlar çıkışa **ikinci kez** yazılır; bunu engellemenin tek yolu, son
noktadan bu yana hangi satırın yazıldığını hatırlamaktır. O küme tam olarak yeniden hesaplanan
satırlar kümesidir: aralık yokken 1600, aralık 6 ve 3'te 400, aralık 1'de 0. **Tam bir kez, kurtarmanın
yeniden hesapladığı satır kadar durum tutmaktır**; aralığı sıklaştırmak kurtarmayı ucuzlatırken bu
durumu da küçültür ve iki kazanç aynı sayıdan gelir.

Bu kümenin kendisi de bir durumdur ve düşen işçiyle birlikte yiter. Yazılan satırların listesi
yalnız bellekte duruyorsa, kurtarma onu da kaybeder ve hangi satırın çıkışa gittiği bilinmez hâle
gelir; tam bir kez ancak liste denetim noktasının içine alınırsa sağlanır. Bu, kesin bedeli
`DURUM_SATIRI = 200`'ün ötesine taşır: nokta artık kısmi toplamları değil, son noktadan bu yana
yazılmış satırların kimliğini de tutar. Aralık 6'da bu 400 kimliktir, aralık 3'te de 400.

Bu tutulmazsa ne olduğu Veri Hatları ve Orkestrasyon kursunda ölçülmüştü: en az bir kez kurulum 2200
satırı geçirir, 19 yinelenme oluşur ve sunulan sapma **boş kalır**. Kursun üçüncü iddiası burada da
geçerlidir: tam bir kez sağlandığı sürece dört aralığın dördü de **aynı sonucu** üretir. Sonuca
bakarak hangi aralığın seçildiğini söylemenin yolu yoktur; fark yalnız ödenen bedeldedir.

#### Özet

- 2200 satırlık akan işte `2026-09` düşmesinde yeniden hesaplanan satır denetim noktası yokken
  **1600**, aralık 6'da **400**, aralık 3'te **yine 400**, aralık 1'de **0**'dır.
- Aralığı yarıya indirmek kurtarmayı yarıya indirmez: düşmeden önceki son nokta yerinden oynamamıştır.
  Aynı düşmede aralık 4 **0**, aralık 3 **400** verir; daha sık nokta tutan kurulum daha kötü olabilir.
- On bir düşme noktasının ortalaması düzenlidir (1200,0 → 545,5 → 327,3 → 218,2 → 109,1 → 0), ama bir
  kurulum ortalama değil tek bir düşme yaşar. En kötü duruma göre en ucuzu 1200 ile aralık 4 ve 3,
  ortalamaya göre 927,3 ile aralık 4'tür.
- Denetim noktasının bedeli kesin, kazancı olasılıklıdır: düşme olasılığı 1'de en iyi aralık 4, 0,75'te
  6, 0,5 ve altında **hiç nokta tutmamaktır**.
- Bölümlü durumda düşen tek işçi 1600 yerine **408**, 400 yerine **102** satır geri ister; bölüşüm
  kurtarmayı ancak durum da bölünmüşse ucuzlatır.
- Tam bir kez, kurtarmanın yeniden hesapladığı satır kadar durum tutmaktır: 1600, 400, 400 ve 0. Bu
  sağlandığı sürece dört aralık da aynı sonucu üretir ve fark yalnız bedeldedir.

#### Sonraki Adım

Beş ders bedelin beş ayrı adını yazdı: taşınan satır, kopya, çarpıklık, tutulan satır ve yeniden
hesaplanan satır. Hepsinin altında aynı iki sayı duruyordu — bir işin **ne kadar sürdüğü** ve toplamda
**ne kadar iş yaptığı** — ve her derste bu ikisi ayrı ayrı yazıldı. Geriye tek bir soru kalıyor: bir
küme kaç işçiyle kurulmalı. Sonraki ders işçi sayısını birden on altıya kadar süpürür ve süre inerken
toplam işin çıktığını gösterir; hangisinin ödendiği yazılmadan bu kararın verilemeyeceğini sayar ve
kursu kapatır.

### Maliyet ve Küme Boyutlandırma

Source: https://academia.sh/tr/kurslar/dagitik-veri-isleme/maliyet-ve-kume-boyutlandirma

Bir kümenin kaç işçiyle kurulacağı kararının süre ile toplam iş arasında verildiğinin ve iki sayının ters yönlere gittiğinin sayılması: aynı 2200 satırlık iş bir, iki, dört, sekiz ve on altı işçide süre 2250, 3691, 3179, 2610 ve 1398, toplam iş 2200, 5830, 7414, 8074 ve 8635 verir; süre inerken toplam iş çıkar ve hangisinin ödendiği yazılmadan boyutlandırma kararı verilemez. İkinci işçinin süreyi 2250'den 3691'e çıkarması karıştırmanın doğmasından gelir ve çarpıklığı 1,00 olan iyi bir anahtarda bile 2250'den 2800'e çıkış görülür. İşçi ile sürenin çarpımı 2250'den 22368'e tek yönlü tırmanır, sabit bedel işçi başına sayıldığında on altı işçinin kazancı 852'den 102'ye iner ve ağ bedeli 12'ye çıktığında en kısa süreyi veren kurulum tek işçiye döner. Ders kursun kapanışıdır ve on bir dersin sayılarını tek tabloda toplar.

Bir önceki ders bedelin beşinci adını yazdı: yeniden hesaplanan satır. Ondan önce taşınan satır,
kopya, çarpıklık ve tutulan satır gelmişti. Beş adın altında hep aynı iki sayı duruyordu — bir işin
**ne kadar sürdüğü** ve toplamda **ne kadar iş yaptığı**. Geriye kursun en sık sorulan sorusu
kalıyor: bu küme kaç işçiyle kurulmalı.

Soru masum görünür ve yanıtı yoktur, çünkü eksiktir. Bir kümeyi büyütmek iki sayıyı aynı anda
değiştirir ve bunlar **ters yönlere** gider. Bu ders işçi sayısını birden on altıya süpürür, her
adımda ikisini de yazar ve kararın hangi ölçüte, hangi seçime ve hangi bölüşüm anahtarına asılı
olduğunu sayar.

- **DB53.** Kaynak, hat ve dağıtık kurulum **kurgudur** ve ders içinde üretilir. Hiçbir dağıtık
  işleme çatısı, küme yöneticisi ya da bulut servisi çağrılmaz. Tohum 20260218.
- **DB54.** Maliyet muhasebesi yordamı Bulut Bilişim müfredatındaki Bulut Yönetişimi, Güvenlik ve
  Maliyet kursunda ve Yapay Zekâ Mühendisliği müfredatındaki Model API'leri ile Uygulama Geliştirme
  kursunda ölçüldü; **tekrarlanmaz**. Burada maliyet yalnız **süre ve toplam iş** olarak yazılır;
  hiçbir para birimi ya da fiyat kullanılmaz.
- **DB55. Süre en yüklü işçinin işidir** ve bu modelin kendi birimidir, ham milisaniye değildir.
  **Toplam iş** bütün işçilerin yaptığı işin toplamıdır.
- **DB56.** `AG_BEDELI = 3` ve `GOREV_SABIT = 50` birer seçimdir; bu dersin sonucu **ikisine de
  duyarlıdır** ve aşağıda ikisi de değerleriyle birlikte ele alınır.
- **DB57.** `maliyet` sabit bedeli **iş başına bir kez** sayar; Kaynak Yöneticileri dersi aynı bedeli
  **işçi başına** sayar. İki sayım burada yan yana iki sütun olarak yazılır.
- **DB58.** Taban çizgisi tek işçidir: karıştırma yok, çarpıklık 1,00, süre **2250**, toplam iş
  **2200**.

#### İşçi Süpürmesi

İlk blok kaynağı, hattın tüketime kadarki üç aşamasını ve bölüşüm ile maliyet ölçülerini kurar.
Ölçülen iş kursun boyunca kullanılan iştir: satırlar `bolge` anahtarıyla bölüşülmüş, sonra `abone`
anahtarına karıştırılıyor.

```python
# KURGUDUR. Olcum aginin sayac okumalari , hattin ilk uc asamasi ve dagitik
# kurulum ders icinde uretilir; hicbir dagitik isleme catisi , kume yoneticisi
# ya da bulut servisi cagrilmaz. Tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


ISCI, AG_BEDELI, GOREV_SABIT = 4, 3, 50


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def izno(m):
    h = 2166136261
    for c in str(m):
        h = ((h ^ ord(c)) * 16777619) & 0xFFFFFFFF
    return h


def dagit(kayitlar, anahtar, isci=ISCI):
    kova = {i: [] for i in range(isci)}
    for k in kayitlar:
        kova[izno(k[anahtar]) % isci].append(k)
    return kova


def yuk(kova):
    n = [len(v) for v in kova.values()]
    return {"toplam": sum(n), "en_yuklu": max(n), "en_az": min(n),
            "carpiklik": round(max(n) / (sum(n) / len(n)), 4)}


def karistir(kova, yeni_anahtar, isci=ISCI):
    yeni = {i: [] for i in range(isci)}
    tasinan = 0
    for eski, kayitlar in kova.items():
        for k in kayitlar:
            hedef = izno(k[yeni_anahtar]) % isci
            if hedef != eski:
                tasinan += 1
            yeni[hedef].append(k)
    return yeni, tasinan


def maliyet(kova, tasinan=0, isci=ISCI, ag=AG_BEDELI, sabit=GOREV_SABIT):
    """Sure = en yuklu iscinin isi (paralel). Toplam is ayrica sayilir."""
    n = [len(v) for v in kova.values()]
    return {"sure": max(n) + tasinan * ag // isci + sabit,
            "toplam_is": sum(n) + tasinan * ag, "tasinan": tasinan}


TUM = satirlar()
SAYI = (1, 2, 4, 8, 16)
print(f"is {len(TUM)} satir , bolusum anahtari bolge , karistirma aboneye")
print("isci  carpiklik  tasinan  sure  toplam is  isci x sure  sure (sabit isci basina)")
for i in SAYI:
    kv = dagit(TUM, "bolge", i)
    _, t = karistir(kv, "abone", i)
    m = maliyet(kv, t, i)
    mb = maliyet(kv, t, i, sabit=i * GOREV_SABIT)
    print(f"{i:>4}  {yuk(kv)['carpiklik']:>9}  {t:>7}  {m['sure']:>4}  {m['toplam_is']:>9}"
          f"  {i * m['sure']:>11}  {mb['sure']:>23}")
```

```
is 2200 satir , bolusum anahtari bolge , karistirma aboneye
isci  carpiklik  tasinan  sure  toplam is  isci x sure  sure (sabit isci basina)
   1        1.0        0  2250       2200         2250                     2250
   2       1.66     1210  3691       5830         7382                     3741
   4       3.32     1738  3179       7414        12716                     3329
   8       6.64     1958  2610       8074        20880                     2960
  16       6.88     2145  1398       8635        22368                     2148
```

Süre sütunu düz inmez: 2250'den **3691'e çıkar**, sonra 3179, 2610 ve 1398'e iner. Tek işçinin
altına ancak on altı işçide düşer. Toplam iş sütunu ise hiç geri dönmez: 2200'den 5830'a, 7414'e,
8074'e ve 8635'e **tek yönlü tırmanır**. Aynı satır kümesi, aynı sonuç, dört kat toplam iş.

İkinci satır kursun ikinci iddiasının en net örneğidir. Tek işçide karıştırma yoktur; ikinci işçi
eklendiği anda 1210 satır ağdan geçmek zorunda kalır, toplam iş 2200'den 5830'a sıçrar ve en yüklü
işçinin payı hâlâ büyük olduğu için süre de yükselir. **Paralellik bedava değildir ve ilk adımı
negatiftir.**

Son iki sütun kararın nasıl döndüğünü gösterir. İşçi ile sürenin çarpımı — kümeyi iş boyunca
kiralamanın karşılığı — 2250'den **22368**'e hiç inmeden tırmanır; bu ölçüte göre en iyi kurulum tek
işçidir. Sabit bedel işçi başına sayıldığında on altı işçinin süresi 1398'den **2148**'e çıkar ve tek
işçiye olan üstünlüğü 852 birimden **102 birime** iner. `GOREV_SABIT = 50` gibi küçük bir seçim, on
altı katlık bir kurulumun kazancını neredeyse siler.

#### Karar Neye Asılı

Üç ölçüt üç ayrı yanıt verdi. Sıradaki soru bu yanıtların modeldeki iki seçime ve bölüşüm anahtarına
ne kadar bağlı olduğudur. İkinci blok ağ bedelini süpürür, sonra aynı süpürmeyi çarpıklığı olmayan
bir anahtarla tekrarlar.

```python
# Bu blok ilk bloktaki TUM , SAYI , dagit , yuk , karistir ve maliyet tanimlarini surdurur.
print("ag bedeli  " + "  ".join(f"{i:>5}" for i in SAYI) + "   en kisa sure")
for ag in (1, 3, 6, 12):
    s = []
    for i in SAYI:
        kv = dagit(TUM, "bolge", i)
        _, t = karistir(kv, "abone", i)
        s.append(maliyet(kv, t, i, ag=ag)["sure"])
    print(f"{ag:>9}  " + "  ".join(f"{v:>5}" for v in s) + f"   {SAYI[s.index(min(s))]:>13}")

print("\nbolusum anahtari abone , karistirma doneme")
print("isci  carpiklik  tasinan  sure  toplam is")
for i in SAYI:
    kv = dagit(TUM, "abone", i)
    _, t = karistir(kv, "donem", i)
    m = maliyet(kv, t, i)
    print(f"{i:>4}  {yuk(kv)['carpiklik']:>9}  {t:>7}  {m['sure']:>4}  {m['toplam_is']:>9}")
```

```
ag bedeli      1      2      4      8     16   en kisa sure
        1   2250   2481   2310   2120   1130              16
        3   2250   3691   3179   2610   1398              16
        6   2250   5506   4483   3344   1800              16
       12   2250   9136   7090   4813   2604               1

bolusum anahtari abone , karistirma doneme
isci  carpiklik  tasinan  sure  toplam is
   1        1.0        0  2250       2200
   2        1.0     1100  2800       5500
   4       1.02     1651  1849       7153
   8       1.04     1927  1058       7981
  16        1.2     2066   602       8398
```

İlk tablo `AG_BEDELI`'nin bir seçim olmasının ne demek olduğunu gösterir. Bedel 1, 3 ve 6'da en kısa
süreyi on altı işçi verir; bedel 12'de kazanan **tek işçiye döner**, çünkü 2604 sayısı 2250'nin
üstündedir. Ağın yerel işleme oranı bir kez değişince, on altı katlık bir kümenin gerekçesi ortadan
kalkar. **Boyutlandırma kararı ölçülen işin değil, ağ ile hesap arasındaki oranın kararıdır** ve o
oran yazılmadan sonuç taşınamaz.

İkinci tablo çarpıklığın payını ayırır. `abone` anahtarında çarpıklık on altı işçiye kadar 1,20'yi
geçmez ve süre 602'ye iner — `bolge` anahtarındaki 1398'in çok altına. Buna karşılık **ikinci
işçinin süreyi yükseltmesi burada da olur**: 2250'den 2800'e. Karıştırma iyi bir anahtarla bile
doğar ve ilk adımda kazancından büyüktür. Kursun ikinci iddiası çarpıklığın sonucu değildir;
çarpıklık onu yalnız derinleştirir.

#### Özet

- Aynı 2200 satırlık iş bir, iki, dört, sekiz ve on altı işçide süre **2250, 3691, 3179, 2610,
  1398** ve toplam iş **2200, 5830, 7414, 8074, 8635** verir; süre inerken toplam iş çıkar.
- İkinci işçi süreyi 2250'den **3691'e çıkarır**: karıştırma doğar, 1210 satır ağdan geçer ve toplam
  iş 5830'a sıçrar. Süre tek işçinin altına ancak on altı işçide iner.
- Üç ölçüt üç ayrı kurulum seçer: süre on altı işçiyi, toplam iş tek işçiyi, işçi ile sürenin çarpımı
  (2250'den **22368**'e) yine tek işçiyi. Hangi ölçütün ödendiği yazılmadan karar verilemez.
- `GOREV_SABIT = 50` işçi başına sayıldığında on altı işçinin süresi 2148 olur ve kazanç 852'den
  **102**'ye iner; `AG_BEDELI` 12'ye çıktığında en kısa süreyi veren kurulum **tek işçiye döner**.
- Çarpıklığı olmayan `abone` anahtarında süre 602'ye kadar iner, ama ikinci işçinin süreyi 2250'den
  **2800**'e çıkarması orada da olur; karıştırmanın ilk adımdaki bedeli çarpıklıktan bağımsızdır.

#### Kurs Kapanışı

Satırlar ortak birim taşımaz; her satırın **hangi ölçütle** okunduğu ders adının yanındadır.
Parantezdeki fark **dağıtık kurulum eksi tek işçilik tabandır**; işaretin yönü ölçüt adından okunur.

| Ders | Tek işçilik taban | Dağıtık kurulum | Bedel |
|---|---|---|---|
| `dagitik-hesaplama/01` — süre ve toplam iş | tek işçi süre **2250**, toplam iş **2200** | dört işçi `abone` süre **611** (**−1639**), toplam iş **2200** (**0**) | toplam iş dört kurulumda da 2200'dür ve onları birbirinden ayırmaz |
| `dagitik-hesaplama/02` — yerel okunan blok ve süre | tek kopya **5/22** yerel, süre **1850** | çoğaltma 3 ile **22/22** (**+17**), süre **650** (**−1200**) | saklanan blok 22'den **66**'ya çıkar, dördüncü kopya yerelliğe hiçbir şey katmaz |
| `dagitik-hesaplama/03` — taşınan satır ve toplam iş | eşleme taşınan **0**, toplam iş **2200** | doğrudan indirgeme taşınan **1738** (**+1738**), toplam iş **7414** (**+5214**) | ön-indirgeme aynı sonucu 20 ara satır ve **2245** toplam işle verir |
| `dagitik-hesaplama/04` — okunan satır ve açılan bölüm | istekli plan **6556** satır, **44** bölüm | tembel plan **200** satır (**−6356**), **1** bölüm (**−43**) | plandaki tür hatası ancak ilk eylemde ve 200 satır okunduktan sonra görünür |
| `dagitik-hesaplama/05` — süre ve ödenen sabit bedel | tek yuva süre **3000**, sabit bedel **800** | on altı yuva süre **215** (**−2785**), sabit bedel **800** (**0**) | her göreve bir yuva verilince sekizden on altıya kazanç **121**'de kalıp eklenen **400**'e yenilir |
| `dagitik-basarim/01` — taşınan satır ve süre | `abone` düzeninde taşınan **0**, süre **611** | `bolge`'ye karıştırınca taşınan **1738** (**+1738**), süre **1914** (**+1303**) | toplam iş 2200'den **7414**'e çıkar; 5214 birimi hiçbir hesap yapmayan saf harekettir |
| `dagitik-basarim/02` — birleştirme süresi, 4000 satırlık boyut | yayınlı **5876** | bölümlü **5429** (**−447**) | kesişme noktası **2980**'dir ve ağ bedeli 6 ile 12'de bölümlü hiçbir boyutta öne geçemez |
| `dagitik-basarim/03` — çarpıklık ve süre | tek işçi çarpıklık **1,00**, süre **2250** | `bolge` ile iki işçi çarpıklık **1,66** (**+0,66**), süre **3691** (**+1441**) | on altı işçide çarpıklık **6,88**, boş işçi **13**; süre **589**'un altına hiçbir sayıda inemez |
| `dagitik-basarim/04` — iki sorgunun toplamı | önbelleksiz **5500** | önbellekli **3300** (**−2200**) | önbellek tutulmazsa toplam yine **5500**'dür ve tek sorguda kazanç **0**'dır |
| `dagitik-basarim/05` — yeniden hesaplanan satır | denetim noktası yok **1600** | aralık 3 ile **400** (**−1200**) | kesin bedel 0'dan **800**'e çıkar, oysa aralık 6 da aynı **400**'ü verir |
| `dagitik-basarim/06` — süre ve toplam iş | tek işçi süre **2250**, toplam iş **2200** | on altı işçi süre **1398** (**−852**), toplam iş **8635** (**+6435**) | ikinci işçi süreyi **3691**'e çıkarır; sabit bedel işçi başına sayılınca kazanç **102**'ye iner |

Tablo tek bir kuralın on bir kez yazılmasıdır. **Dağıtık bir işin sayısı tek bir sayı değildir: süre
ile toplam iş ayrı ölçülür ve hangisinin ölçüldüğü yazılmayan bir hızlanma iddiası eksiktir.** İki
sütunun aynı satırda ters yöne gittiği yerler kuralın kendisidir: `dagitik-hesaplama/03`'te süre
düşerken toplam iş 2200'den 7414'e çıkar, bu derste süre 852 birim inerken toplam iş 6435 birim
tırmanır. `dagitik-hesaplama/01` bunun aynasını gösterir: dört kurulumun süresi 611 ile 2250
arasında değişirken toplam iş dördünde de 2200'dür, yani yalnız toplam işi yazan bir rapor bu
kurulumların hiçbirini ayırt edemez.

**İkinci iddia aynı tablodan okunur: işçi eklemek işi kötüleştirir.** `dagitik-basarim/03`'te ikinci
işçi süreyi 2250'den 3691'e çıkarır ve toplam işi 5830'a taşır; bu derste aynı adım çarpıklığı 1,00
olan `abone` anahtarında bile süreyi 2800'e çıkarır. `dagitik-hesaplama/05`'te sekizden on altı
göreve geçişte kazanılan 121 birim, eklenen 400 birimlik sabit bedele yenilir.
`dagitik-hesaplama/02`'de dördüncü kopya saklanan bloğu 88'e çıkarıp yerelliğe hiçbir şey katmaz.
`dagitik-basarim/04`'te tek sorgulu bir işte önbellek 2200 satır tutar ve 0 kazandırır,
`dagitik-basarim/05`'te aralığı altıdan üçe indirmek kesin bedeli 400'den 800'e çıkarıp kurtarmayı
hiç değiştirmez. Altı derste **eklenen kaynak ödenmiş, karşılığı alınmamıştır.**

**Üçüncü iddia daha sessizdir: sonuç her kurulumda aynıdır.** `dagitik-hesaplama/03`'te üç kurulumun
sunduğu beş bölge toplamı birebir aynıdır, oysa biri 7414 öbürü 2245 toplam iş harcar.
`dagitik-basarim/01`'de süre 611'den 1914'e çıkarken çıktı değişmez, `dagitik-basarim/02`'de iki
birleştirme stratejisi her boyutta aynı satırları eşleştirir, `dagitik-basarim/03`'te üç bölüşüm de
bölge toplamlarının aynısını üretir, `dagitik-basarim/04`'te tutulmayan önbellek sonucu değil yalnız
bedeli değiştirir, `dagitik-basarim/05`'te tam bir kez sağlandığı sürece dört aralık da aynı sonucu
verir. **Doğruluğu sınayan hiçbir denetim yanlış bölüşümü göstermez**; yanlış bölüşüm ancak
sayıldığında görünür ve sayılmadığı sürece fatura sessizce büyür.

On bir derste veri bölündü, taşındı, dengelendi, saklandı ve kurtarıldı; yirmi yedi derste ise
alındı, dönüştürüldü, depolandı ve sıraya kondu. Bu verinin gittiği yer hiç sorulmadı. Onu **besleyen
modelin** kendisi, aynı veriyle **aynı modelin yeniden üretilip üretilemeyeceği** ve eğitilen bir
modelin **üretime nasıl çıkacağı** hiçbir derste ölçülmedi. Sonraki kurs hattın çıktısını girdi
kabul eder ve bir eğitim koşumunun tekrarlanabilirliğini, sürümlenebilirliğini ve dağıtımını sayar.

