Ders 03 / 11
Eşle-İndirge 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.
İçindekiler
Ö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 bir (anahtar, değer) çifti üretir; 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_BEDELIyani 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
aboneanahtarı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.
# 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.
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.
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 dörtte üçü 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.
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.
abonedüzenindenbolgeanahtarı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.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.