İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat