Ders 14 / 22
Bellek Kullanımı
Kurgu ölçüm ağının 3.199 satırlık kümesinde tek bir sütunun bellekte kaç bayt tuttuğu, o sütunun hangi tiple tutulduğuna bağlıdır: bölge sütunu nesne listesi olarak 173.837 bayt, sabit genişlikli metin olarak 19.251 bayt, kategorik kodla 3.628 bayt tutar ve kazanç 47,9 kattır. Aynı sütunun iki savunulabilir ölçümü arasında 6,6 kat fark vardır, çünkü ölçüm işlevi göstericiyi sayar ve işaret ettiği nesneyi izlemez. Okuma sütunu 8 bayttan 1 bayta indirildiğinde bellek 31,4 kat küçülür ama toplam 6,9 m³ kayar ve 108 satır tarife basamağı değiştirir; kategorik tip 5 farklı değerli sütunda 5,3 kat kazandırırken 1.329 farklı değerli sütunda 4,1 kat kaybettirir.
İçindekiler
Önceki konu sekiz derste veri çerçevesi soyutlamasını kurdu ve her çağrının varsayılanını yazdı. O konunun kapanışı tek bir koşula dikkat çekmişti: çerçevenin rahatlığı, küme belleğe sığdığı sürece geçerlidir. Bu konu o koşulun kalktığı yerden başlar ve ilk sorusu en somut olanıdır — bir sütun bellekte kaç bayt tutar.
Cevap değerlerin sayısına değil, o değerlerin hangi tiple tutulduğuna bağlıdır. Tip kararı da çoğu zaman kimse vermeden verilir: kümeyi okuyan çağrı bir varsayılan seçer ve sütun o varsayılanla belleğe yerleşir. Dersin ölçü birimi ayrılan bayttır. Yanında duran ikinci sayı, o baytı kazanmak için sessizce alınan karardır: hangi değerler artık ayırt edilemiyor ve hangi sayı bunun için kaç birim kaydı.
- AB1. Küme kurgudur ve M26/K03’ün hazırladığı uzun biçimli okuma tablosunu verir: 3.199 satır, 1.329 abone, üç dönem. Gerçek bir küme yerine aynı yapıyı üreten bir üreteç yazıldı; tohum görünürdür ve çıktı koşumdan koşuma aynıdır. Modeldir.
- AB2. Ölçüm
sys.getsizeofile yapılır. Bu işlev bir nesnenin kendi gövdesini verir, işaret ettiği nesneleri izlemez; toplam ölçüm bu yüzden elle kurulur. - AB3. Rakamlar sekiz baytlık göstericisi olan bir çalışma ortamından okundu ve ortama bağlıdır. Değişmeyen şey oranlardır; gösterge genişliği değişse de gösterimler arası kat aynı kalır.
- AB4. Okumalar bir ondalıkla raporlanır. İki toplamın aynı sayılması için eşik 0,05 m³’tür, çünkü bundan küçük bir fark yalnızca yuvarlamadan gelebilir.
- AB5. Tarife basamakları 10 ve 25 m³ sınırlarıyla üç bant kurar ve üretecin verebileceği en büyük okuma 37,0 m³’tür; tamsayı gösterimlerinin genişliği bu üst sınıra göre seçilir.
Kümenin Sütunları
# kaynak.py — bolgesel olcum agindan gelen kurgu uzun bicimli okuma tablosu. # MODELDIR: gercek bir kume yerine ayni yapiyi ureten bir uretec yazildi; # tohum gorunur ve cikti kosumdan kosuma aynidir. M26/K03'un hazirladigi kume: # 3199 satir, 1329 abone, uc donem, tarife siniri 10 ve 25 m3. from collections import Counter TOHUM = 1246 BOLGE = ("kuzey", "dogu", "merkez", "guney", "bati") DONEM = (("2024-01", 1329), ("2024-02", 1035), ("2024-03", 835)) YOGUNLUK = (0, 1, 2, 2, 2, 3, 4, 2, 2, 1) # merkez agirlikli kurgu olcum agi def uretec(tohum: int): s = tohum & 0xFFFFFFFF def sonraki() -> float: nonlocal s s = (1103515245 * s + 12345) & 0xFFFFFFFF return s / 4294967296 return sonraki def basamak(okuma: float) -> str: return "0-10" if okuma <= 10 else "10-25" if okuma <= 25 else "25-40" def kayitlar() -> list[tuple]: r, k = uretec(TOHUM), [] for donem, adet in DONEM: for i in range(adet): o = round(5 + r() * 32.0, 1) k.append((10000 + i, f"S-{4000 + i}", donem, BOLGE[YOGUNLUK[i % 10]], o, basamak(o))) return k k0 = kayitlar() say = lambda j: ", ".join(f"{a} {n}" for a, n in Counter(x[j] for x in k0).most_common()) print(f"tohum {TOHUM}; satir {len(k0)}, farkli abone {len({x[0] for x in k0})}, " f"donem {len(DONEM)}") print("bolge :", say(3)) print("tarife:", say(5)) print(f"toplam {sum(x[4] for x in k0):.1f} m3, en kucuk {min(x[4] for x in k0)}, " f"en buyuk {max(x[4] for x in k0)}")
tohum 1246; satir 3199, farkli abone 1329, donem 3 bolge : merkez 1601, dogu 639, kuzey 321, guney 319, bati 319 tarife: 10-25 1504, 25-40 1187, 0-10 508 toplam 66667.9 m3, en kucuk 5.0, en buyuk 37.0
Küme altı sütun taşıyor ve iki tanesi bu dersin bütün ölçümlerini üretiyor. Bölge sütunu 3.199 değer taşır ama yalnız beş farklı değer içerir; merkez tek başına satırların yarısını tutar. Okuma sütunu 3.199 sayı taşır, hepsi 5,0 ile 37,0 arasındadır ve her biri bir ondalıklıdır. İkisi de aynı satır sayısına sahip, ama bellek karşılığı bakımından birbirine hiç benzemez.
Aynı Sütun Üç Tiple
# bellek.py — ayni bolge sutunu uc tiple tutulur ve ayrilan bayt sayilir. # MODELDIR: sutun once dosyadan okunmus gibi elde edilir, cunku ayristirici # her satir icin yeni bir dizgi nesnesi uretir ve sabit bir listeden gelen # dizgiler gibi paylasilmaz. import csv, io, sys from array import array tampon = io.StringIO() csv.writer(tampon, lineterminator="\n").writerows(k0) satirlar = list(csv.reader(io.StringIO(tampon.getvalue()))) bolge_sut = [s[3] for s in satirlar] print(f"sutunda {len(bolge_sut)} deger, {len({id(s) for s in bolge_sut})} ayri nesne, " f"{len(set(bolge_sut))} farkli deger") kap = sys.getsizeof(bolge_sut) # yalnizca gostericiler nesne = sum(sys.getsizeof(s) for s in {id(s): s for s in bolge_sut}.values()) paylasimli = sum(sys.getsizeof(s) for s in set(bolge_sut)) en_uzun = max(len(a) for a in set(bolge_sut)) # sabit genislikli metin sabit = bytearray(len(bolge_sut) * en_uzun) for j, a in enumerate(bolge_sut): sabit[j * en_uzun:(j + 1) * en_uzun] = a.encode("ascii").ljust(en_uzun) sozluk = sorted(set(bolge_sut)) # kategorik tip kod = array("B", [sozluk.index(a) for a in bolge_sut]) kat_sozluk = sys.getsizeof(sozluk) + sum(sys.getsizeof(s) for s in sozluk) y = lambda h: "".join(str(v).rjust(12) if j else str(v).ljust(26) for j, v in enumerate(h)) print(y(["bolge sutunu tipi", "kap bayt", "nesne bayt", "toplam", "satir/bayt"])) for ad, kb, nb in [("N1 nesne listesi", kap, nesne), ("N1 paylasilmis sayilirsa", kap, paylasimli), ("N2 sabit genislikli metin", sys.getsizeof(sabit), 0), ("N3 kategorik kod", sys.getsizeof(kod), kat_sozluk)]: print(y([ad, kb, nb, kb + nb, round((kb + nb) / len(bolge_sut), 1)])) print(f"N1 -> N3 kazanci {round((kap + nesne) / (sys.getsizeof(kod) + kat_sozluk), 1)} kat; " f"ayni sutunun iki olcumu arasindaki oran " f"{round((kap + nesne) / (kap + paylasimli), 1)} kat")
sutunda 3199 deger, 3199 ayri nesne, 5 farkli deger bolge sutunu tipi kap bayt nesne bayt toplam satir/bayt N1 nesne listesi 26040 147797 173837 54.3 N1 paylasilmis sayilirsa 26040 229 26269 8.2 N2 sabit genislikli metin 19251 0 19251 6.0 N3 kategorik kod 3279 349 3628 1.1 N1 -> N3 kazanci 47.9 kat; ayni sutunun iki olcumu arasindaki oran 6.6 kat
Kazanç tablonun iki ucu arasındadır: aynı beş değerli sütun nesne listesi olarak 173.837 bayt, kategorik kodla 3.628 bayt tutar — 47,9 kat. Satır başına 54,3 bayttan 1,1 bayta inmenin sebebi tek bir gözlemdir: sütunda 3.199 değer var ama yalnız beş farklı değer. Kategorik tip bu farkı yapıya çevirir; sütun bir bayt kod dizisi olur, beş dizgi bir kez sözlükte durur.
İkinci sayı tablonun ortasında duruyor ve ölçümün kendisiyle ilgilidir. İlk satır ile ikinci satır aynı sütunu ölçüyor ve arada 6,6 kat var. Ayrım şudur: dosyadan okunan sütunda 3.199 değerin her biri ayrı bir nesnedir, çünkü ayrıştırıcı her satır için yeni bir dizgi üretir. Aynı sütun sabit bir listeden kurulsaydı beş nesne paylaşılırdı ve nesne payı 147.797 bayt yerine 229 bayt olurdu. Ölçüm işlevi göstericiyi sayar, işaret ettiği nesneyi izlemez; hangi sayının doğru olduğu sütunun nereden geldiğine bağlıdır ve ikisi de savunulabilir. Bellek raporunda hangi kuralın kullanıldığı yazılmıyorsa o rapor bir seçimdir.
Sabit genişlikli metin arada durur ve kendi kararını taşır: her satır en uzun bölge adı kadar yer kaplar. Altı bayt en uzun ada göre seçildi; sütuna yedi harfli bir bölge adı girerse ya sütun yeniden kurulur ya da ad kırpılır. Kategorik tip bu kararı sözlüğe taşır ve genişlik sorununu ortadan kaldırır.
Genişlik Seçiminin Oynattığı Sayı
# genislik.py — okuma sutunu dort gosterimle tutulur; her gosterim icin # ayrilan bayt, toplam ve tarife basamagi degisen satir yan yana basilir. ESIK = 0.05 # okumalar bir ondalikla raporlanir, altindaki fark yuvarlamadandir okuma_sut = [float(s[4]) for s in satirlar] d64 = array("d", okuma_sut) # 8 baytlik kayan nokta u16 = array("H", [round(o * 10) for o in okuma_sut]) # 2 baytlik desi-m3 u8 = array("B", [round(o) for o in okuma_sut]) # 1 baytlik tam m3 nesneli = sys.getsizeof(okuma_sut) + sum(sys.getsizeof(o) for o in okuma_sut) dogru = round(sum(okuma_sut), 1) G = (22, 13, 12, 12, 17) y2 = lambda h: "".join(str(v).rjust(G[j]) if j else str(v).ljust(G[0]) for j, v in enumerate(h)) print(y2(["okuma sutunu tipi", "toplam bayt", "satir/bayt", "toplam m3", "basamak degisen"])) for ad, bayt, dizi in [("nesne listesi", nesneli, okuma_sut), ("8 bayt kayan nokta", sys.getsizeof(d64), list(d64)), ("2 bayt desi-m3", sys.getsizeof(u16), [v / 10 for v in u16]), ("1 bayt tam m3", sys.getsizeof(u8), [float(v) for v in u8])]: kayan = sum(1 for a, b in zip(okuma_sut, dizi) if basamak(a) != basamak(b)) print(y2([ad, bayt, round(bayt / len(okuma_sut), 1), round(sum(dizi), 1), kayan])) sapma = abs(sum(u8) - dogru) print(f"1 bayt secimi bellegi {round(nesneli / sys.getsizeof(u8), 1)} kat kucultur; " f"toplam {sapma:.1f} m3 kayar, esigin {round(sapma / ESIK)} kati") # Ayni donusum sayac_no sutununda 1329 farkli deger bulur: kod iki bayta cikar # ve sozluk sutunun kendisinden buyuk olur. sayac_sut = [s[1] for s in satirlar] s_sozluk = sorted(set(sayac_sut)) s_kod = array("H", [s_sozluk.index(a) for a in sayac_sut]) s_kategorik = sys.getsizeof(s_kod) + sys.getsizeof(s_sozluk) + sum( sys.getsizeof(a) for a in s_sozluk) s_sabit = sys.getsizeof(bytearray(len(sayac_sut) * 6)) b_sabit, b_kategorik = sys.getsizeof(sabit), sys.getsizeof(kod) + kat_sozluk print(f"bolge : {len(sozluk):5d} farkli deger, sabit genislikli {b_sabit} bayt, " f"kategorik {b_kategorik} bayt -> {round(b_sabit / b_kategorik, 1)} kat kazanc") print(f"sayac_no: {len(s_sozluk):5d} farkli deger, sabit genislikli {s_sabit} bayt, " f"kategorik {s_kategorik} bayt -> {round(s_kategorik / s_sabit, 1)} kat kayip") # Butun kume iki duzende: satir kayitlari ve sutun basina secilmis dar tipler. satir_duzeni = sys.getsizeof(satirlar) + sum( sys.getsizeof(s) + sum(sys.getsizeof(a) for a in s) for s in satirlar) d_sozluk, t_sozluk = sorted({s[2] for s in satirlar}), sorted({s[5] for s in satirlar}) sutunlar = (array("I", [int(s[0]) for s in satirlar]), s_kod, array("B", [d_sozluk.index(s[2]) for s in satirlar]), kod, u16, array("B", [t_sozluk.index(s[5]) for s in satirlar])) sutun_duzeni = sum(sys.getsizeof(v) for v in sutunlar) + sum( sys.getsizeof(sz) + sum(sys.getsizeof(a) for a in sz) for sz in (s_sozluk, d_sozluk, sozluk, t_sozluk)) print(f"satir duzeni {satir_duzeni} bayt, sutun duzeni {sutun_duzeni} bayt -> " f"{round(satir_duzeni / sutun_duzeni, 1)} kat; satir basina " f"{round(satir_duzeni / len(satirlar))} bayt yerine " f"{round(sutun_duzeni / len(satirlar))} bayt")
okuma sutunu tipi toplam bayt satir/bayt toplam m3 basamak degisen nesne listesi 102816 32.1 66667.9 0 8 bayt kayan nokta 25672 8.0 66667.9 0 2 bayt desi-m3 6478 2.0 66667.9 0 1 bayt tam m3 3279 1.0 66661.0 108 1 bayt secimi bellegi 31.4 kat kucultur; toplam 6.9 m3 kayar, esigin 138 kati bolge : 5 farkli deger, sabit genislikli 19251 bayt, kategorik 3628 bayt -> 5.3 kat kazanc sayac_no: 1329 farkli deger, sabit genislikli 19251 bayt, kategorik 79653 bayt -> 4.1 kat kayip satir duzeni 1298875 bayt, sutun duzeni 109714 bayt -> 11.8 kat; satir basina 406 bayt yerine 34 bayt
İlk üç satır bedelsizdir. Nesne listesinden sekiz baytlık sayı dizisine geçmek belleği 4 kat, iki baytlık desi-m³ gösterimine geçmek 15,9 kat küçültür ve toplam değişmez: okumalar bir ondalıkla raporlanıyor, bir ondalık on ile çarpılınca tam sayı oluyor ve iki bayt 370 değerini rahatça taşıyor. Buraya kadar soyutlamanın kazancı gerçekten bedava.
Dördüncü satır bedeli gösterir. Bir baytlık tam m³ gösterimi belleği nesne listesine göre 31,4 kat küçültür, toplamı yalnız 6,9 m³ oynatır ve bu fark toplamın on binde biri kadardır — rapora bakılırsa zararsız görünür. Ama son sütun başka bir sayı verir: 108 satır tarife basamağı değiştirir. Yuvarlama toplamda birbirini götürür, sınırda götürmez; 10,4 m³ okuyan bir sayaç tam sayıya yuvarlandığında ilk basamağa düşer ve o abonenin faturası değişir. Toplam 6,9 m³’lük sapmayı eşikle karşılaştırmak da aynı yere çıkar: fark eşiğin 138 katıdır, yani yuvarlamadan gelmiş sayılamaz. Gizlenen karar budur: genişlik daraltmak bir bellek kararı gibi görünür, oysa hangi satırın hangi banda düştüğünü belirleyen bir ölçüm kararıdır.
Son iki satır kategorik tipin sınırını çizer. Aynı dönüşüm bölge sütununda sabit genişlikli metne göre 5,3 kat kazandırır, sayaç numarası sütununda 4,1 kat kaybettirir. Sebep tek bir sayıdır: 1.329 farklı değer bir bayta sığmaz, kod iki bayta çıkar ve sözlük sütunun kendisinden büyük olur. “Metin sütununu kategorik tipe geçir” bir kural değil, farklı değer sayısına bağlı bir karardır ve o sayı yazılmadan verilemez.
Son satır kararların toplamını verir. Aynı 3.199 kayıt satır kayıtları olarak 1.298.875 bayt, sütun başına seçilmiş dar tiplerle 109.714 bayt tutar: satır başına 406 bayt yerine 34 bayt, 11,8 kat. Bu sayı altı ayrı tip kararının bileşimidir ve hiçbiri kendiliğinden gelmez; her sütun için hangi genişliğin ve hangi gösterimin seçildiği ayrı ayrı yazıldı. Kümeyi okuyan bir çağrı bu kararların hepsini varsayılanla verir ve sonucu tek bir sayı olarak bildirmez.
Özet
- Bölge sütunu nesne listesi olarak 173.837 bayt, sabit genişlikli metin olarak 19.251 bayt, kategorik kodla 3.628 bayt tutar; kazanç 47,9 kat, satır başına 54,3 bayttan 1,1 bayta iniştir.
- Aynı sütunun iki savunulabilir ölçümü arasında 6,6 kat fark vardır, çünkü ölçüm işlevi göstericiyi sayar; dosyadan okunan sütunda 3.199 ayrı nesne, sabit listeden kurulan sütunda beş nesne bulunur.
- Okuma sütununda sekiz bayttan iki bayta inmek toplamı değiştirmez; bir bayta inmek belleği 31,4 kat küçültür ama toplamı 6,9 m³ oynatır ve bu fark eşiğin 138 katıdır.
- Aynı yuvarlama 108 satırın tarife basamağını değiştirir; toplamda görünmeyen bedel sınırda ortaya çıkar.
- Kategorik tip 5 farklı değerli sütunda 5,3 kat kazandırır, 1.329 farklı değerli sütunda 4,1 kat kaybettirir; kazanç satır sayısına değil farklı değer sayısına bağlıdır.
Sonraki Adım
Bu ders bir sütunun bellek karşılığını daralttı ve dört gösterim arasında 31 kata varan bir fark buldu. Ama daraltmanın bir tabanı vardır: 3.199 satır her tiple belleğe sığar, 320 milyon satır hiçbir tiple sığmaz. O noktada soru tip seçmek değil, kümenin tamamını aynı anda bellekte tutmaktan vazgeçmektir. Sonraki ders kümeyi parçalara bölerek okur ve parça boyutunun bellek tepesiyle geçiş sayısı arasında kurduğu dengeyi ölçer; hangi işlemlerin parça parça hesaplanabildiğini, hangilerinin hesaplanamadığını sayar ve parçalamanın sessizce ürettiği yanlış ortalamayı gösterir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.