Ders 08 / 10
Bellek Kullanımı
Aynı 80 adım altı biçimde tutulduğunda ayakta duran nesne sayısı 1 ile 172 arasında değişir; paylaşılan dizgi 11, ayrı dizgi 89, sözlüklü nesne 172, yuvalı nesne 91 nesne tutar ve altısı da çizelgeleyiciye aynı 31 tik / 49 örtüşen adımı verir.
İçindekiler
Önceki ders bir koşumun çağrı dökümünü açtı ve sıcak yolu çağrı sayısıyla buldu. Dökümün
söylemediği bir şey vardı: ilerlet her iki yükte de 80 kez çağrıldı, ama o 80 çağrının
üzerinde çalıştığı yapı bellekte kaç nesne olarak duruyordu?
Soru boşuna değil. Sekiz görevin on adımı, bir liste içinde liste olarak da tutulabilir, her adımı bir nesne yapan bir sınıfla da, tek bir bayt dizisiyle de. Üçü de aynı 80 adımı taşır ve çizelgeleyiciye aynı yanıtı verdirir. Ayakta tuttukları nesne sayısı ise aynı değildir — ve bu ders o sayıyı ölçer.
Neden Bayt Değil Nesne
Bir yapının bellekte kapladığı yeri bayt olarak sormak doğal görünür, ama o sayı bu kursun ölçüsü değildir. Nedeni ölçünün kendisindedir: bir nesnenin bayt karşılığı yorumlayıcının nesneyi nasıl yerleştirdiğine bağlıdır ve ortamdan ortama değişir. Değişen bir sayı iki biçimi karşılaştıramaz.
Nesne sayısı ise yapının kendisinden gelir. Bir listede kaç öğe olduğu, kaç öğenin aynı nesneyi gösterdiği, bir örneğin ayrıca bir sözlük taşıyıp taşımadığı — bunların hepsi gezilerek sayılabilir ve her koşumda aynı çıkar.
Sayımın bir sınırı olmak zorunda. Bir nesneden başlayıp erişilebilen her şeyi gezmek, tipine, tipin tanımlandığı modüle ve oradan da programın tamamına ulaşır; böyle bir sayı yapıyı değil ortamı ölçer. Bu yüzden gezme kapları izler — liste, demet, sözlük, örnek sözlüğü ve yuva — ve tip nesnelerinde, işlevlerde, modüllerde durur. Sayılan şey verinin tuttuğu yapıdır, o veriyi kullanan programın tamamı değil.
Sayının anlamı da vardır. Her nesnenin kendi kimliği, kendi tipi ve kendi yaşam süresi vardır; bunları taşımak nesne başına sabit bir yük getirir ve bu yük nesnenin içindeki veriyle orantılı değildir. On adımlık bir listeyi seksen ayrı nesneye dağıtmak, veriyi büyütmeden yükü seksen kez ödemek demektir. Bellek iddiası bu yüzden nesne sayısıyla kurulur.
Var Etmekle Ayakta Tutmak
Veri Yapıları ve Fonksiyonel Araçlar kursu var edilen nesneyi saydı: her üretimini kendi sayan bir sınıfla, bir biçimin koşum boyunca kaç nesne ürettiği ölçüldü ve tembel biçimin üretimi azaltmadığı, yalnız tutmayı azalttığı gösterildi. Bu ölçüm burada tekrarlanmaz, atıfla kullanılır.
Buradaki soru öbür yarısıdır: koşum bittikten sonra, elde tutulan yapının içinde aynı anda kaç nesne ayakta duruyor? Var etme bir akış ölçüsüdür, ayakta durma bir durum ölçüsü. İki biçim aynı sayıda nesne var edip bambaşka sayıda nesne tutabilir; iki biçim aynı sayıda nesne tutup bambaşka sayıda nesne var etmiş olabilir.
Altı Biçim
Ölçülen yapılar, ortak tanımın ürettiği sekiz görevden türetilir ve hepsi aynı 80 adımı taşır.
Liste, paylaşılan dizgi ortak tanımın kendi biçimidir: adımlar iki değişmez dizgiyi gösterir ve 80 yuvanın hepsi bu iki nesneden birine bağlanır. Liste, ayrı dizgi aynı yapıdır, tek farkı her adımın dizgisinin çalışma anında yeniden kurulmasıdır — değerler eşittir, nesneler ayrıdır. Sözlük, paylaşılan dizgi dış listeyi görev numarasıyla anahtarlanmış bir sözlüğe çevirir.
Nesne, sözlüklü her adımı bir sınıf örneğine sarar; örnek, niteliklerini bir sözlükte
tutar. Nesne, yuvalı aynı sınıfı __slots__ bildirimiyle kurar; nitelik adları önceden
sabitlendiği için örnek ayrıca sözlük taşımaz. Tek bayt dizisi 80 adımı tek bir
değişmez bayt dizisinde toplar; görev sınırları aritmetikle geri kazanılır.
Ölçümün varsayımları:
- BS7 — Altı biçim de ortak tanımın
gorevlerçıktısından türetilir ve aynı 80 adımı taşır; hiçbiri veriyi değiştirmez. - BS8 — Sayım yapıyı gezer ve ayrı nesneleri sayar: paylaşılan bir nesne kaç yerden gösterilirse gösterilsin bir sayılır. Ayırt etme kimlik üzerinden yapılır ama hiçbir kimlik sayısı basılmaz.
- BS9 — Gezme listeleri, demetleri, sözlükleri, örnek sözlüklerini ve yuvaları izler. Tip nesneleri, işlevler ve modüller sayılmaz; sayılan şey yapının kendisidir, koşumun tamamı değil.
- BS10 — Ölçülen şey ayakta duran nesnedir. Yapı kurulurken oluşan ara nesneler sayıma girmez; onlar Veri Yapıları kursunun ölçüsüdür.
- BS11 — Ölçülen bayt yazılmaz. Bir yapının bayt karşılığı ortama bağlıdır; nesne sayısı bağlı değildir ve iki biçimi karşılaştıran sayı budur.
- BS12 — Her biçim çizelgeleyiciye verilmeden önce ortak dizime çözülür ve çözülen dizinin ortak tanımınkiyle aynı olup olmadığı tabloda gösterilir; aynı değilse karşılaştırma geçersizdir.
Ölçüm
"""Bellek: ayni 80 adim alti bicimde; sayilan bayt degil, ayakta duran nesne.""" TOHUM = 20260817 ISLEMCI, GC = "islemci", "g_c" def uretec(tohum): d = tohum % 2147483646 + 1 def r(n): nonlocal d d = (d * 48271) % 2147483647 return d % n return r def gorevler(sayi=8, adim=10, gc_payi=7, tohum=TOHUM): r, liste = uretec(tohum), [] for i in range(sayi): liste.append([GC if r(10) < gc_payi else ISLEMCI for _ in range(adim)]) return liste def kosum(gorevler, isci, islemci_yuvasi): kalan = [list(g) for g in gorevler] tik = ortusen = islemci_adim = gc_adim = 0 while any(kalan): etkin = [i for i, g in enumerate(kalan) if g][:isci] if not etkin: break yuva, ilerleyen = islemci_yuvasi, 0 for i in etkin: adim = kalan[i][0] if adim == ISLEMCI: if yuva <= 0: continue yuva -= 1 islemci_adim += 1 else: gc_adim += 1 kalan[i].pop(0) ilerleyen += 1 tik += 1 ortusen += max(0, ilerleyen - 1) return tik, ortusen, islemci_adim, gc_adim def cocuklar(n): if isinstance(n, (list, tuple)): return list(n) if isinstance(n, dict): return list(n.keys()) + list(n.values()) if hasattr(n, "__dict__"): return [n.__dict__] if hasattr(type(n), "__slots__"): return [getattr(n, a) for a in type(n).__slots__] return [] def nesneleri_say(kok): """Yapiyi gezer; paylasilan nesne bir kez sayilir. Kimlik basilmaz.""" gorulen, tutulan, yigin, tur = set(), [], [kok], {} while yigin: n = yigin.pop() if id(n) in gorulen: continue gorulen.add(id(n)) tutulan.append(n) ad = type(n).__name__ tur[ad] = tur.get(ad, 0) + 1 yigin.extend(cocuklar(n)) return len(gorulen), tur def ayri_dizgi(s): return "".join([s[:2], s[2:]]) class Adim: def __init__(self, tur): self.tur = tur class DarAdim: __slots__ = ("tur",) def __init__(self, tur): self.tur = tur G = gorevler() BICIM = { "liste, paylaşılan dizgi": ([list(g) for g in G], lambda y: [list(g) for g in y]), "liste, ayrı dizgi": ([[ayri_dizgi(a) for a in g] for g in G], lambda y: [list(g) for g in y]), "sözlük, paylaşılan dizgi": ({i: list(g) for i, g in enumerate(G)}, lambda y: [y[i] for i in sorted(y)]), "nesne, sözlüklü": ([[Adim(a) for a in g] for g in G], lambda y: [[a.tur for a in g] for g in y]), "nesne, yuvalı": ([[DarAdim(a) for a in g] for g in G], lambda y: [[a.tur for a in g] for g in y]), "tek bayt dizisi": (bytes(1 if a == ISLEMCI else 0 for g in G for a in g), lambda y: [[ISLEMCI if b else GC for b in y[i * 10:i * 10 + 10]] for i in range(8)]), } print(f"{'biçim':<26s} {'nesne':>6s} {'çözülen dizi aynı':>18s} {'tik / örtüşen':>14s}") for ad, (yapi, coz) in BICIM.items(): sayi, _ = nesneleri_say(yapi) d = coz(yapi) tik, ortusen, _, _ = kosum(d, 8, 1) print(f"{ad:<26s} {sayi:6d} {str(d == G):>18s} {f'{tik} / {ortusen}':>14s}") print() for ad in ("liste, paylaşılan dizgi", "nesne, sözlüklü", "nesne, yuvalı"): print(f"{ad:<26s} {nesneleri_say(BICIM[ad][0])[1]}") print() p = BICIM["liste, paylaşılan dizgi"][0] a = BICIM["liste, ayrı dizgi"][0] print(f"paylaşılan biçimde ilk adım kurgunun nesnesiyle aynı -> {p[0][0] is G[0][0]}") print(f"ayrı biçimde aynı -> {a[0][0] is G[0][0]}, eşit -> {a[0][0] == G[0][0]}") b = BICIM["tek bayt dizisi"][0] print(f"bayt dizisinden iki kez alınan dilim aynı nesne -> {b[0:10] is b[0:10]}, " f"eşit -> {b[0:10] == b[0:10]}") print(f"toplam adım {sum(len(g) for g in G)}, en az nesneli biçim " f"{min(nesneleri_say(y)[0] for y, _ in BICIM.values())}, en çok " f"{max(nesneleri_say(y)[0] for y, _ in BICIM.values())}")
biçim nesne çözülen dizi aynı tik / örtüşen
liste, paylaşılan dizgi 11 True 31 / 49
liste, ayrı dizgi 89 True 31 / 49
sözlük, paylaşılan dizgi 19 True 31 / 49
nesne, sözlüklü 172 True 31 / 49
nesne, yuvalı 91 True 31 / 49
tek bayt dizisi 1 True 31 / 49
liste, paylaşılan dizgi {'list': 9, 'str': 2}
nesne, sözlüklü {'list': 9, 'Adim': 80, 'dict': 80, 'str': 3}
nesne, yuvalı {'list': 9, 'DarAdim': 80, 'str': 2}
paylaşılan biçimde ilk adım kurgunun nesnesiyle aynı -> True
ayrı biçimde aynı -> False, eşit -> True
bayt dizisinden iki kez alınan dilim aynı nesne -> False, eşit -> True
toplam adım 80, en az nesneli biçim 1, en çok 172
Sonuç Değişmedi
Tablonun sağ iki sütunu ölçümün geçerlilik koşuludur. Altı biçimin altısı da çözüldüğünde ortak tanımın dizisiyle aynı çıkıyor, ve altısı da çizelgeleyiciye aynı yanıtı verdiriyor: 31 tik / 49 örtüşen adım.
Bu, sol sütundaki farkın ne olduğunu tanımlar. Nesne sayısı 1 ile 172 arasında değişirken işin sonucu hiç değişmiyor. Ölçülen şey aynı sonucu tutmanın bedelidir, sonucun kendisi değil. Bu ayrım olmadan tablo bir karşılaştırma değil, altı ayrı ölçüm olurdu.
Sütunun tabloda durması bir biçimcilik değil. Bellek ölçümlerinde en sık yapılan yanlış,
biçimi değiştirirken veriyi de sessizce değiştirmektir — bir alanı düşürmek, bir dizgiyi
kısaltmak, bir görevi eksik kurmak. Böyle bir ölçümde nesne sayısı gerçekten düşer, ama
düşme nedeni biçim değil eksilen veridir. çözülen dizi aynı sütunu bunu her satırda
sınıyor; True olmayan bir satır tablodan çıkarılırdı.
Paylaşımın Ödediği
İlk iki satır tek bir farkla ayrılıyor. Yapı aynı — bir dış liste, sekiz iç liste, seksen
yuva. Değerler de aynı; alt bloktaki eşit -> True bunu söylüyor. Ayrı olan tek şey
nesnelerdir: paylaşılan biçimde 80 yuva iki dizgi nesnesini gösteriyor, ayrı biçimde
seksen dizgiyi.
Sayı 11’den 89’a çıkıyor: 78 nesne fazla, sıfır bilgi karşılığında. Alt bloktaki kimlik sınaması nedenini gösteriyor — ilk biçimde adım, kurgunun ürettiği nesnenin ta kendisi; ikinci biçimde eşit ama ayrı bir nesne.
Bu, gerçek programlarda en sık karşılaşılan bellek örüntüsüdür. Bir dosyadan ya da bir iletiden okunan alanlar birbirine eşit olsa bile ayrı nesnelerdir; okuma sırasında her biri yeniden kurulur. Aynı değerin tekrar ettiği bir sütunda paylaşımı yeniden kurmak — az sayıda değişmez değeri bir kez üretip her yerde onu göstermek — nesne sayısını doğrudan düşürür.
Nesne Başına Yükü Kim Ödüyor
Dördüncü ve beşinci satırlar aynı sınıfın iki kuruluşu. Her adım bir örnek; fark yalnız niteliklerin nerede tutulduğu.
Sözlüklü kuruluş 172, yuvalı kuruluş 91 nesne tutuyor. Aradaki 81’in nereden
geldiğini tip dökümü söylüyor: sözlüklü biçimde dict sayısı 80’dir — örnek başına bir
sözlük — ve nitelik adı olan bir dizgi de fazladan sayılıyor, bu yüzden str sayısı 2 değil
3. Yuvalı biçimde dict satırı hiç yok.
Nitelik adları önceden bildirildiğinde örneğin ayrıca bir sözlük taşımasına gerek kalmaz. Kazanç, örnek sayısıyla çarpılarak büyür: burada seksen örnek seksen sözlükten kurtuluyor. Bedeli esnekliktir — bildirilmemiş bir nitelik sonradan eklenemez.
Üçüncü satır aynı yükün başka bir yüzünü gösteriyor. Dış kabı listeden sözlüğe çevirmek nesne sayısını 11’den 19’a çıkarıyor. Fazladan sekiz nesne, sekiz anahtarın kendisidir: liste konumları nesne değildir, sözlük anahtarları nesnedir. Erişim aynı, sonuç aynı, sekiz nesne fazla.
Tek Nesnenin Bedeli
Son satır 80 adımı tek nesnede tutuyor. Bu, tablodaki en düşük sayı ve nedeni açık: bayt dizisinin içindeki 80 değer ayrı nesne değil, tek bir nesnenin içeriğidir. Yapı sınırları — hangi on adımın hangi göreve ait olduğu — aritmetikle geri kazanılıyor.
Bedeli alt bloktaki son kimlik sınamasında duruyor: aynı dilim iki kez alındığında ortaya iki ayrı nesne çıkıyor. Değişmez bir bayt dizisinden parça okumak, her okumada yeni bir nesne kurmak demektir. Yani bir nesnede duran yapı, okundukça nesne üretir; ayakta duran sayı düşerken var edilen sayı yükselir.
İki ölçünün ayrı olması burada işe yarıyor. Bir yapıyı seçerken sorulacak soru tek değildir: elde ne kadar tutuluyor, ve o tutulan şeye erişmek her seferinde ne üretiyor? Bayt dizisi ilkinde en iyi, ikincisinde en kötü seçenektir; paylaşılan dizgili liste ikisinde de ortanın üstündedir ve 11 nesneyle 80 adımı doğrudan erişilebilir tutar.
Altı satır bir sıralama değil, bir eksen veriyor. Bir uçta yapı bilgisinin tümü nesnelerde duruyor ve erişim bedava; öbür uçta yapı bilgisi koda taşınmış, nesne bir taneye inmiş ve erişimin bedeli her okumaya dağılmış. Ölçüm hangi ucun doğru olduğunu söylemez — hangi ucun neye mal olduğunu söyler. Kararı veren şey, aynı yapıya kaç kez bakılacağıdır.
Özet
- Bu kurs bellek yükünü ölçülen baytla değil ayakta duran nesne sayısıyla ölçer; bayt karşılığı ortama bağlıdır, nesne sayısı yapının kendisinden gelir ve her koşumda aynı çıkar.
- Aynı 80 adım altı biçimde 1 ile 172 nesne arasında tutulabiliyor, ve altısı da çizelgeleyiciye aynı 31 tik / 49 örtüşen adım sonucunu verdiriyor.
- Paylaşımı kaybetmek yapıyı değiştirmeden 11 nesneyi 89’a çıkarır: eşit ama ayrı seksen dizgi, sıfır bilgi karşılığında 78 fazladan nesnedir.
- Örnek başına sözlük tutmak 172, nitelik adlarını önceden bildirmek 91 nesne demektir; aradaki 81, seksen örnek sözlüğü ile bir nitelik adıdır.
- Dış kabı listeden sözlüğe çevirmek sekiz anahtar nesnesi ekler; liste konumu nesne değildir, sözlük anahtarı nesnedir.
- Tek nesnede tutulan yapı ayakta duranı 1’e indirir ama her okumada yeni nesne üretir; tutma ölçüsüyle üretme ölçüsü ayrı yönlere çekebilir.
Sonraki Adım
Buraya kadar iki sayı ölçüldü: bir işlevin kaç kez çağrıldığı ve bir yapının kaç nesne
tuttuğu. İkisi de aynı yöne işaret ediyor — sıcak yol, islemci_adimi_mi çağrısının 322
kez döndüğü yerdir. Peki o yol daha aza indirilebilir mi? Bir sıcak yolu dilin dışına, daha
alt bir seviyeye taşımak sık başvurulan bir yoldur ve kazancı çoğu kez süreyle anlatılır.
Sonraki ders bunu süreyle değil modelde adım olarak ölçer: sıcak yolun adım sayısı
düştüğünde toplam tik ne oluyor, ve sınırdan geçmenin kendisi ne kadarını geri alıyor?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.