İçeriğe geç
academia.sh

Ders 01 / 11

Küme 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.

İçindekiler

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.

# 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.

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.

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.

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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat