Ders 02 / 22
Diziler
Kursun ölçü birimi kuruluyor: aynı iş (abone başına toplam, genel ortalama, 40 m³ üstü hücre sayısı) iç içe listelerle ve bir dizi arayüzüyle yapılır ve satır sayısı 22'den 2'ye, ayrılan bayt 188.864'ten 30.320'ye (öğe başına 49,96'dan 8,02 bayta) düşerken geçiş sayısı 3'ten 3'e, yani hiç düşmez. Gizlenen karar dizinin boş değeri olmamasıdır: 581 boş hücre sıfırla doldurulduğunda ortalama 26,993180, nöbetçi değerle 26,839476, yalnız dolu hücrelerle 31,895661 m³ çıkar; 40 m³ üstü sayım üç kararda da 1.116'dır. Öğe tipi seçimi 3.780 öğeyi 7.560 ile 30.240 bayt arasında tutar.
İçindekiler
Önceki derste ortam sabitlendi ve defter iki makinede aynı sayıyı verir hale geldi. Hesabın kendisi ise değişmedi: 3.780 hücre iç içe döngülerle tek tek gezildi, her hücre kendi başına bir nesne olarak tutuldu ve her soru için veri baştan sona yeniden okundu. Bu ders aynı işi bir dizi arayüzüyle yapar.
Ders yalnız bir kolaylık göstermez, kursun ölçü birimini kurar. Bundan sonraki her derste iki sayı yan yana duracak: soyutlamanın kazandırdığı ve soyutlamanın gizlediği. Kazanç burada üç birimle ölçülür — kaç satır kod, veri üzerinden kaç tam geçiş, kaç bayt. Gizlenen karar ise dizinin sana sormadan verdiği karardır. Kütüphane çağrılmaz; dizi davranışı standart kitaplıkla modellenir, çünkü amaç bir aracı taklit etmek değil davranışını ölçülebilir kılmaktır.
- SH3. Bir dizi arayüzü üç şeyi birlikte sabitler: bitişik bellek, tek öğe tipi ve biçim. Kazanç bu üçlünün sonucudur ve baytta ile çağrı yerindeki satır sayısında görünür; veri üzerindeki geçiş sayısında görünmez.
- SH4. Dizinin boş değeri yoktur. Eksik bir hücre bir sayıya dönüşmek zorundadır ve hangi sayıya dönüştüğü bir karardır. Bu karar bazı özetleri oynatır, bazılarını hiç oynatmaz.
Aynı İş, İki Yapı
# diziler.py — MODELDIR: dizi arayuzunun uc ozelligi standart kitaplikla yaziliyor: # bitisik bellek, tek oge tipi, bicim. Kutuphane cagrilmiyor; amac araci taklit # etmek degil, davranisini olculebilir kilmak. Kume K03'un hazirladigi kurgu kumedir. import inspect import sys from array import array def uretec(tohum): s = tohum & 0xFFFFFFFF def sonraki(): nonlocal s s = (1664525 * s + 1013904223) & 0xFFFFFFFF return s / 4294967296 return sonraki ABONE, DONEM = 1260, 3 r = uretec(20260218) HAM = [round(4 + r() * 56, 2) for _ in range(ABONE * DONEM)] r2 = uretec(9110723) p = [r2() for _ in range(ABONE * DONEM)] BOS = set(sorted(range(len(p)), key=lambda j: p[j])[:581]) OKUMA = [None if j in BOS else HAM[j] for j in range(ABONE * DONEM)] TABLO = [OKUMA[i*DONEM:(i+1)*DONEM] for i in range(ABONE)] GECIS = 0 # veri uzerinden bastan sona bir tam tarama def elle(t): global GECIS abone_toplami = [] for s in t: a = 0.0 for x in s: if x is not None: a += x abone_toplami.append(a) genel, n = 0.0, 0 for s in t: for x in s: if x is not None: genel += x n += 1 ust = 0 for s in t: for x in s: if x is not None and x > 40.0: ust += 1 GECIS += 3 return abone_toplami, genel / n, ust class Dizi: def __init__(self, veri, bicim, tip="d"): self.d, self.bicim, self.tip = array(tip, veri), bicim, tip @property def oge(self): return self.bicim[0] * self.bicim[1] def eksen_toplami(self, eksen): global GECIS GECIS += 1 a, b = self.bicim if eksen == 1: return [sum(self.d[i*b:(i+1)*b]) for i in range(a)] return [sum(self.d[i*b+j] for i in range(a)) for j in range(b)] def toplam(self): global GECIS GECIS += 1 return sum(self.d) def ustunde(self, esik): global GECIS GECIS += 1 return sum(1 for x in self.d if x > esik) def dizi_ile(a): return a.eksen_toplami(1), a.toplam() / a.oge, a.ustunde(40.0) def liste_bayt(t): b = sys.getsizeof(t) for s in t: b += sys.getsizeof(s) + sum(sys.getsizeof(x) for x in s if x is not None) return b DUZ = Dizi([0.0 if x is None else x for x in OKUMA], (ABONE, DONEM)) GECIS = 0 E = elle(TABLO) ge = GECIS GECIS = 0 D = dizi_ile(DUZ) gd = GECIS satir = lambda f: len(inspect.getsource(f).strip().splitlines()) bl, bd = liste_bayt(TABLO), sys.getsizeof(DUZ.d) gen = lambda w: lambda h: "".join(str(v).ljust(w[0]) if j == 0 else str(v).rjust(w[j]) for j, v in enumerate(h)) y = gen([20, 20, 16, 10]) print(f"bicim {DUZ.bicim}, oge sayisi {DUZ.oge}, oge tipi 64 bit kesirli, " f"bos hucre {len(BOS)}") print("is: abone basina toplam, genel ortalama, 40 m3 ustu hucre sayisi\n") print(y(["olcu", "elle yazilan dongu", "dizi arayuzu", "oran"])) print(y(["satir sayisi", satir(elle), satir(dizi_ile), f"{satir(elle)/satir(dizi_ile):.1f}x"])) print(y(["gecis sayisi", ge, gd, f"{ge/gd:.1f}x"])) print(y(["ayrilan bayt", bl, bd, f"{bl/bd:.1f}x"])) print(y(["oge basina bayt", f"{bl/DUZ.oge:.2f}", f"{bd/DUZ.oge:.2f}", f"{bl/bd:.1f}x"])) print(f"\nayni is, iki sonuc: ortalama elle {E[1]:.6f} m3, dizi arayuzuyle {D[1]:.6f} m3; " f"40 m3 ustu hucre iki yolda da {E[2]}")
bicim (1260, 3), oge sayisi 3780, oge tipi 64 bit kesirli, bos hucre 581 is: abone basina toplam, genel ortalama, 40 m3 ustu hucre sayisi olcu elle yazilan dongu dizi arayuzu oran satir sayisi 22 2 11.0x gecis sayisi 3 3 1.0x ayrilan bayt 188864 30320 6.2x oge basina bayt 49.96 8.02 6.2x ayni is, iki sonuc: ortalama elle 31.895661 m3, dizi arayuzuyle 26.993180 m3; 40 m3 ustu hucre iki yolda da 1116
Üç ölçünün tanımı kurs boyunca sabit kalır. Satır sayısı çağrı yerinde yazılan kod satırıdır ve koddan okunur, elle sayılmaz. Geçiş sayısı veri üzerinden baştan sona yapılan tam tarama sayısıdır; modelde her tarama bir sayacı bir artırır, dolayısıyla iddia edilen değil ölçülen bir sayıdır. Ayrılan bayt veriyi tutan yapının kendisinin ve içindeki nesnelerin kapladığı yerdir. Üçü de koşumdan koşuma değişmez; süre değişir, bu yüzden süre ölçülmez. Bir soyutlamanın kazancı bu üç sayıdan hangilerini düşürdüğüyle, bedeli ise hangilerini düşürmediğiyle tanımlanır.
Tablonun ilk satırı beklenen kazançtır. Aynı üç soru elle yazıldığında 22 satır tutuyor, dizi arayüzünde 2 satır. Fark bir yazım kısalığı değil: elle yazılan sürümde eksik hücre denetimi üç kez tekrarlanıyor, döngü sınırları üç kez yazılıyor ve her tekrarda bunlardan birini atlama ihtimali var. Soyutlama bu üç tekrarı tek bir yere taşıyor.
Üçüncü satır ikinci kazançtır ve nedeni biçim ile öğe tipi kararının bir kez verilmiş olmasıdır. İç içe listelerde 3.780 sayının her biri kendi başına bir nesnedir; nesnenin kendisi yaklaşık 24 bayt, ona işaret eden gösterici 8 bayt, satırı tutan 1.260 küçük listenin her biri ayrıca yer kaplar. Toplam 188.864 bayt, öğe başına 49,96. Dizide 3.780 sayı bitişik bir bellek bloğunda, öğe başına tam 8 bayt duruyor ve biçim yalnız iki tamsayıdan ibaret: 30.320 bayt, öğe başına 8,02. Oran 6,2 kat.
İkinci satır ise kazanç değil. Geçiş sayısı iki yolda da 3, çünkü sorulan soru üç tane ve her soru veriyi baştan sona bir kez okuyor. Dizi arayüzü döngüyü çağrı yerinden kaldırır, veriden kaldırmaz. Bu kursun en sık yanlış okunan sayısıdır: iki satır kod yazıldığı için tek geçiş yapıldığı sanılır. Zincirlenen her çağrı kendi geçişini ekler ve bir sonraki dersin ölçtüğü şey tam olarak budur. Elle yazılan sürümde bu üç geçişi tek geçişe indirmek üç döngüyü tek döngüde birleştirmek demektir ve bunun yazılması iki satır tutar; dizi arayüzünde aynı birleştirmeyi yapmak için çağrı zincirinden çıkıp döngüye geri dönmek gerekir. Kısalık ile geçiş sayısı burada birbirinin karşısındadır.
Son satır ise gizlenen kararı işaret ediyor. Aynı iş, aynı veri, iki farklı ortalama: 31,895661 ile 26,993180 m³. Aradaki 4,90 m³ bir hesap hatası değil, dizinin yapısından doğan bir karardır.
Biçim ve Eksen
Modeldeki Dizi sınıfı iki alan taşıyor ve ikisinin ayrı durması dizi arayüzünün en belirleyici
özelliğidir. d alanı 3.780 sayıyı tek bir düz blokta tutar; bicim alanı yalnız iki tamsayıdan
ibarettir ve o düz bloğun nasıl okunacağını söyler. Veri iki boyutlu değildir — okunuşu iki
boyutludur.
Bu ayrımın doğrudan sonucu, biçim değiştirmenin veriyi hiç kopyalamamasıdır. (1260, 3) yerine
(3, 1260) yazmak 30.240 baytın hiçbirini kımıldatmaz, yalnız eksen_toplami işlevindeki dilim
sınırlarını değiştirir. Bu ucuzluk soyutlamanın en kullanışlı yanıdır ve aynı zamanda en sessiz
yanıdır: yanlış bir biçim bir hata üretmez, yalnız başka bir sayı üretir.
Eksen o biçim içindeki bir yöndür ve modeldeki numaralandırma soldan sağadır: eksen 0 abone
yönü, eksen 1 dönem yönüdür. eksen_toplami(1) her abonenin üç dönemini toplar ve 1.260 değer
verir; eksen_toplami(0) her dönemin bütün abonelerini toplar ve 3 değer verir. İkisi de “toplam”
adını taşır, ikisi de aynı 102.034,22 m³’ü paylaştırır, ama biri abone davranışını, öteki dönem
davranışını anlatır. Elle yazılan döngüde hangi yönde toplandığı döngü satırlarından okunur; dizi
arayüzünde tek bir tamsayıdan okunur ve o tamsayı çoğu zaman varsayılan olarak bırakılır. Bu ders
kapsamında eksen açıkça yazılıyor; bir sonraki derse kadar tutulacak kural şudur: eksen
yazılmayan bir toplulaştırma, yönü seçilmemiş bir toplulaştırmadır.
Dizinin Boş Değeri Yoktur
# bos.py — MODELDIR: dizinin "bos" degeri yok. 581 bos hucre bir sayiya donusmek # zorunda ve hangi sayiya donustugu bir karar. Uc karar, uc ortalama. KARAR = [ ("sifirla doldur", lambda v: [0.0 if x is None else x for x in v]), ("nobetci -1", lambda v: [-1.0 if x is None else x for x in v]), ("yalniz dolu hucre", lambda v: [x for x in v if x is not None]), ] z = gen([24, 12, 16, 12]) print(z(["581 bos hucrenin yeri", "oge sayisi", "ortalama (m3)", "40 m3 ustu"])) for ad, f in KARAR: a = Dizi(f(OKUMA), (len(f(OKUMA)), 1)) print(z([ad, a.oge, f"{a.toplam()/a.oge:.6f}", a.ustunde(40.0)])) TIP = [("16 bit tamsayi", "h"), ("32 bit tamsayi", "i"), ("32 bit kesirli", "f"), ("64 bit kesirli", "d")] LITRE = [int(round((0.0 if x is None else x) * 1000)) for x in OKUMA] z2 = gen([18, 10, 10, 30]) print("\n" + z2(["oge tipi", "bayt/oge", "3780 oge", "temsil araligi"])) for ad, t in TIP: b = array(t).itemsize aralik = (f"-{2**(8*b-1)} .. {2**(8*b-1)-1}" if t in ("h", "i") else ("7 anlamli basamak" if t == "f" else "15 anlamli basamak")) print(z2([ad, b, b * DUZ.oge, aralik])) TOPLAM = DUZ.toplam() kucuk = array("f", [TOPLAM])[0] print(f"\nen buyuk okuma {max(LITRE)} litre; 16 bit tamsayi bu degeri tasimaz") print(f"kumenin toplami {TOPLAM:.2f} m3; 32 bit kesirli tipte {kucuk} olarak tutulur, " f"fark {abs(kucuk-TOPLAM):.5f} m3 ve 01. dersin 0,0005 m3 esiginin ustunde")
581 bos hucrenin yeri oge sayisi ortalama (m3) 40 m3 ustu sifirla doldur 3780 26.993180 1116 nobetci -1 3780 26.839476 1116 yalniz dolu hucre 3199 31.895661 1116 oge tipi bayt/oge 3780 oge temsil araligi 16 bit tamsayi 2 7560 -32768 .. 32767 32 bit tamsayi 4 15120 -2147483648 .. 2147483647 32 bit kesirli 4 15120 7 anlamli basamak 64 bit kesirli 8 30240 15 anlamli basamak en buyuk okuma 60000 litre; 16 bit tamsayi bu degeri tasimaz kumenin toplami 102034.22 m3; 32 bit kesirli tipte 102034.21875 olarak tutulur, fark 0.00125 m3 ve 01. dersin 0,0005 m3 esiginin ustunde
Üç satır üç savunulabilir karardır ve ortalamayı 26,839476 ile 31,895661 m³ arasında oynatır. Sıfırla doldurmak eksik okumayı “hiç su kullanılmadı” saymaktır ve payı büyütmeden paydayı büyüttüğü için ortalamayı 4,90 m³ düşürür. Nöbetçi değer eksik hücreyi işaretlemek için seçilir, ama işaretin dışlanması unutulursa toplamdan 581 m³ düşer ve ortalama daha da iner. Yalnız dolu hücre kararı payda olarak 3.199’u kullanır ve önceki kursun ölçtüğü sayıyı verir. Üçünün de bir gerekçesi vardır; gerekçesi olmayan tek şey, kararın hiç yazılmamasıdır.
Dördüncü sütun bu kararın nerede görünmediğini gösteriyor. 40 m³ üstü hücre sayısı üç kararda da 1.116’dır, çünkü üç doldurma değeri de eşiğin altındadır. Bir denetim yalnız bu sayıya bakıyorsa üç kararı birbirinden ayıramaz. Kararın oynattığı sayı ile kararı görünmez kılan sayı aynı tablodadır ve hangisinin rapor edildiği kararın kendisi kadar önemlidir.
Elle yazılan sürümün bu kararı vermek zorunda kalmadığına dikkat etmek gerekir. İç içe listelerde
boş hücre None olarak durur; bu değer bir sayı değildir, toplamaya girmez ve is not None
denetimi onu döngünün içinde açıkça dışarıda bırakır. Karar oradadır ama görünürdür, çünkü her
döngüde tek tek yazılmıştır. Dizide karar hiç yazılmaz; diziyi kuran satırda bir doldurma değeri
seçilir ve o değer, bir daha hiç anılmadan, bütün özetlere karışır. Soyutlamanın gizlediği şey
kararın kendisi değil, kararın nerede alındığıdır.
Alt tablo öğe tipi kararını ölçüyor. Aynı 3.780 öğe, seçilen tipe göre 7.560 ile 30.240 bayt arasında yer tutar; dörde bir fark bellekten değil, temsil aralığından ödenir. En büyük okuma 60.000 litredir ve 16 bit tamsayının 32.767 sınırını aşar — bu tipi seçmek dört kat bellek kazancı değil, sessiz bir taşmadır. 32 bit kesirli tip taşmaz ama yaklaşık yedi anlamlı basamak tutar; kümenin 102.034,22 m³ toplamı sekiz anlamlı basamak gerektirdiği için 102.034,21875 olarak saklanır. Fark 0,00125 m³ ve önceki dersin 0,0005 m³ eşiğinin üstünde, yani bu tip seçimi tek başına iki koşumu farklı kılmaya yeter.
Özet
- Bir dizi arayüzü bitişik belleği, tek öğe tipini ve biçimi birlikte sabitler; biçim burada (1260, 3) ve öğe sayısı 3.780’dir.
- Aynı üç soru elle 22 satır, dizi arayüzünde 2 satır tutar; ayrılan bayt 188.864’ten 30.320’ye, öğe başına 49,96’dan 8,02 bayta iner.
- Geçiş sayısı iki yolda da 3’tür: soyutlama döngüyü çağrı yerinden kaldırır, veriden kaldırmaz.
- Dizinin boş değeri olmadığı için 581 boş hücre bir sayıya dönüşür; ortalama sıfır doldurmada 26,993180, nöbetçi değerde 26,839476, yalnız dolu hücrede 31,895661 m³ çıkar.
- Aynı üç karar 40 m³ üstü hücre sayısını hiç değiştirmez (1.116); öğe tipi seçimi ise 3.780 öğeyi 7.560 ile 30.240 bayt arasında tutar ve 16 bit tamsayı 60.000 litrelik okumayı taşımaz.
Sonraki Adım
Ölçü birimi kuruldu ve üç sayıdan biri beklenenin dışında çıktı: geçiş sayısı düşmedi. Sonraki ders tam bu sayıyı ele alır. Aynı türetme işi tek bir döngüde bir geçişle yapılabilir ya da vektörleştirilmiş çağrıların zinciri olarak yazılabilir; ikinci yol her çağrıda bir geçiş ve bir ara dizi ekler. Aynı ders vektörleştirmenin sessizce verdiği iki kararı da sayar: öğe tipinin yükseltilmesi ve dar bir tipte taşma. Bu dersin son tablosunda 16 bit tamsayının 60.000 litrelik okumayı taşımadığı görüldü; sonraki ders bu sınırın aşıldığında ne olduğunu ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.