Ders 03 / 22
Vektörleştirilmiş İşlemler
Aynı türetme işi tek döngüyle ve vektörleştirilmiş çağrı zinciriyle yapılır: satır sayısı 10'dan 2'ye düşerken geçiş sayısı 1'den 3'e, ayrılan bayt 30.320'den 90.960'a çıkar; zincirdeki her halka bir geçiş ve bir ara dizi ekler. Çağrının sessizce verdiği üç karar sayılır: tek bir kesirli sabit zinciri 64 bite taşır ve ayrılan baytı 30.400'den 60.720'ye çıkarır, 16 bit öğe tipi 3.780 öğenin 1.538'ini kaydırarak 102.034,22 m³ toplamı 1.239,85 m³ gösterir, nöbetçi değerle işaretlenmiş 581 boş hücrenin ilk işlemden sonra 0'ı ayırt edilebilir kalır ve aşımı sıfır görünen hücre sayısı 1.783'e çıkar.
İçindekiler
Önceki ders üç ölçüden birinin beklenenin dışında çıktığını gösterdi: satır sayısı on birde bire indi, ayrılan bayt altıda bire indi, ama geçiş sayısı hiç düşmedi. Bu ders o sayıyı ele alır ve daha kötü bir haberle başlar — vektörleştirilmiş bir zincirde geçiş sayısı düşmez, artar.
İşin kendisi önceki kursun türetilmiş değişken adımından geliyor: her hücrenin ikinci tarife basamağının üstünde kalan payı hesaplanacak. Okumadan 25 m³ çıkarılır, negatif sonuçlar sıfırda kırpılır, kalan litreye çevrilir. Üç işlem, tek bir sonuç. İki yolla yazılır: tek bir döngüde kaynaşmış olarak ve üç vektörleştirilmiş çağrının zinciri olarak.
- SH5. Vektörleştirilmiş bir çağrı döngüyü yazmaktan kurtarır, geçişten kurtarmaz. Zincirdeki her halka veriyi bir kez tarar ve sonucu yeni bir dizide tutar; kazanç satırda, bedel geçişte ve ayrılan bayttadır.
- SH6. Vektörleştirilmiş bir işlem öğe tipini kendi seçer. Bu seçim üç biçimde sayıyı oynatır: tipi yükselterek baytı büyütür, dar bir tipte taşarak sonucu küçültür, nöbetçi değeri sıradan bir sayıya çevirerek eksik hücreyi ayırt edilemez kılar.
Bir İş, İki Yol
# vektor.py — MODELDIR: vektorlestirilmis islem, ogeyi tek tek gezmeyen bir cagri # olarak yaziliyor. Her cagri veriyi bir kez tarar (bir gecis) ve sonucu yeni bir # dizide tutar (bir ara dizi). 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 = [0.0 if j in BOS else HAM[j] for j in range(ABONE * DONEM)] GECIS = 0 # veri uzerinden bastan sona bir tam tarama ARA = 0 # sonuc ve ara dizilerin ayirdigi bayt class Dizi: def __init__(self, veri, tip="d"): self.d, self.tip = array(tip, veri), tip def islem(a, f, tip=None): global GECIS, ARA GECIS += 1 c = Dizi([f(x) for x in a.d], tip or a.tip) ARA += sys.getsizeof(c.d) return c UST = 25.0 # ikinci tarife basamaginin ust siniri, m3 LITRE = 1000 # m3 -> litre def elle(veri): global GECIS, ARA cikti = [] for x in veri: f = x - UST cikti.append((f if f > 0.0 else 0.0) * LITRE) GECIS += 1 c = Dizi(cikti) ARA += sys.getsizeof(c.d) return c cikar = lambda a, s: islem(a, lambda x: x - s) kirp = lambda a, alt: islem(a, lambda x: x if x > alt else alt) carp = lambda a, s: islem(a, lambda x: x * s) def zincirle(a): return carp(kirp(cikar(a, UST), 0.0), LITRE) A = Dizi(OKUMA) GECIS, ARA = 0, 0 E = elle(A.d); ge, ae = GECIS, ARA GECIS, ARA = 0, 0 V = zincirle(A); gv, av = GECIS, ARA satir = lambda f: len(inspect.getsource(f).strip().splitlines()) 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([22, 18, 20, 10]) print(f"is: (okuma - {UST} m3) degeri sifirda kirpilir ve litreye cevrilir; {len(OKUMA)} oge\n") print(y(["olcu", "elle tek dongu", "vektorlestirilmis", "oran"])) print(y(["satir sayisi", satir(elle), satir(zincirle), f"{satir(elle)/satir(zincirle):.1f}x"])) print(y(["gecis sayisi", ge, gv, f"{gv/ge:.1f}x"])) print(y(["ayrilan bayt", ae, av, f"{av/ae:.1f}x"])) esit = sum(1 for x, z in zip(E.d, V.d) if abs(x - z) < 1e-9) print(f"\nsonuc ayni: {esit}/{len(OKUMA)} oge 1e-9 litre esiginin altinda ayni " f"(kayan noktali karsilastirma == ile yapilmaz; esik litre cozunurlugunun altindadir)") print(f"asim toplami {sum(V.d)/LITRE:.2f} m3")
is: (okuma - 25.0 m3) degeri sifirda kirpilir ve litreye cevrilir; 3780 oge olcu elle tek dongu vektorlestirilmis oran satir sayisi 10 2 5.0x gecis sayisi 1 3 3.0x ayrilan bayt 30320 90960 3.0x sonuc ayni: 3780/3780 oge 1e-9 litre esiginin altinda ayni (kayan noktali karsilastirma == ile yapilmaz; esik litre cozunurlugunun altindadir) asim toplami 34707.55 m3
İki yol da aynı 3.780 sayıyı üretiyor. Karşılaştırma litre çözünürlüğünün çok altında bir eşikle,
1e-9 litre ile yapıldı; kayan noktalı iki değer == ile karşılaştırılmaz, çünkü aynı matematiksel
sonuca farklı sırada varan iki hesap son basamakta ayrışabilir. Eşik burada anlamlı bir farkı
gizlemez: 1e-9 litre bir sayacın okuyabileceği en küçük değerin milyarda biridir.
Kazanç satırdadır ve gerçektir. Elle yazılan sürüm 10 satır tutuyor, zincir 2 satır. Fark yalnız
uzunluk değil: elle yazılan sürümde döngü sınırı, biriktirme listesi ve kırpma koşulu aynı yerde
iç içedir; üç işlemden birini değiştirmek döngünün gövdesine dokunmayı gerektirir. Zincirde üç
halka birbirinden bağımsızdır ve cikar, kirp, carp bir kez yazılıp her yerde kullanılır.
Bedel sonraki iki satırdadır ve çoğu zaman hesaba katılmaz. Zincir üç halkadan oluştuğu için veri üç kez taranır; elle yazılan döngü bir kez tarar. Her halka sonucunu yeni bir dizide tuttuğu için 3.780 öğelik üç dizi ayrılır: 90.960 bayt. Bunun 60.640 baytı ara diziler, yani kullanıldıktan sonra atılan iki geçici sonuçtur. Elle yazılan sürüm yalnız çıktı dizisini ayırır: 30.320 bayt. Küçük kümelerde bu fark görünmez; ölçek büyüdüğünde zincirin uzunluğu doğrudan bellek tepesine yazılır, çünkü ara dizilerin her biri girdi kadar yer tutar.
Buradan kursun sürekli kuralına bağlanan bir okuma çıkıyor: zincirdeki halka sayısı, çağrıyı yazan kişinin verdiği ama çoğu zaman farkında olmadığı bir karardır. Üç halkalık bir zincir üç geçiş, beş halkalık bir zincir beş geçiş demektir ve kod satırı ikisinde de aynı kalır. Zincirin uzunluğu kodda görünür, bedeli görünmez.
Zinciri Kaynaştırmak
# kaynasik.py — MODELDIR: ucuncu yol. Zincirin uc halkasi tek bir vektorlestirilmis # cagriya kaynastiriliyor; dongu yine yazilmiyor ama veri bir kez taraniyor. def kaynasik(a): return islem(a, lambda x: (x - UST if x > UST else 0.0) * LITRE) GECIS, ARA = 0, 0 K = kaynasik(A); gk, ak = GECIS, ARA w = gen([20, 10, 12, 12, 18]) print(w(["olcu", "elle", "zincir", "kaynasik", "en dusugu"])) print(w(["satir sayisi", satir(elle), satir(zincirle), satir(kaynasik), "zincir / kaynasik"])) print(w(["gecis sayisi", ge, gv, gk, "elle / kaynasik"])) print(w(["ayrilan bayt", ae, av, ak, "elle / kaynasik"])) print(w(["yeniden kullanilir", 0, 3, 0, "zincir"])) sapan = sum(1 for x, z in zip(V.d, K.d) if abs(x - z) > 1e-9) print(f"\nuc yol da ayni sonucu veriyor: {sapan} oge 1e-9 litre esiginin ustunde sapiyor") print(f"kaynasik yol zincirle ayni satirda, elle ile ayni gecis ve bayttadir; bedeli " f"uc kararin tek ifadeye girmesi ve yeniden kullanilabilir parca sayisinin 3'ten 0'a inmesi")
olcu elle zincir kaynasik en dusugu satir sayisi 10 2 2 zincir / kaynasik gecis sayisi 1 3 1 elle / kaynasik ayrilan bayt 30320 90960 30320 elle / kaynasik yeniden kullanilir 0 3 0 zincir uc yol da ayni sonucu veriyor: 0 oge 1e-9 litre esiginin ustunde sapiyor kaynasik yol zincirle ayni satirda, elle ile ayni gecis ve bayttadir; bedeli uc kararin tek ifadeye girmesi ve yeniden kullanilabilir parca sayisinin 3'ten 0'a inmesi
Üçüncü yol zincirin bedelinin kaçınılmaz olmadığını gösteriyor. Üç halkanın işi tek bir vektörleştirilmiş çağrının içine kaynaştırıldığında satır sayısı zincirdekiyle aynı kalıyor, geçiş sayısı elle yazılan döngüdeki gibi 1’e, ayrılan bayt 30.320’ye iniyor. Üç ölçünün de en düşüğü burada.
Bedelin nerede olduğu son satırda duruyor. Zincirde cikar, kirp ve carp bağımsız üç parçadır
ve başka bir hesapta başka sırayla yeniden kullanılır. Kaynaştırılmış ifadede aynı üç karar tek bir
gövdeye girer ve dışarıdan alınamaz; yeniden kullanılabilir parça sayısı 3’ten 0’a iner. Tarife
eşiği bir basamak kayarsa zincirde yalnız çağrının bir parametresi değişir, kaynaştırılmış ifadede
gövde yeniden yazılır.
Ölçünün kursa taşınan biçimi budur: soyutlamanın kazancı üç sayının hepsinde birden görünmez ve “vektörleştirilmiş yazmak” tek bir şey değildir. Kaç halka yazıldığı bir karardır ve o karar geçiş sayısıyla bellek tepesini birlikte belirler.
Çağrının Verdiği Üç Karar
# gizli.py — MODELDIR: vektorlestirilmis cagrinin senin yerine verdigi uc karar. LITRELIK = [int(round(x * LITRE)) for x in OKUMA] # 1. Tip yukseltme. Zincirin ilk halkasi tamsayi, ikincisi kesirli bir sabitle carpiyor. TAM = Dizi(LITRELIK, "i") GECIS, ARA = 0, 0 kalan = islem(TAM, lambda x: x - 25000, "i") tam_bayt = ARA + sys.getsizeof(TAM.d) GECIS, ARA = 0, 0 ucret = islem(islem(TAM, lambda x: x - 25000, "i"), lambda x: x * 0.0042, "d") kesirli_bayt = ARA + sys.getsizeof(TAM.d) # 2. Tasma. Bellek kazanmak icin oge tipi 16 bit tamsayi secilirse litre degerleri # sinira sigmaz. Kaydirma acikca yaziliyor; oge tipinin davranisinin modelidir. SINIR = 2 ** 15 kaydir = lambda v: ((v + SINIR) % (2 * SINIR)) - SINIR tasan = [v for v in LITRELIK if v > SINIR - 1] kayan = [kaydir(v) for v in LITRELIK] # 3. Nobetci degerin kaybolmasi. 581 bos hucre -1 ile isaretlenmisti. NOB = Dizi([-1.0 if j in BOS else HAM[j] for j in range(ABONE * DONEM)]) son = carp(kirp(cikar(NOB, UST), 0.0), LITRE) ayirt = sum(1 for j in BOS if abs(son.d[j]) > 1e-9) sifir = sum(1 for x in son.d if abs(x) < 1e-9) z = gen([26, 22, 26]) print(z(["gizlenen karar", "yazilan varsayim", "olculen sonuc"])) print(z(["tip yukseltme", "tamsayi kalir", f"{tam_bayt} -> {kesirli_bayt} bayt"])) print(z(["16 bit tasma", "tasma olmaz", f"{len(tasan)}/{len(LITRELIK)} oge kaydi"])) print(z(["nobetci hucre", "581 hucre isaretli", f"{ayirt}/581 ayirt edilebilir"])) print(f"\ntip yukseltme: tek bir kesirli sabit zincirin geri kalanini 64 bite tasir, " f"ayrilan bayt {kesirli_bayt - tam_bayt} artar; ucret toplami {sum(ucret.d):.2f}") print(f"tasma: {len(tasan)} oge 32.767 litre sinirini asiyor; toplam " f"{sum(LITRELIK)/LITRE:.2f} m3 yerine {sum(kayan)/LITRE:.2f} m3 cikar") print(f"nobetci: zincirden sonra asimi sifir gorunen hucre {sifir}; bunun 581'i " f"gercek sifir degil, isaretlenmis bos hucre")
gizlenen karar yazilan varsayim olculen sonuc tip yukseltme tamsayi kalir 30400 -> 60720 bayt 16 bit tasma tasma olmaz 1538/3780 oge kaydi nobetci hucre 581 hucre isaretli 0/581 ayirt edilebilir tip yukseltme: tek bir kesirli sabit zincirin geri kalanini 64 bite tasir, ayrilan bayt 30320 artar; ucret toplami 31643.72 tasma: 1538 oge 32.767 litre sinirini asiyor; toplam 102034.22 m3 yerine 1239.85 m3 cikar nobetci: zincirden sonra asimi sifir gorunen hucre 1783; bunun 581'i gercek sifir degil, isaretlenmis bos hucre
Tip yükseltme zincirin en sessiz kalemidir. Dizi 32 bit tamsayı olarak kuruldu ve ilk halka
tamsayı kaldı; ikinci halka kesirli bir birim fiyatla çarpınca sonuç tamsayıda temsil edilemez ve
bütün dizi 64 bit kesirliye taşınır. Ayrılan bayt 30.400’den 60.720’ye çıkar. Yazılan kodda
büyümeyi gösteren hiçbir işaret yoktur; büyümeye karar veren şey 0.0042 sabitinin kesirli
olmasıdır. Uzun zincirlerde bu, ilk kesirli halkadan sonraki bütün halkaların iki kat yer tutması
demektir.
Taşma ters yöndedir. Öğe tipini daraltmak bellek kazandırır: 16 bit tamsayı 3.780 öğeyi 7.560 baytta tutar. Ama litre cinsinden okumaların 1.538’i 32.767 sınırını aşıyor ve bu değerler negatif tarafa kayıyor. Kümenin toplamı 102.034,22 m³ yerine 1.239,85 m³ çıkar — yüzde 98,8’lik bir kayıp. Sonuç bir hata iletisiyle değil, makul görünen küçük bir sayıyla gelir; bir denetim yalnız “toplam pozitif mi” diye bakıyorsa bunu geçirir. Kaydırma davranışı burada açıkça yazıldı, çünkü öğe tipinin sınırı aşıldığında ne yaptığı tipin kendi kararıdır ve model bunu görünür kılıyor.
Nöbetçi değer üçüncü karardır ve önceki dersin bıraktığı yerden devam eder. Boş hücreleri
-1 ile işaretlemek dizide eksik değer tutmanın bilinen yoludur, ama işaret yalnız ilk işleme
kadar yaşar: -1 değerinden 25 çıkınca -26 olur, kırpma onu sıfıra çeker ve çarpma sıfır
bırakır. Zincirin sonunda 581 hücrenin hiçbiri ayırt edilemez. Aşımı sıfır görünen 1.783
hücrenin 581’i aslında hiç okunmamış hücredir ve bu ikisi arasındaki farkı gösterecek hiçbir iz
kalmamıştır. Kırpma işlemi burada iki farklı anlamı tek değere indiriyor: “eşiğin altında kaldı”
ile “ölçülmedi”.
Üç kalemin ortak yanı şudur: hiçbiri hata üretmez. Tip yükseltmesi doğru sonucu verir ve yalnız belleği büyütür, taşma makul görünen bir sayı verir, nöbetçinin kırpılması geçerli bir aşım değeri verir. Bir soyutlamanın gizlediği kararlar tam olarak bunlardır — çağrının çalıştığı ve sonuç ürettiği durumlarda alınan kararlar. Bu yüzden kursun kuralı bir çağrının doğru sonuç verip vermediğine değil, varsayılanının yazılıp yazılmadığına bakar.
Özet
- Vektörleştirilmiş zincir satır sayısını 10’dan 2’ye indirir, geçiş sayısını 1’den 3’e ve ayrılan baytı 30.320’den 90.960’a çıkarır; bunun 60.640 baytı ara dizidir.
- Zincirdeki halka sayısı geçiş sayısına birebir yazılır; kod satırı sabit kalır, bedel kodda görünmez.
- Kesirli bir sabit zincirin geri kalanını 64 bite taşır ve ayrılan baytı 30.400’den 60.720’ye çıkarır.
- 16 bit öğe tipi 3.780 öğenin 1.538’ini kaydırır ve 102.034,22 m³ toplamı 1.239,85 m³ gösterir; sonuç hata değil, makul görünen bir sayıdır.
- Nöbetçi değerle işaretlenmiş 581 boş hücrenin ilk işlemden sonra 0’ı ayırt edilebilir kalır; aşımı sıfır görünen hücre 1.783’e çıkar ve “eşiğin altında” ile “ölçülmedi” tek değere iner.
- Üç halka tek çağrıya kaynaştırıldığında geçiş 1’e, bayt 30.320’ye iner ve satır sayısı değişmez; bedeli yeniden kullanılabilir parça sayısının 3’ten 0’a inmesidir.
Sonraki Adım
Bu derste bütün işlemler tek bir biçim üzerinde yapıldı: 3.780 öğelik bir dizi ile bir sayı arasında. Gerçek hesaplarda iki dizi karşılaşır ve biçimleri çoğu zaman aynı olmaz — dönem başına bir taban, abone başına bir hane sayısı, tarife basamağı başına bir birim fiyat. Sonraki ders farklı biçimlerin nasıl uyumlu sayıldığını ve uyumsuz iki biçimin neden hata vermek yerine beklenmedik bir dizi ürettiğini ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.