---
title: 'Eşzamanlılık ve Başarım'
source: 'https://academia.sh/tr/kurslar/python-eszamanlilik'
language: tr
updated: '2026-08-17T04:42:14+00:00'
license: 'CC BY-SA 4.0'
lessons: 10
outcomes:
    - 'Genel yorumlayıcı kilidinin eşzamanlılık seçimine etkisini açıklar'
    - 'İş parçacığı, süreç ve eşzamansız modeller arasında ölçüte dayalı seçim yapar'
    - 'Darboğazları profil çıkararak bulur'
---

# Eşzamanlılık ve Başarım

Bu kurs süre ölçmez, örtüşen adım sayar; genel yorumlayıcı kilidinin hangi adımı örtüştürmeye izin verdiğini, iş parçacığı ile sürecin hangi yükte ne kazandırdığını ve bir ölçümün neyi saydığını söylemeden karşılaştırılamayacağını ölçer.

## Eşzamanlılık Modelleri

Genel yorumlayıcı kilidinin neyi engellediği, girdi/çıktı ağırlıklı işlerde iş parçacığı, işlemci ağırlıklı işlerde çoklu süreç, olay döngüsü ile eş yordamlar, engelleyen kodun eşzamansız akışa taşınması ve ortak arayüzle iş dağıtımı.

### Genel Yorumlayıcı Kilidi

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/genel-yorumlayici-kilidi

Bu kurs süre değil örtüşen adım sayar; seksen adımlık kurguda tek akış her yükte 80 tik ve 0 örtüşen adım verir, tek yuvalı rejimde bir tikte atılan en çok işlemci adımı 1'dir ve girdi/çıktı ağırlıklı yükün 26 işlemci adımı 26 ayrı tike dağılır.

Önceki kurs on bir derste tek bir soruyu ölçtü: çağrıyı kim yanıtladı, sözü kim denetledi.
Kapanışı bu ölçümlerin hiç yazılmamış varsayımını açığa çıkardı — **tek akış**. Yanıtı veren
sınıf sorulduğunda tek bir çözümleme zinciri yürüdü; ihlali yakalayan katman sorulduğunda
çağrılar biri bitince öbürü başlayarak koştu. Tek akışta "kim yanıtladı" sorusunun her
seferinde tek bir yanıtı oldu.

Birden çok akış varken soru biçim değiştirir. Artık sorulan **kim yanıtladı** değil, **kim
aynı anda ilerleyebildi**. Bu kurs o soruyu on derste sayar ve ilk dersi en dar yerinden
başlar: Python'un yorumlayıcısında bir anda kaç akışın işlemci adımı atabildiğinden.

#### Kilit Bir Sayıdır

**Genel yorumlayıcı kilidi (global interpreter lock)**, yorumlayıcının kendi iç yapılarını
tutarlı tutmak için koyduğu bir kısıttır: aynı anda **tek bir akış** yorumlayıcı bayt kodu
yürütür. Kilidi elinde tutmayan akış bekler. Kilit bir anda bir akışa verilir, bırakılır,
başkasına geçer.

Bu dersin bakışında kilit bir düzenek değil, bir **sayıdır**: bir anda kaç akışın işlemci
adımı atabildiği. Kursun ölçü adı bu sayı için **yuva (slot)**. Kilit varken yuva birdir.

Kilidin bırakıldığı yerler bu sayıyı bozmaz, çünkü kilidin bırakıldığı adım zaten işlemci
adımı değildir. Bir akış dosyadan okurken, ağdan yanıt beklerken ya da uykudayken
yorumlayıcı bayt kodu yürütmez; kilit o sırada başka bir akışa geçer. Kursun ikinci adım
türü budur: **girdi/çıktı adımı**. Yuva bu adımları sınırlamaz.

Süreç, iş parçacığı, çizelgeleme ve bağlam değiştirme **İşletim Sistemi Kavramları**
kursunda mekanizma olarak kuruldu; burada tekrarlanmaz. Buradaki soru işletim sisteminin
akışları nasıl sıraladığı değil, **Python'un hangi adımı örtüştürebildiğidir**.

#### Neden Süre Ölçmüyoruz

Eşzamanlılık anlatan bir ölçümün ilk sezgisi süreyi karşılaştırmaktır. Bu kurs onu yapmaz ve
hiçbir derste yapmayacak.

Gerçek süre üç şeye birden bağlıdır: makinenin kendisi, o anki yükü ve ölçümün hangi anda
başlatıldığı. Üçü de yazının dışındadır. Aynı kod aynı makinede iki kez koştuğunda iki ayrı
sayı verir ve iki ayrı makinede karşılaştırılamaz. Süreyle yazılmış bir iddia okunduğu
yerde **yeniden üretilemez**.

Onun yerine sayılabilir bir şey lazım: bir tikte birden çok görev ilerlediyse, ilerleyenlerin
**bir eksiği** örtüşmüş sayılır. Kursun ölçüsü budur ve adı **örtüşen adım (overlapping
step)**. Örtüşen adım kurgudan çıkar, koşumdan okunur ve her yerde aynı sayıyı verir.

Yasak yalnız saniyeyi kapsamıyor. Hızlanma çarpanı da bir süre iddiasıdır ve aynı sorunu
taşır; "şu kadar kat hızlı" cümlesi, hangi işin hangi koşulda ölçüldüğü yazılmadığında
hiçbir şey söylemez. Kursun sonlarındaki bir ders bunu tersinden ölçecek: aynı iş, aynı
rejim, tek fark ölçümün neyi saydığı — ve iki sayı çıkacak. Bu ders o iddianın zeminini
kuruyor; sayılan birim her ölçümde **adıyla** yazılır.

#### Kurgu

Sekiz görev, her biri **on adım**; toplam **80 adım**. Her adım ya **işlemci** adımıdır ya da
**girdi/çıktı** adımı. Bir yük, girdi/çıktı adımlarının payıyla tanımlanır: girdi/çıktı
ağırlıklı yükte pay onda yedi, dengeli yükte onda beş, işlemci ağırlıklı yükte onda birdir.

Görevler belirlenimci bir çizelgeleyicide **tik tik** ilerler. Bir tikte kaç görevin
ilerleyebileceğini iki sayı belirler: **işçi**, aynı anda ilerleyebilecek görev sayısı; ve
**işlemci yuvası**, bir tikte kaç görevin işlemci adımı atabildiği. Tek yuva, işlemci
adımlarının sırayla atılması demektir.

Ölçümün varsayımları:

- **EM1** — Kâhin kurgunun kendisidir: sekiz görevin adım dizisini biz ürettik, hangi adımın
  hangi türden olduğu bilinir.
- **EM2** — Sekiz görev, görev başına on adım, toplam seksen adım. Üç yük yalnız girdi/çıktı
  payıyla ayrılır; adım sayısı üçünde de aynıdır.
- **EM3** — Tik çizelgeleyicinin birimidir. Bir tikte bir görev en çok bir adım atar.
- **EM4** — İşçi, bir tikte ilerleyebilecek en çok görev sayısıdır; sıra görev indisine göre
  verilir.
- **EM5** — İşlemci yuvası, bir tikte kaç görevin işlemci adımı atabildiğidir. **Tek yuva,
  genel yorumlayıcı kilidinin bu modeldeki karşılığıdır.**
- **EM6** — Girdi/çıktı adımı yuvadan etkilenmez; kilidin bırakıldığı adım budur.
- **EM7** — Örtüşen adım, bir tikte ilerleyen görev sayısının bir eksiğidir; hiç görev
  ilerlemezse sıfırdır.
- **EM8** — Tek akış rejimi işçi bir, yuva birdir: bir tikte tek görev, tek adım.
- **EM9** — Süre ölçülmez ve yazılmaz. Sayılan şey adım, tik ve örtüşen adımdır.
- **EM10** — `iz`, `kosum`'un rapor ikizidir: aynı sırayla aynı kararları verir, davranışı
  değiştirmez. Çıktının son satırı ikisinin aynı sayıyı verdiğini gösterir.
- **EM11** — Ölçüm tek koşumdur; tohum sabittir ve kurgu belirlenimcidir.
- **EM12** — Bu ders yalnız **tikin içine** bakar. Rejimlerin toplam tik sayısı sonraki
  derslerin ölçüsüdür ve burada basılmaz.

#### Ölçüm

```python
"""Genel yorumlayici kilidi: bir tikte kac gorev ilerleyebilir."""

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):
    """Her gorev bir adim dizisi; gc_payi/10 orani girdi/cikti adimi."""
    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):
    """Tik tik ilerleyen belirlenimci cizelgeleyici.

    isci           : ayni anda ilerleyebilecek gorev sayisi
    islemci_yuvasi : bir tikte kac gorev ISLEMCI adimi atabilir
    Donen: tik sayisi, ortusen adim, islemci adimi, g/c adimi
    """
    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 iz(gorevler, isci, islemci_yuvasi):
    """kosum'un rapor ikizi: her tikte atilan adimlari tur tur dokum eder."""
    kalan, satir = [list(g) for g in gorevler], []
    while any(kalan):
        etkin = [i for i, g in enumerate(kalan) if g][:isci]
        if not etkin:
            break
        yuva, ic, gc = islemci_yuvasi, 0, 0
        for i in etkin:
            if kalan[i][0] == ISLEMCI:
                if yuva <= 0:
                    continue
                yuva -= 1
                ic += 1
            else:
                gc += 1
            kalan[i].pop(0)
        satir.append((ic, gc))
    return satir


YUKLER = (("girdi/çıktı ağırlıklı", 7), ("dengeli", 5), ("işlemci ağırlıklı", 1))

print(f"{'yük':<22s} {'adım':>5s} {'işlemci':>8s} {'g/ç':>5s} "
      f"{'tek akış tik':>13s} {'örtüşen':>8s}")
for ad, pay in YUKLER:
    g = gorevler(gc_payi=pay)
    tik, ortusen, islemci_adim, gc_adim = kosum(g, 1, 1)
    print(f"{ad:<22s} {sum(len(t) for t in g):5d} {islemci_adim:8d} "
          f"{gc_adim:5d} {tik:13d} {ortusen:8d}")

g = gorevler(gc_payi=7)
satir = iz(g, 8, 1)
print()
print("tek yuva, sekiz işçi, girdi/çıktı ağırlıklı yük — ilk on iki tik")
print(f"{'tik':>4s} {'ilerleyen':>10s} {'işlemci':>8s} {'g/ç':>5s} {'örtüşen':>8s}")
for i, (ic, gc) in enumerate(satir[:12], 1):
    print(f"{i:4d} {ic + gc:10d} {ic:8d} {gc:5d} {max(0, ic + gc - 1):8d}")
print(f"ilk on iki tikte atılan adım {sum(ic + gc for ic, gc in satir[:12])}, "
      f"örtüşen adım {sum(max(0, ic + gc - 1) for ic, gc in satir[:12])}")

print()
print(f"bir tikte atılan en çok işlemci adımı {max(ic for ic, _ in satir)}")
print(f"işlemci adımı taşıyan tik {sum(1 for ic, _ in satir if ic)}, "
      f"toplam işlemci adımı {sum(ic for ic, _ in satir)}")
print(f"iz ile kosum aynı sayıyı veriyor: işlemci "
      f"{sum(ic for ic, _ in satir) == kosum(g, 8, 1)[2]}, g/ç "
      f"{sum(gc for _, gc in satir) == kosum(g, 8, 1)[3]}")
```

```
yük                     adım  işlemci   g/ç  tek akış tik  örtüşen
girdi/çıktı ağırlıklı     80       26    54            80        0
dengeli                   80       40    40            80        0
işlemci ağırlıklı         80       73     7            80        0

tek yuva, sekiz işçi, girdi/çıktı ağırlıklı yük — ilk on iki tik
 tik  ilerleyen  işlemci   g/ç  örtüşen
   1          5        1     4        4
   2          5        1     4        4
   3          5        1     4        4
   4          5        1     4        4
   5          3        1     2        2
   6          4        1     3        3
   7          4        1     3        3
   8          5        1     4        4
   9          5        1     4        4
  10          3        1     2        2
  11          2        1     1        1
  12          1        1     0        0
ilk on iki tikte atılan adım 47, örtüşen adım 35

bir tikte atılan en çok işlemci adımı 1
işlemci adımı taşıyan tik 26, toplam işlemci adımı 26
iz ile kosum aynı sayıyı veriyor: işlemci True, g/ç True
```

#### Tek Akış: Yükün Bileşimi Hiçbir Şey Değiştirmiyor

Üst tablonun son iki sütunu üç satırda da aynı: **80 tik, 0 örtüşen adım.**

Bu, bir başarım sonucu değil, bir tanımdır. Tek akış rejiminde işçi birdir; bir tikte tek
görev ilerler, ilerleyenin bir eksiği sıfırdır. Seksen adım seksen tik eder. Yükün
bileşiminin hiçbir etkisi yok: girdi/çıktı ağırlıklı yükte 54 adım kilidi bırakabilirdi,
ama bırakacak başka bir akış olmadığı için bırakmanın karşılığı da yok.

Buradan kursun ilk okuması çıkıyor: **örtüşme yükün özelliği değil, rejimin özelliğidir.**
Aynı seksen adım, ilerleyecek başka bir görev yoksa hiçbir yükte örtüşmez.

Üçüncü ve dördüncü sütun ise yükün kendi özelliği ve rejimden bağımsız. Girdi/çıktı ağırlıklı
yükte **26** işlemci, **54** girdi/çıktı adımı var; işlemci ağırlıklı yükte **73** işlemci,
**7** girdi/çıktı adımı. Toplam üçünde de 80. Bu iki sayı ölçümün her yerinde sabit kalacak,
çünkü rejim adımların türünü değiştirmez, yalnız **hangi tikte** atıldığını değiştirir.

#### Tikin İçi

Alt tablo tek yuvalı rejimin ilk on iki tikini satır satır açıyor ve dersin asıl ölçümü
burada.

**İşlemci sütunu on iki satırın on ikisinde de 1.** Bir tikte atılan en çok işlemci adımı
**1** ve bu, koşumun tamamı için geçerli. Yuva sayısı neyse, bir tikteki işlemci adımı
sayısının üst sınırı odur; burada yuva bir olduğu için üst sınır bir.

**Girdi/çıktı sütunu 0 ile 4 arasında dolaşıyor.** Aynı tikte dört görev birden girdi/çıktı
adımı atabiliyor, çünkü o adımlar yuvayı kullanmıyor. Birinci tikte beş görev ilerliyor:
dördü girdi/çıktı adımı, biri işlemci adımı. Dört örtüşen adım o tikte kazanılıyor.

Sekiz görev canlıyken beşinin ilerlemesi ilk bakışta tuhaf görünebilir. Sebebi sütunlarda
yazılı: o tikte dört görevin sırasındaki adım işlemci adımıdır, yuva ise bir tanedir.
Yuvayı alan ilerler, kalan üçü **bekler**. Bekleyen görev o tikte hiç adım atmaz.

On ikinci satır kilidi en yalın biçimde gösteriyor: **1 ilerleyen, 1 işlemci adımı, 0 örtüşen
adım.** O tikte ilerleyebilecek tek bir görev vardı ve adımı işlemci adımıydı. Böyle bir tik,
tek akış rejiminden ayırt edilemez.

#### İşçi ile Yuva Ayrı İki Sayıdır

Modelin iki ayarı var ve karıştırılmaları bu konunun en sık görülen kusurudur.

**İşçi**, bir tikte kaç görevin sıraya girebildiğini söyler. Sıraya girmek ilerlemeyi
garanti etmez: on ikinci tikte iki görev canlıydı, biri sıraya girdi ve ilerledi, öbürünün
adımı işlemci adımıydı ve yuva doluydu. **Yuva**, sıraya girenlerden kaçının işlemci adımı
atabildiğini söyler.

İkisi bağımsız ayarlandığı için model dört ayrı rejim üretir ve bu kursun kalan derslerinin
her biri bu iki sayıdan birini değiştirir. İş parçacığı açmak **işçi** sayısını büyütür;
yorumlayıcı tek olduğu için yuvayı büyütmez. Ayrı süreç başlatmak ise ayrı bir yorumlayıcı
demektir ve **yuvayı** büyütür. Eşzamansız programlama üçüncü bir yol izler: ne işçiyi ne
yuvayı işletim sisteminden ister, sıraya girme kararını kodun kendisine bırakır.

Bu ayrım, "eşzamanlılık" tek bir şey değil de bir seçim kümesi olduğu için önemli. Bir
programa iş parçacığı eklemek onu her yükte hızlandırmaz; eklenen şey işçidir ve işçinin
kazandırabileceği tek şey **girdi/çıktı adımlarının örtüşmesidir**. Yukarıdaki tabloda
işlemci ağırlıklı yükte örtüşebilecek adım sayısı **7**'dir; o yüke sekiz işçi de verilse
kazanç bu sayının üstüne çıkamaz. Kaç işçinin ne kazandırdığı sonraki iki dersin ölçüsüdür,
ama üst sınır bu derste zaten yazılı: **örtüşen adım, örtüşebilen adım sayısını geçemez.**

#### Örtüşemeyen Adım

Son iki satır bu dersin taşıdığı yapısal sonucu veriyor: **işlemci adımı taşıyan tik 26,
toplam işlemci adımı 26.**

İki sayının eşit olması bir tesadüf değil, tek yuvanın doğrudan sonucudur. Bir tik en çok bir
işlemci adımı taşıyabildiğine göre, 26 işlemci adımı en az **26 ayrı tike** dağılmak
zorundadır. Genel kural şudur: tek yuvalı bir rejimde **tik sayısı, işlemci adımı sayısının
altına inemez**. Yuva sayısı `y` olduğunda alt sınır, işlemci adımı sayısının `y`'ye
bölümünün yukarı yuvarlanmış hâlidir.

Buradan kursun ölçü ekseni doğuyor. Bir yükün 80 adımının hepsi eşit değildir: girdi/çıktı
ağırlıklı yükte **54 adım örtüşebilir, 26 adım örtüşemez**. Eşzamanlılıktan beklenen kazancın
üst sınırını belirleyen şey görev sayısı ya da işçi sayısı değil, **örtüşebilen adımların
payıdır**.

İlk on iki tikte atılan **47** adımın **35**'i örtüşen adım. Bu iki sayı arasındaki fark
tam 12 — yani tik sayısı. Model boyunca geçerli olan kimlik budur: **tik sayısı, toplam
adımdan örtüşen adımın çıkarılmasıyla bulunur.** Örtüşen adımı saymakla tik saymak aynı
ölçümün iki yüzüdür; kurs bu yüzden süreye hiç ihtiyaç duymaz.

#### Özet

- Genel yorumlayıcı kilidi bu kursta bir düzenek değil bir sayıdır: bir tikte kaç görevin
  işlemci adımı atabildiği. Kilit varken bu sayı — **yuva** — birdir.
- Girdi/çıktı adımları yuvayı kullanmaz; kilidin bırakıldığı adımlar bunlardır ve tek yuvalı
  rejimde aynı tikte dört görev birden atabilir.
- Tek akış rejimi üç yükün üçünde de **80 tik** ve **0 örtüşen adım** verir. Örtüşme yükün
  değil rejimin özelliğidir; ilerleyecek ikinci bir görev yoksa hiçbir yük örtüşmez.
- Tek yuvada bir tikte atılan en çok işlemci adımı **1**'dir; girdi/çıktı ağırlıklı yükün
  **26** işlemci adımı **26** ayrı tike dağılır ve tik sayısı bu sayının altına inemez.
- Tik sayısı, toplam adımdan örtüşen adımın çıkarılmasıyla bulunur; bu yüzden örtüşen adım
  süreyi ölçmeden karşılaştırma yapmaya yeter.

#### Sonraki Adım

Bu ders tikin içine baktı ve toplam tik sayısını hiç yazmadı. Sonraki ders onu sayar: sekiz
işçiyi tek yuvayla koşturmak, yani iş parçacığı rejimi, seksen adımı kaç tikte bitirir?
Sorunun yanıtı tek bir sayı değil, **ikidir** — çünkü aynı düzenek girdi/çıktı ağırlıklı
yükle işlemci ağırlıklı yükte apayrı davranır ve aradaki farkın nereden geldiği bu dersin
son iki satırında zaten yazılıdır.

### İş Parçacıkları

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/is-parcaciklari

Sekiz işçi ve tek yuva, seksen adımı girdi/çıktı ağırlıklı yükte 31 tikte bitirir (kazanç 49, oran 0,6125), işlemci ağırlıklı yükte ancak 73 tikte (kazanç 7); aynı düzenek, yedi kat fark ve kazanç her yükte örtüşebilen adım sayısıyla sınırlıdır.

Önceki ders tikin içine baktı ve toplam tik sayısını hiç yazmadı. Tek yuvalı rejimin ilk on
iki tikinde işlemci sütunu hep **1**'di, girdi/çıktı sütunu ise dörde kadar çıkıyordu.
Buradan bir alt sınır çıkmıştı: 26 işlemci adımı en az 26 ayrı tike dağılmak zorunda.

Bu ders sayının kendisini sorar. Sekiz görevi aynı anda sıraya sokmak — yani **iş parçacığı
(thread)** açmak — seksen adımı kaç tikte bitirir? Yanıt tek bir sayı değil. Aynı düzenek,
yükün bileşimine göre iki apayrı sayı verir ve bu dersin ölçtüğü şey aradaki farkın
büyüklüğüdür.

#### İş Parçacığı İşçi Ekler, Yuva Eklemez

İş parçacığı, aynı süreç içinde ayrı bir yürütme akışıdır. Süreç, iş parçacığı, çizelgeleme
ve bağlam değiştirme **İşletim Sistemi Kavramları** kursunda mekanizma olarak kuruldu;
burada tekrarlanmaz. Bu dersin sorusu mekanizma değil, **sayı**: iş parçacığı açmak modelin
hangi ayarını değiştirir?

Yanıt önceki dersin iki ayarında yazılı. Sekiz iş parçacığı, sekiz görevin aynı anda sıraya
girebilmesi demektir; yani **işçi** sayısı sekize çıkar. Ama sekiz iş parçacığı da aynı
yorumlayıcının içindedir ve o yorumlayıcının kilidi tektir. **Yuva** birde kalır.

Bunun doğrudan sonucu şudur: iş parçacığı eklemek, girdi/çıktı adımlarının örtüşmesine izin
verir; işlemci adımlarının örtüşmesine izin vermez. Bir iş parçacığı ağdan yanıt beklerken
kilidi bırakır ve başka bir iş parçacığı ilerler. İkisi de aynı anda bayt kodu yürütmek
isterse biri bekler.

Bu ayrım, iş parçacığı hakkındaki iki yaygın cümlenin ikisini de düzeltiyor. "İş parçacığı
işi paralel yapar" cümlesi yalnız girdi/çıktı adımları için doğrudur; işlemci adımlarında
paralellik yoktur, sıra vardır. "Kilit yüzünden iş parçacığı işe yaramaz" cümlesi ise
girdi/çıktı adımlarını görmez. İkisinin de eksiği aynı: **hangi adımdan söz edildiğini
söylememek.** Aşağıdaki ölçüm tam olarak bu ayrımı sayıyor.

#### Rejim Modellenir, Çalıştırılmaz

Bu ders gerçek iş parçacığı başlatmaz ve bu bir kolaylık tercihi değil, ölçümün koşuludur.
Gerçekten sekiz iş parçacığı açan bir program, hangi iş parçacığının hangi anda ilerlediğini
işletim sisteminin çizelgeleyicisine bırakır. Aynı program iki kez koştuğunda iki ayrı sıra
üretir; yazılan çıktı okuyanın makinesinde çıkmaz. Ölçülmek istenen şey — hangi adımın
örtüştüğü — o gürültünün altında kalır.

Onun yerine çizelgeleme **modellenir**: sıra kuralı yazılıdır, tohum sabittir, koşum her
yerde aynı sayıyı verir.

Ölçümün varsayımları:

- **EM13** — Çekirdek önceki dersin aynısıdır: sekiz görev, görev başına on adım, toplam
  seksen adım; tohum sabit, kurgu belirlenimci.
- **EM14** — İş parçacığı rejiminin modeldeki karşılığı **işçi 8, yuva 1**'dir.
- **EM15** — Gerçek iş parçacığı başlatılmaz; başlatılsaydı çıktı çizelgeleyiciye bağlı
  olurdu ve yeniden üretilemezdi.
- **EM16** — Karşılaştırma tabanı tek akış rejimidir (işçi 1, yuva 1) ve üç yükte de
  **80 tik** verir.
- **EM17** — Kazanç, tek akış tikinden rejimin tikinin çıkarılmasıdır; oran kazancın tek
  akış tikine bölümüdür. **Bu bir hızlanma çarpanı değildir**, iki tik sayısının farkıdır.
- **EM18** — Alt sınır, önceki derste kurulan kuraldır: tek yuvada tik sayısı işlemci adımı
  sayısının altına inemez.
- **EM19** — İşçi taraması aynı görev dizisiyle yapılır; yalnız işçi sayısı değişir.
- **EM20** — Bağlam değiştirme bedeli modelde sıfırdır. Gerçek bir sistemde bu bedel vardır
  ve modelin dışındadır; ölçülen şey bedelin büyüklüğü değil, örtüşmenin sınırıdır.
- **EM21** — Görevler birbirinden bağımsızdır; ortak durum, kilit ve yarış bu ölçümün
  dışındadır.
- **EM22** — Süre ölçülmez. Sayılan birim adım, tik ve örtüşen adımdır.
- **EM23** — Ölçüm tek koşumdur; üç yük aynı çekirdekten üretilir.

#### Ölçüm

```python
"""Is parcaciklari: ayni duzenek, iki yuk, iki ayri kazanc."""

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):
    """Her gorev bir adim dizisi; gc_payi/10 orani girdi/cikti adimi."""
    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):
    """Donen: tik, ortusen adim, islemci adimi, g/c adimi."""
    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


YUKLER = (("girdi/çıktı ağırlıklı", 7), ("dengeli", 5), ("işlemci ağırlıklı", 1))

print("iş parçacığı rejimi: sekiz işçi, tek yuva")
print(f"{'yük':<22s} {'adım':>5s} {'tek akış':>9s} {'iş parçacığı':>13s} "
      f"{'örtüşen':>8s} {'kazanç':>7s} {'oran':>7s}")
for ad, pay in YUKLER:
    g = gorevler(gc_payi=pay)
    tek = kosum(g, 1, 1)[0]
    tik, ortusen, _, _ = kosum(g, 8, 1)
    print(f"{ad:<22s} {sum(len(t) for t in g):5d} {tek:9d} {tik:13d} "
          f"{ortusen:8d} {tek - tik:7d} {(tek - tik) / tek:7.4f}")

print()
print("alt sınır ile ulaşılan tik")
print(f"{'yük':<22s} {'işlemci adım':>13s} {'alt sınır':>10s} {'ulaşılan':>9s} "
      f"{'fark':>5s}")
for ad, pay in YUKLER:
    g = gorevler(gc_payi=pay)
    tik, _, islemci_adim, _ = kosum(g, 8, 1)
    print(f"{ad:<22s} {islemci_adim:13d} {islemci_adim:10d} {tik:9d} "
          f"{tik - islemci_adim:5d}")

print()
print("işçi sayısı arttıkça (yuva hep bir)")
print(f"{'işçi':>5s} " + " ".join(f"{ad:>25s}" for ad, _ in YUKLER))
print(f"{'':5s} " + " ".join(f"{'tik / örtüşen':>25s}" for _ in YUKLER))
for isci in (1, 2, 4, 8):
    hucre = []
    for ad, pay in YUKLER:
        tik, ortusen, _, _ = kosum(gorevler(gc_payi=pay), isci, 1)
        hucre.append(f"{f'{tik} / {ortusen}':>25s}")
    print(f"{isci:5d} " + " ".join(hucre))

print()
g_gc, g_ic = gorevler(gc_payi=7), gorevler(gc_payi=1)
k_gc = kosum(g_gc, 1, 1)[0] - kosum(g_gc, 8, 1)[0]
k_ic = kosum(g_ic, 1, 1)[0] - kosum(g_ic, 8, 1)[0]
print(f"aynı düzenek, iki yük: kazanç {k_gc} tike karşı {k_ic} tik, "
      f"oran {k_gc / k_ic:.1f}")
print(f"işlemci ağırlıklı yükte g/ç adımı {kosum(g_ic, 8, 1)[3]}, "
      f"kazanç {k_ic}; girdi/çıktı ağırlıklı yükte g/ç adımı "
      f"{kosum(g_gc, 8, 1)[3]}, kazanç {k_gc}")
```

```
iş parçacığı rejimi: sekiz işçi, tek yuva
yük                     adım  tek akış  iş parçacığı  örtüşen  kazanç    oran
girdi/çıktı ağırlıklı     80        80            31       49      49  0.6125
dengeli                   80        80            42       38      38  0.4750
işlemci ağırlıklı         80        80            73        7       7  0.0875

alt sınır ile ulaşılan tik
yük                     işlemci adım  alt sınır  ulaşılan  fark
girdi/çıktı ağırlıklı             26         26        31     5
dengeli                           40         40        42     2
işlemci ağırlıklı                 73         73        73     0

işçi sayısı arttıkça (yuva hep bir)
 işçi     girdi/çıktı ağırlıklı                   dengeli         işlemci ağırlıklı
                  tik / örtüşen             tik / örtüşen             tik / örtüşen
    1                    80 / 0                    80 / 0                    80 / 0
    2                   44 / 36                   52 / 28                    73 / 7
    4                   34 / 46                   44 / 36                    73 / 7
    8                   31 / 49                   42 / 38                    73 / 7

aynı düzenek, iki yük: kazanç 49 tike karşı 7 tik, oran 7.0
işlemci ağırlıklı yükte g/ç adımı 7, kazanç 7; girdi/çıktı ağırlıklı yükte g/ç adımı 54, kazanç 49
```

#### Aynı Düzenek, Yedi Kat Fark

Üst tablonun iki ucu bu dersin bütün iddiasını taşıyor.

**Girdi/çıktı ağırlıklı yükte** sekiz işçi ve tek yuva, seksen adımı **31** tikte bitiriyor.
Kazanç **49 tik**, oran **0,6125**. Örtüşen adım da **49**: tik sayısının toplam adımdan
örtüşen adımın çıkarılmasıyla bulunduğu kimlik burada birebir görünüyor.

**İşlemci ağırlıklı yükte** aynı düzenek seksen adımı ancak **73** tikte bitiriyor. Kazanç
**7 tik**, oran **0,0875**.

Değişen tek şey adımların türü. Görev sayısı aynı, adım sayısı aynı, işçi sayısı aynı, yuva
aynı, çizelgeleyici aynı. Son satırın verdiği oran **7,0** — kazanç arasındaki fark tam yedi
kat.

Arada duran **dengeli** yük iki ucun aynı eğrinin üstünde olduğunu gösteriyor: 80 tik
**42**'ye iniyor, kazanç **38**, oran **0,4750**. Girdi/çıktı payı onda yediden onda beşe
düştüğünde kazanç 49'dan 38'e iniyor; onda bire düştüğünde 7'ye. Kazanç, girdi/çıktı payının
kesikli bir işlevi gibi davranıyor ve üç ölçüm noktası da aynı yönü veriyor.

Bunun okunuşu şudur: **eşzamanlılık bir hızlandırma düzeneği değildir.** Aynı rejim bir yükte
işin yarıdan fazlasını örtüştürür, öbüründe onda birine bile yaklaşamaz. Bir programa iş
parçacığı eklenip kazanç görülmediğinde eksik olan şey iş parçacığı sayısı değil, o programda
**örtüşebilen adım** olabilir.

#### Kazancın Üst Sınırı Kodda Yazılı

Son satır kazancın nereden geldiğini de söylüyor ve iki sayıyı yan yana koyuyor.

İşlemci ağırlıklı yükte **girdi/çıktı adımı 7**, kazanç **7**. İki sayı eşit. Girdi/çıktı
ağırlıklı yükte girdi/çıktı adımı **54**, kazanç **49** — kazanç, örtüşebilen adım sayısının
biraz altında ama onu aşmıyor.

Kural şudur: **kazanç, örtüşebilen adım sayısını geçemez.** Tek yuvalı bir rejimde işlemci
adımları sırayla atılmak zorunda olduğuna göre, tik sayısından düşülebilecek tek şey
girdi/çıktı adımlarıdır. İşlemci ağırlıklı yükte örtüşebilecek yalnız yedi adım var; sekiz
değil, seksen hiç değil.

Orta tablo aynı sınırı öbür uçtan gösteriyor. Alt sınır, önceki derste kurulan kuraldı: tek
yuvada tik sayısı işlemci adımı sayısının altına inemez. Girdi/çıktı ağırlıklı yükte alt sınır
**26**, ulaşılan **31**; fark **5**. İşlemci ağırlıklı yükte alt sınır **73**, ulaşılan
**73**; fark **0**.

Son satır önemli: işlemci ağırlıklı yükte rejim alt sınıra **tam olarak oturuyor**. Daha iyisi
mümkün değil, çünkü 73 işlemci adımının her biri kendi tikini istiyor. Bu yükte iyileştirme
arayan biri iş parçacığı sayısıyla oynayarak hiçbir şey elde edemez; değiştirmesi gereken şey
**yuva sayısıdır** ve o, iş parçacığının değiştirebileceği bir sayı değildir.

Girdi/çıktı ağırlıklı yükteki **5** tiklik fark de anlamlı: 31 tikin 26'sı bir işlemci adımı
taşıyor, kalan 5'i yalnız girdi/çıktı adımı taşıyor. O beş tikte yuva boş duruyor, çünkü o
anda sırasında işlemci adımı olan canlı görev yok. Yuvanın boş durması bir kayıp gibi görünse
de düzeltilebilir bir şey değil: hangi görevin hangi tikte işlemci adımı isteyeceği adım
dizisine yazılıdır, çizelgeleyicinin seçebileceği bir şey değildir.

#### İşçi Eklemenin Nerede Durduğu

Alt tablo işçi sayısını 1'den 8'e çıkarıyor ve üç yükte üç ayrı davranış veriyor.

**İşlemci ağırlıklı yükte** ikinci işçi 80 tiki **73**'e indiriyor; dördüncü ve sekizinci işçi
hiçbir şey eklemiyor. Tik **73**'te, örtüşen adım **7**'de donuyor. Yedi adım örtüşebiliyordu,
yedisi de ikinci işçiyle örtüştü; üçüncüsüne iş kalmadı. **Bu yükte iş parçacığı sayısını
artırmanın sayılabilir bir karşılığı yok.**

**Girdi/çıktı ağırlıklı yükte** eğri hiç düz değil ama azalan bir eğri: ikinci işçi 36 tik,
üçüncü ve dördüncü birlikte 10 tik, beşinciden sekizinciye kadar dördü birlikte 3 tik
kazandırıyor. Kazanç işçi sayısıyla doğrusal büyümüyor; ilk işçiler örtüşebilen adımların
çoğunu zaten kapıyor.

**Dengeli yükte** aynı biçim daha erken duruyor: 80'den 52'ye, sonra 44'e, sonra 42'ye. Sekiz
işçiye çıkmak dört işçiye göre yalnız iki tik kazandırıyor.

Tablonun ilk satırı da bilgi taşıyor: tek işçiyle üç yükün üçü de **80 tik / 0 örtüşen adım**
veriyor. Yani sekiz görevi tek işçiyle koşturmak, rejim adı ne olursa olsun tek akıştır. Bir
programda iş parçacığı havuzunun genişliği bire indirildiğinde ölçülen şey eşzamanlılık
değil, yalnız sıradır. Havuz genişliğini ölçüme yazmayan bir karşılaştırma bu yüzden
okunamaz.

Üç sütunun ortak dersi şu: **işçi eklemek, örtüşebilen adım tükenene kadar kazandırır; sonra
durur.** Nerede duracağını belirleyen sayı işçi sayısı değil, yükün bileşimidir. Ve bu
tablonun hiçbir yerinde işlemci ağırlıklı sütun 73'ün altına inmiyor — o sınırı aşmanın yolu
bu dersin rejiminde yok.

#### Modelin Dışında Bıraktıkları

Bir ölçüm neyi saydığını söylemedikçe okunamaz, bu yüzden bu modelin **saymadığı** iki şeyi
adıyla yazmak gerekir.

**Bağlam değiştirme bedeli sıfır sayılıyor.** Gerçek bir sistemde bir iş parçacığından
öbürüne geçmek boşuna değildir; kaydedilecek durum vardır. Model bu bedeli hiç saymıyor, yani
yukarıdaki kazanç sayıları **iyimser taraftan** yazılıdır. Bu, sonuçları bozmuyor: bedel
eklendiğinde kazanç yalnız küçülür, işaret değiştirmez. İşlemci ağırlıklı yükteki **7** tik
zaten küçüktü; bedel eklenirse daha da küçülür ve o yükte iş parçacığının anlamsızlığı
güçlenir.

**Ortak durum yok sayılıyor.** Sekiz görev birbirinden bağımsız; hiçbiri ötekinin yazdığını
okumuyor. Gerçek bir programda paylaşılan bir sözlüğe iki iş parçacığından yazmak, araya
kilit koymayı gerektirir ve kilit beklemesi yeni bir bekleme türü ekler. Bu, modelin ölçtüğü
şeyin dışında kalır ama yönü bellidir: kilit beklemesi **örtüşen adımı azaltır**, çünkü
bekleyen görev o tikte ilerlemez.

İkisinin ortak sonucu şu: ölçülen **49** ve **7**, iş parçacığından beklenebilecek kazancın
üst sınırıdır. Gerçek bir program bu sayılara ancak yaklaşabilir. Bir kazanç iddiası bu
sınırın üstündeyse, ölçümde sayılmayan bir şey vardır.

Buradan ayrı bir uyarı da çıkıyor: iş parçacığı, **bekleyen** adımlar için bir düzenektir.
Bir program bekleme yerine hesap yapıyorsa, kilit yüzünden değil, örtüşecek bir şey olmadığı
için kazanamaz.

#### Özet

- İş parçacığı açmak modelde **işçi** sayısını büyütür, **yuva** sayısını değil; sekiz iş
  parçacığı da aynı yorumlayıcının tek kilidini paylaşır.
- Sekiz işçi ve tek yuva, girdi/çıktı ağırlıklı yükte 80 tiki **31**'e indirir — kazanç
  **49 tik**, oran **0,6125**. Aynı düzenek işlemci ağırlıklı yükte 80'i ancak **73**'e
  indirir — kazanç **7 tik**. Aradaki fark tam **yedi kat**.
- Kazanç, örtüşebilen adım sayısını geçemez: işlemci ağırlıklı yükte girdi/çıktı adımı
  **7**'dir ve kazanç da **7**'dir.
- İşlemci ağırlıklı yükte rejim alt sınıra tam oturur (**73**'e karşı **73**); girdi/çıktı
  ağırlıklı yükte **5** tiklik boşluk kalır ve o beş tikte yuva boş durur.
- İşçi eklemek örtüşebilen adım tükenene kadar kazandırır: işlemci ağırlıklı yükte kazanç
  ikinci işçide donar, girdi/çıktı ağırlıklı yükte sekizinci işçiye kadar azalarak sürer.

#### Sonraki Adım

Bu dersin son sayısı bir sınırı gösteriyor: işlemci ağırlıklı yükte kaç işçi eklenirse
eklensin tik **73**'ün altına inmiyor, çünkü değişmeyen ayar yuva. Sonraki ders o ayarı
değiştirir. Ayrı bir süreç başlatmak ayrı bir yorumlayıcı demektir ve ayrı yorumlayıcı ayrı
bir yuva demektir. Sorusu şu: dört yuva işlemci ağırlıklı yükte seksen adımı kaça indirir ve
yuva sayısı görev sayısına eşitlendiğinde yükün bileşimi hâlâ bir şey söyler mi?

### Çoklu Süreç

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/coklu-surec

Dört yuva işlemci ağırlıklı yükte seksen adımı 20 tike indirir (kazanç 60), sekiz yuvada üç yükün üçü de 10 tik ve 70 örtüşen adım verir; buna karşılık her göreve iki uçta 15'er aktarım adımı eklendiğinde dört yuvanın kazancı 60 tikten 0'a iner.

Önceki ders bir duvara tosladı. İşlemci ağırlıklı yükte işçi sayısını ikiden sekize
çıkarmak tik sayısını **73**'te bıraktı; kazanç ikinci işçide dondu ve bir daha kımıldamadı.
Sebebi tabloda yazılıydı: değişmeyen ayar **yuva**. Yetmiş üç işlemci adımı, bir tikte tek
işlemci adımı atılabildiği sürece yetmiş üç tik ister.

İş parçacığı o ayarı değiştiremez, çünkü sekiz iş parçacığı da aynı yorumlayıcının içindedir.
Değiştirebilecek olan şey **ayrı bir yorumlayıcıdır**. Bu ders o yolu ölçer ve iki soru sorar:
dört yuva seksen adımı kaça indirir ve yuva sayısı görev sayısına eşitlendiğinde yükün
bileşimi hâlâ bir şey söyler mi?

#### Ayrı Süreç, Ayrı Yorumlayıcı

Süreç, kendi adres alanı olan bir yürütme birimidir. Süreç ile iş parçacığının farkı
**İşletim Sistemi Kavramları** kursunda mekanizma olarak kuruldu; burada tekrarlanmaz. Bu
dersin sorusu yine sayı: ayrı süreç başlatmak modelin hangi ayarını değiştirir?

Ayrı süreç, ayrı bir yorumlayıcı demektir. Ayrı yorumlayıcının kendi genel yorumlayıcı kilidi
vardır ve o kilit başkasının kilidiyle ilgilenmez. Dört süreç, dört bağımsız kilit, yani
**dört yuva**. Modeldeki karşılığı doğrudan budur: işlemci yuvası dörde çıkar.

Bu, iş parçacığının yapamadığı şeydir. İş parçacığı işçi ekler, süreç yuva ekler. Önceki
dersin duvarı işçi duvarı değil yuva duvarıydı; bu yüzden aşan şey de süreçtir.

#### Süreçlerin Ödediği Şey Sayılabilir

Ayrı adres alanının bedeli var ve bu bedel kursun ölçü birimiyle yazılabilir.

Aynı süreçteki iki iş parçacığı aynı nesneyi görür; birinin yazdığını öbürü okur. Ayrı iki
süreç görmez. Görevi bir sürece vermek, girdisini o sürece **taşımak**; sonucu almak, geri
taşımak demektir. Taşınan şey aktarılabilir bir gösterime çevrilir, öbür uçta geri kurulur.

Bu iş bir süre değil, bir **adım dizisidir**. Model onu tam olarak böyle sayar: her görevin
iki ucuna eklenen bir miktar işlemci adımı. Aktarım adımı da bir işlemci adımıdır ve yuvayı
kullanır. Ölçümün son bölümü bedelin hangi büyüklükte kazancı yediğini sorar.

Ölçümün varsayımları:

- **EM24** — Çekirdek önceki iki dersin aynısıdır: sekiz görev, görev başına on adım, toplam
  seksen adım; tohum sabit.
- **EM25** — Süreç rejiminin modeldeki karşılığı **yuvanın büyümesidir**; işçi sekizde sabit
  tutulur ki tek değişkenin yuva olduğu görülsün.
- **EM26** — Gerçek süreç başlatılmaz. Başlatılsaydı hangi sürecin ne zaman ilerlediği
  işletim sistemine bağlı olurdu ve çıktı yeniden üretilemezdi.
- **EM27** — Dört yuva, dört ayrı yorumlayıcı demektir; sekiz yuva, görev sayısı kadar
  yorumlayıcı.
- **EM28** — Alt sınır kuralı sürüyor: tik sayısı, işlemci adımı sayısının yuvaya bölümünün
  yukarı yuvarlanmış hâlinin altına inemez.
- **EM29** — Aktarım bedeli, her görevin **iki ucuna** eklenen işlemci adımı sayısıdır: giren
  veri ve dönen sonuç. Bedel bir tam sayıdır ve her görevde aynıdır.
- **EM30** — Aktarım adımı yuvayı kullanır; ayrı bir kaynağı yoktur.
- **EM31** — Aktarım bedelli ve bedelsiz koşumlar aynı görev dizisinden üretilir; karşılaştırma
  tabanı, bedel ödemeyen tek akış rejimidir (**80 tik**).
- **EM32** — Süreç başlatmanın kendisi modelde bedelsizdir; sayılan yalnız veri devridir.
  Gerçek bir sistemde başlatma da bedellidir ve bu, ölçülen kazancı yalnız küçültür.
- **EM33** — Görevler bağımsızdır; süreçler arası ortak durum ve eşgüdüm bu ölçümün
  dışındadır.
- **EM34** — Süre ölçülmez; sayılan birim adım, tik ve örtüşen adımdır.
- **EM35** — Ölçüm tek koşumdur ve üç yük aynı çekirdekten üretilir.

#### Ölçüm

```python
"""Coklu surec: yuva eklemek ve yuvanin gorev sayisina ulasmasi."""

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):
    """Her gorev bir adim dizisi; gc_payi/10 orani girdi/cikti adimi."""
    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):
    """Donen: tik, ortusen adim, islemci adimi, g/c adimi."""
    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 aktarimli(gorevler, bedel):
    """Her gorevin iki ucuna 'bedel' kadar islemci adimi ekler: veri devri."""
    return [[ISLEMCI] * bedel + list(t) + [ISLEMCI] * bedel for t in gorevler]


YUKLER = (("girdi/çıktı ağırlıklı", 7), ("dengeli", 5), ("işlemci ağırlıklı", 1))

print("yuva sayısı arttıkça (işçi hep sekiz)")
print(f"{'yuva':>5s} " + " ".join(f"{ad:>25s}" for ad, _ in YUKLER))
print(f"{'':5s} " + " ".join(f"{'tik / örtüşen':>25s}" for _ in YUKLER))
for yuva in range(1, 9):
    hucre = []
    for ad, pay in YUKLER:
        tik, ortusen, _, _ = kosum(gorevler(gc_payi=pay), 8, yuva)
        hucre.append(f"{f'{tik} / {ortusen}':>25s}")
    print(f"{yuva:5d} " + " ".join(hucre))

g = gorevler(gc_payi=1)
tek = kosum(g, 1, 1)[0]
print()
print(f"işlemci ağırlıklı yük, tek akış {tek} tik")
for yuva in (1, 4, 8):
    tik, ortusen, islemci_adim, _ = kosum(g, 8, yuva)
    print(f"  {yuva} yuva: {tik:3d} tik, {ortusen:3d} örtüşen adım, "
          f"kazanç {tek - tik:3d} tik, alt sınır "
          f"{-(-islemci_adim // yuva):3d}")

print()
print("aktarım bedeli: her göreve iki uçta 'bedel' kadar işlemci adımı")
print(f"{'bedel':>6s} {'adım':>5s} {'dört yuva':>10s} {'örtüşen':>8s} "
      f"{'tek akış':>9s} {'kazanç':>7s}")
for bedel in (0, 1, 2, 4, 8, 15):
    gg = aktarimli(g, bedel)
    tik, ortusen, _, _ = kosum(gg, 8, 4)
    print(f"{bedel:6d} {sum(len(t) for t in gg):5d} {tik:10d} {ortusen:8d} "
          f"{tek:9d} {tek - tik:7d}")
```

```
yuva sayısı arttıkça (işçi hep sekiz)
 yuva     girdi/çıktı ağırlıklı                   dengeli         işlemci ağırlıklı
                  tik / örtüşen             tik / örtüşen             tik / örtüşen
    1                   31 / 49                   42 / 38                    73 / 7
    2                   16 / 64                   23 / 57                   38 / 42
    3                   11 / 69                   18 / 62                   29 / 51
    4                   10 / 70                   16 / 64                   20 / 60
    5                   10 / 70                   12 / 68                   19 / 61
    6                   10 / 70                   10 / 70                   19 / 61
    7                   10 / 70                   10 / 70                   16 / 64
    8                   10 / 70                   10 / 70                   10 / 70

işlemci ağırlıklı yük, tek akış 80 tik
  1 yuva:  73 tik,   7 örtüşen adım, kazanç   7 tik, alt sınır  73
  4 yuva:  20 tik,  60 örtüşen adım, kazanç  60 tik, alt sınır  19
  8 yuva:  10 tik,  70 örtüşen adım, kazanç  70 tik, alt sınır  10

aktarım bedeli: her göreve iki uçta 'bedel' kadar işlemci adımı
 bedel  adım  dört yuva  örtüşen  tek akış  kazanç
     0    80         20       60        80      60
     1    96         24       72        80      56
     2   112         28       84        80      52
     4   144         36      108        80      44
     8   208         52      156        80      28
    15   320         80      240        80       0
```

#### Dört Yuva: Seksenden Yirmiye

Orta blok önceki dersin duvarını yıkıyor. İşlemci ağırlıklı yükte tek yuva **73** tik
veriyordu; dört yuva aynı seksen adımı **20** tikte bitiriyor. Kazanç **60 tik**, örtüşen adım
**60**.

Karşılaştırma dersin bütün noktasını taşıyor. İş parçacığı bu yükte **7** tik kazandırmıştı;
süreç **60** kazandırıyor. Değişen şey görev sayısı değil, işçi sayısı değil, çizelgeleyici
değil — yalnız yuva.

Alt sınır sütunu da yerini buluyor: 73 işlemci adımı dört yuvaya bölündüğünde alt sınır
**19**, ulaşılan **20**. Bir tiklik boşluk kalıyor, çünkü koşumun sonuna doğru yuvaları
dolduracak kadar canlı görev kalmıyor. Sekiz yuvada alt sınır **10** ve ulaşılan da **10** —
bu kez tam oturuyor.

Buradan pratik bir okuma çıkıyor: **işlemci ağırlıklı bir yükte kazanç, yuva sayısına
bölünmeyle gelir.** İşlemci adımı sayısı sabit; onları kaç yuvaya dağıttığınız tik sayısını
belirler. İş parçacığı bu bölmeyi yapamaz.

#### Yuva Görev Sayısına Ulaştığında Yük Kaybolur

Üst tablonun son satırı bu dersin ikinci ölçümü ve kursun dördüncü okuması: **sekiz yuvada
üç yükün üçü de 10 tik ve 70 örtüşen adım veriyor.**

Yukarıdaki yedi satır boyunca üç sütun birbirinden ayrıydı. Tek yuvada 31, 42 ve 73 gibi
üç ayrı sayı vardı; dört yuvada 10, 16 ve 20. Sekizinci satırda üçü de aynı yere düşüyor ve
aradaki bütün fark **kayboluyor**.

Sebebi tanımda: yuva sayısı görev sayısına eşitlendiğinde hiçbir görev yuva bulamadığı için
beklemez. Her tikte canlı olan her görev bir adım atar. Sekiz görevin her biri on adım
taşıdığına göre on tik gerekir ve her tikte sekiz görev ilerlediğinden tik başına yedi örtüşen
adım birikir: 10 tik, 70 örtüşen adım. Adımların **türü** artık hiçbir şey belirlemiyor.

Bu, ölçümün en sık yanlış okunan yeridir. "Girdi/çıktı ağırlıklı iş şu rejimi ister, işlemci
ağırlıklı iş bu rejimi ister" cümlesi doğrudur ama **koşulludur**: kısıt varken doğrudur.
Kısıt kalkınca yükün bileşimi bir ayrım üretmez. Yükün bileşimini sormanın anlamlı olduğu yer,
yuvanın görev sayısından **az** olduğu yerdir — ki gerçek bir makinede her zaman öyledir.

#### Girdi/Çıktı Ağırlıklı Yükte Süreç

Üst tablonun ilk sütunu ayrı bir soruyu yanıtlıyor: süreç, zaten iş parçacığının iyi
çalıştığı yükte ne yapar?

Girdi/çıktı ağırlıklı yükte tek yuva **31** tik veriyordu; dört yuva **10**'a indiriyor.
Kazanç var, ama iki şey dikkat çekiyor. Birincisi, bu yükte dördüncü yuvadan sonra hiçbir
kazanç yok — dört, beş, altı, yedi ve sekiz yuva aynı **10 / 70** satırını veriyor. Kısıt
artık yuva değil, görev başına düşen on adım.

İkincisi ve önemlisi, bu sütun aktarım bedelini içermiyor. Bu yükte işlemci adımı yalnız
**26**; taşınacak veri ise iş parçacığı rejimindekiyle aynı. Dört yuvanın kazandırdığı 21
tike karşılık her göreve iki uçta beşer aktarım adımı eklemek bile hesabı tersine çevirmeye
yeter. Bekleyen bir iş için süreç açmak, **beklemeyi taşıma bedeliyle değiş tokuş etmektir**.

Genel kural olarak yazılabilecek şey şu: yuva eklemenin karşılığını **işlemci adımı sayısı**
belirler, aktarımın bedelini ise **taşınan verinin** boyu. İkisi aynı yükte birbirinden
bağımsız değişir. Süreç, işlemci adımı çok ve taşınan veri az olduğunda kazandırır; ikisi
tersine döndüğünde en pahalı rejimdir.

Eşzamanlılığın birden çok makineye yayılması **Sistem Tasarımı ve Dağıtık Sistemler**
müfredatının konusudur ve burada tekrarlanmaz. Bu kursun ölçtüğü her şey tek makinede, tek
program içinde geçiyor; aktarım bedeli de o sınırın içindeki bir bedeldir. Ayrı süreçlere
dağıtmakla ayrı makinelere dağıtmak arasındaki fark modelde tek bir sayıda toplanır: aktarım
bedelinin büyüklüğü. Yön aynıdır, ölçek değildir.

#### Yuva Eklemek Doğrusal Kazandırmaz

Üst tablonun işlemci ağırlıklı sütunu tek başına okunmayı hak ediyor: 73, 38, 29, 20, 19, 19,
16, 10.

İkinci yuva **35** tik kazandırıyor, üçüncü **9**, dördüncü **9**, beşinci **1**, altıncı
**0**, yedinci **3**, sekizinci **6**. Eğri ne doğrusal ne de düzgün azalan. Altıncı yuva
hiçbir şey kazandırmıyor; yedinci ve sekizinci yeniden kazandırıyor.

Bunun kaynağı kurgunun ayrıntısında: kazanç, o tikte işlemci adımı isteyen görev sayısına
bağlı. Beş yuva varken çoğu tikte zaten beş görev istemiyorsa altıncı yuva boş durur. Sonlara
doğru görevler eşitlenince yeni yuva yeniden iş bulur.

Bunun genel dersi şu: **yuva sayısını iki katına çıkarmak tik sayısını yarıya indirmez.**
Kazanç, adım dizilerinin o anki hizasına bağlıdır ve ancak ölçülerek bilinir. Yuva sayısını
artırırken durulacak yeri belirleyen şey bir formül değil, iki ardışık ölçüm arasındaki
farktır.

#### Aktarım Bedeli Kazancı Yer

Alt tablo süreç rejiminin faturasını kesiyor. Bedel sıfırken dört yuva **20** tik veriyordu ve
kazanç **60**'tı. Her göreve iki uçta birer aktarım adımı eklendiğinde toplam adım 80'den
**96**'ya çıkıyor, tik **24** oluyor, kazanç **56**'ya iniyor. İki adımda kazanç **52**, dört
adımda **44**, sekiz adımda **28**.

On beş adımda kazanç **0**: 320 adımlık iş dört yuvada **80** tikte bitiyor, yani bedel
ödemeyen tek akışın tam olarak bittiği yerde. Bu noktadan sonra süreç rejimi kaybetmeye
başlar.

Sayının okunuşu şudur: **süreç eklemenin kazancı, veri devrinin adım sayısıyla yarışır.**
Görev başına on adımlık bir iş için otuz adımlık devir bedeli kazancı tamamen siler. Bu oran
kurguya bağlıdır ve genel bir eşik değildir; genel olan şey ilişkinin kendisidir. Küçük
görevleri süreçlere dağıtmak, devrin işten büyük olması demektir.

Tablonun örtüşen adım sütunu buradaki tuzağı da gösteriyor. Bedel arttıkça örtüşen adım
**60**'tan **240**'a çıkıyor — sayı büyüyor, kazanç küçülüyor. Örtüşen adım tek başına
okunduğunda yanıltır: eklenen adımlar da örtüşür. Bir ölçümün paydası neyse, okunması gereken
şey odur; burada payda tek akışın **80** tikidir ve karşılaştırma ona göredir.

#### Özet

- Süreç eklemek modelde **yuva** sayısını büyütür: ayrı süreç ayrı yorumlayıcı, ayrı
  yorumlayıcı ayrı kilittir. İş parçacığının yapamadığı budur.
- İşlemci ağırlıklı yükte dört yuva seksen adımı **20** tike indirir; kazanç **60 tik**,
  örtüşen adım **60**. Aynı yükte tek yuvanın kazancı **7** tikti.
- Sekiz yuvada üç yükün üçü de **10 tik / 70 örtüşen adım** verir. Yuva görev sayısına
  ulaştığında işlemci adımı kısıt olmaktan çıkar ve yükün bileşimi sonucu belirlemez.
- Yuva eklemenin kazancı doğrusal değildir: işlemci ağırlıklı yükte ikinci yuva **35**,
  beşinci **1**, altıncı **0** tik kazandırır. Durulacak yer ölçülerek bulunur.
- Her göreve iki uçta **15**'er aktarım adımı eklendiğinde dört yuvanın kazancı **60** tikten
  **0**'a iner: 320 adımlık iş **80** tikte biter, bedelsiz tek akışla aynı yerde.

#### Sonraki Adım

Buraya kadar iki rejim de örtüşmeyi **dışarıdan** aldı: iş parçacığı işletim sisteminden işçi
istedi, süreç ayrı bir yorumlayıcıdan yuva istedi. İkisinde de hangi görevin ne zaman
ilerleyeceğine karar veren şey kodun dışındaydı. Sonraki ders üçüncü yolu ölçer: örtüşmeyi
işletim sisteminden istemeyen, **kendi isteğiyle denetimi bırakan** kod. Sorusu şu — bir eş
yordam denetimi ne sıklıkla bırakırsa ne kadar örtüşme çıkar ve hiç bırakmazsa ne olur?

### Eşzamansız Programlama

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/eszamansiz-programlama

Örtüşmenin kaynağı işletim sistemi değil kodun kendisidir: her adımda denetimi bırakan eş yordam 80 teslimle 31 tik ve 49 örtüşen adım verir, yalnız girdi/çıktı adımında bırakan 54 teslimle 36 tik ve 44 örtüşen adım, hiç bırakmayan 0 teslimle 80 tik ve 0 örtüşen adım.

Önceki iki ders örtüşmeyi hep **dışarıdan** aldı. İş parçacığı işletim sisteminden işçi
istedi, süreç ayrı bir yorumlayıcıdan yuva istedi. İkisinde de hangi görevin ne zaman
ilerleyeceğine karar veren şey kodun dışındaydı; program yalnız kaç akış istediğini söyledi.

Üçüncü bir yol var ve bu ders onu ölçer. **Eşzamansız programlamada** örtüşmenin kaynağı ne
işletim sistemidir ne de ikinci bir yorumlayıcı: **kodun kendisidir.** Bir eş yordam
denetimi bırakacağı yeri kendi yazar. Bu dersin sorusu bu yüzden dar ve sayılabilir: bir eş
yordam denetimi ne sıklıkla bırakırsa ne kadar örtüşme çıkar ve hiç bırakmazsa ne olur?

#### Eş Yordam Sürdürülebilir Bir Gövdedir

**Eş yordam (coroutine)**, çağrıldığında gövdesi koşmayan, çağrıya karşılık bir nesne
döndüren ve **sürdürüldükçe** ilerleyen bir işlevdir. `async def` ile tanımlanır. Gövdesindeki
`await`, bir sonucu bekleyen bir işaret değil, **denetimi bırakma noktasıdır**: eş yordam o
noktada durur, kendini çağıran tarafa döner ve başka bir şeyin ilerlemesine izin verir.

Duraklayan gövde bu kursta ilk kez geçmiyor. **Veri Yapıları ve Fonksiyonel Araçlar** kursu
üreteci ölçtü ve gövdenin `yield`'de durup yerel adlarını koruduğunu gösterdi; o ölçüm burada
tekrarlanmaz. Aradaki fark tek cümlede yazılabilir: **üreteç bir değer vermek için durur, eş
yordam denetimi bırakmak için durur** — birinin duruşu akışa bir öğe ekler, öbürünün duruşu
başkasına sıra verir.

**Olay döngüsü (event loop)** bu duruşları yöneten taraftır. Elinde sürdürülmeyi bekleyen eş
yordamlar vardır; birini sürdürür, o bir teslim noktasına gelip döndüğünde sıradakini sürdürür.
Bir eş yordamı döngüye kaydedip bağımsız ilerlemesini istemek, onu bir **görev** yapmaktır.

#### Olay Döngüsü Yeni Bir Yuva Getirmez

Modelin diliyle söylenirse: eşzamansız rejim ne işçi sayısını ne yuva sayısını işletim
sisteminden ister. Olay döngüsü tek bir akışta koşar; aynı anda tek bir gövde ilerler ve
yuva birdir.

O zaman örtüşme nereden gelir? Tek bir yerden: bir gövde denetimi bıraktığında öbürünün
ilerleyebilmesinden. Bu kursun ölçüsüyle, **teslim noktası** olmadan örtüşen adım da yoktur.
Buradan doğrudan bir üst sınır çıkıyor ve ölçüm onu sınayacak: **örtüşen adım, teslim
sayısını geçemez.**

Bunun tersi de var ve dersin sonunda ölçülüyor: teslim etmek örtüşme için **gerekli** ama
**yeterli değil**. Bırakılan denetimi alacak, o anda hazır bekleyen ikinci bir görev de olmak
zorunda.

#### Ölçüm Kendi Döngüsünü Yazıyor

Ölçümün çizelgeleyicisi burada elle yazılmış bir olay döngüsüdür; eş yordamlar ise gerçektir.
`async def` ile tanımlanmış, gerçek `await` içeren gövdeler, gerçek eş yordam nesneleri
üretiyor ve `send` ile sürdürülüyor. Değiştirilen tek şey **kimin sürdürdüğü**: standart olay
döngüsü yerine sırası yazılı bir döngü. Sebebi ölçümün koşulu — hangi görevin hangi tikte
sürdürüldüğü sabit olmazsa örtüşen adım sayılamaz.

Dersin sonundaki kısa blok gerçek olay döngüsünü çalıştırıyor ve sırayı `await`'in kendisi
sabitliyor.

Ölçümün varsayımları:

- **EM36** — Görev kurgusu önceki derslerin aynısıdır: sekiz görev, görev başına on adım,
  toplam seksen adım, girdi/çıktı ağırlıklı yük.
- **EM37** — Eş yordamlar gerçektir; `async def` ile tanımlanır ve `await` ile durur. Onları
  sürdüren döngü ders içinde yazılmıştır.
- **EM38** — Teslim noktası, eş yordamın olay döngüsüne dönüş sayısıdır; her `await`
  bir teslimdir.
- **EM39** — Üç rejim yalnız teslim noktalarının yerinde ayrılır: her adımda, yalnız
  girdi/çıktı adımında, hiç. Adım dizileri üçünde de aynıdır.
- **EM40** — Olay döngüsü tek akıştır: işlemci yuvası birdir ve değişmez.
- **EM41** — Bir eş yordam denetimi bırakmadan `k` adım atarsa döngü o `k` adım boyunca
  meşguldür; ilk adım sürdürüldüğü tike yazılır, kalan `k-1` adım kimsenin ilerlemediği ayrı
  tiklere.
- **EM42** — Sürdürme sırası görev indisine göredir ve koşum boyunca değişmez.
- **EM43** — Karşılaştırma tabanı önceki derslerin çizelgeleyicisidir: aynı görevler, sekiz
  işçi, tek yuva.
- **EM44** — Gerçek olay döngüsü yalnız **sıralı `await`** ile çalıştırılır; çıktı sırası
  `await`'in kendisiyle sabitlenir, çizelgeleyiciye bırakılmaz.
- **EM45** — `sleep(0)` bu ölçümde bir bekleme değil, bir **teslim** işaretidir; sayılan şey
  süre değil, denetimin kaç kez bırakıldığıdır.
- **EM46** — Süre ölçülmez; sayılan birim adım, tik, örtüşen adım ve teslimdir.
- **EM47** — Ölçüm tek koşumdur ve tohum sabittir.

#### Ölçüm

```python
"""Es yordam: denetimi kim, ne siklikla birakiyor."""

import asyncio

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):
    """Her gorev bir adim dizisi; gc_payi/10 orani girdi/cikti adimi."""
    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):
    """Onceki derslerin cizelgeleyicisi; karsilastirma tabani."""
    kalan = [list(g) for g in gorevler]
    tik = ortusen = 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:
            if kalan[i][0] == ISLEMCI:
                if yuva <= 0:
                    continue
                yuva -= 1
            kalan[i].pop(0)
            ilerleyen += 1
        tik += 1
        ortusen += max(0, ilerleyen - 1)
    return tik, ortusen


class Bekleyis:
    """Es yordamin denetimi olay dongusune biraktigi nokta."""

    def __init__(self, tur):
        self.tur = tur

    def __await__(self):
        return (yield self.tur)


async def her_adimda(adimlar, gunluk):
    """Her adimdan once denetimi birakir: teslim noktasi = adim sayisi."""
    for a in adimlar:
        await Bekleyis(a)
        gunluk.append(a)


async def gc_adiminda(adimlar, gunluk):
    """Yalniz girdi/cikti adiminda birakir; islemci adimlarini pes pese atar."""
    for a in adimlar:
        if a == GC:
            await Bekleyis(a)
        gunluk.append(a)


async def hic(adimlar, gunluk):
    """Denetimi hic birakmaz."""
    for a in adimlar:
        gunluk.append(a)


def dongu(yapici, gorevler, islemci_yuvasi=1):
    """El yazimi belirlenimci olay dongusu. Donen: tik, ortusen, teslim.

    Bir es yordam denetimi birakmadan k adim atarsa dongu o k adim boyunca
    mesguldur: borc olarak k-1 tik daha yazilir ve o tiklerde kimse ilerlemez.
    """
    gunluk = [[] for _ in gorevler]
    coro = [yapici(g, gunluk[i]) for i, g in enumerate(gorevler)]
    istek = [None] * len(coro)
    canli = [True] * len(coro)
    borc = [0] * len(coro)
    teslim = 0

    def surdur(i, pesin=0):
        nonlocal teslim
        onceki = len(gunluk[i])
        try:
            istek[i] = coro[i].send(None)
            teslim += 1
        except StopIteration:
            istek[i] = None
            canli[i] = False
        borc[i] += max(0, len(gunluk[i]) - onceki - pesin)

    for i in range(len(coro)):
        surdur(i)                       # ilk teslim noktasina kadar
    tik = ortusen = 0
    while any(canli) or any(borc):
        mesgul = [i for i in range(len(coro)) if borc[i] > 0]
        if mesgul:
            borc[mesgul[0]] -= 1
            tik += 1
            continue
        yuva, ilerleyen = islemci_yuvasi, 0
        for i in range(len(coro)):
            if not canli[i] or istek[i] is None:
                continue
            if istek[i] == ISLEMCI:
                if yuva <= 0:
                    continue
                yuva -= 1
            surdur(i, 1)
            ilerleyen += 1
        if ilerleyen == 0:
            break
        tik += 1
        ortusen += max(0, ilerleyen - 1)
    return tik, ortusen, teslim


g = gorevler(gc_payi=7)
print("girdi/çıktı ağırlıklı yük, seksen adım, tek yuva")
print(f"{'teslim noktası':<24s} {'teslim':>7s} {'tik':>5s} {'örtüşen':>8s}")
for ad, yapici in (("her adımda", her_adimda),
                   ("yalnız g/ç adımında", gc_adiminda),
                   ("hiç", hic)):
    tik, ortusen, teslim = dongu(yapici, g)
    print(f"{ad:<24s} {teslim:7d} {tik:5d} {ortusen:8d}")

tik, ortusen = kosum(g, 8, 1)
print(f"{'önceki dersin çizelgesi':<24s} {'':>7s} {tik:5d} {ortusen:8d}")


async def basamak(ad, kere, gunluk):
    """Her yinelemede denetimi olay dongusune birakir."""
    for i in range(kere):
        await asyncio.sleep(0)
        gunluk.append(f"{ad}{i}")


async def sirali():
    """Sirali await: ikinci es yordam birincisi bitmeden baslamaz."""
    gunluk = []
    for ad in ("a", "b"):
        await basamak(ad, 3, gunluk)
    return gunluk


print()
gunluk = asyncio.run(sirali())
print("gerçek olay döngüsü, sıralı await:", " ".join(gunluk))
print(f"teslim noktası {len(gunluk)}, örtüşen adım 0 — "
      f"bırakan var, alan yok")
```

```
girdi/çıktı ağırlıklı yük, seksen adım, tek yuva
teslim noktası            teslim   tik  örtüşen
her adımda                    80    31       49
yalnız g/ç adımında           54    36       44
hiç                            0    80        0
önceki dersin çizelgesi             31       49

gerçek olay döngüsü, sıralı await: a0 a1 a2 b0 b1 b2
teslim noktası 6, örtüşen adım 0 — bırakan var, alan yok
```

#### Teslim Sıklığı Örtüşmeyi Belirliyor

Tablo üç satırda tek bir değişkeni değiştiriyor: `await`'in nereye yazıldığı. Adım dizileri
üçünde de aynı, görev sayısı aynı, yuva aynı.

**Her adımda teslim eden** eş yordam **80** kez denetimi bırakıyor ve seksen adımı **31**
tikte bitiriyor; örtüşen adım **49**.

**Yalnız girdi/çıktı adımında teslim eden** eş yordam **54** kez bırakıyor — girdi/çıktı adımı
sayısı kadar. Tik **36**'ya çıkıyor, örtüşen adım **44**'e iniyor. Beş tiklik kayıp, işlemci
adımlarının pes peşe atılmasından geliyor: eş yordam iki girdi/çıktı adımı arasında üç işlemci
adımı taşıyorsa, o üç adımı tek bir sürdürmede yapar ve döngü o sırada kimseye sıra veremez.

**Hiç teslim etmeyen** eş yordam **0** kez bırakıyor ve sonuç **80 tik, 0 örtüşen adım** —
tek akış rejiminin tam olarak kendisi. `async def` ile yazılmış olması hiçbir şey değiştirmiyor.

Üç satırın verdiği sıralama şu: teslim **80 → 54 → 0**, örtüşen adım **49 → 44 → 0**. İlişki
tek yönlü ve üst sınır her satırda korunuyor: **örtüşen adım teslim sayısını hiç geçmiyor.**

Buradan doğrudan bir tasarım sonucu çıkıyor: eşzamansız bir programda örtüşmeyi belirleyen
şey kaç görev başlatıldığı değil, gövdelerin **nerede durduğudur**. `async` sözcüğü bir
teslim noktası üretmez; `await` üretir.

İkinci satırın beş tiklik kaybı ayrıca okunmayı hak ediyor, çünkü gerçek gövdelerin çoğu o
satıra benzer. Kayıp, işlemci adımlarının uzunluğuyla büyür: iki teslim noktası arasında ne
kadar çok adım varsa döngü o kadar uzun meşgul kalır. Bu, eşzamansız kodda uzun hesapların
neden ayrı bir yere taşındığının sayılabilir gerekçesidir — taşınmadıklarında bütün görevleri
birden bekletirler.

#### Aynı Çizelge, Başka Kaynak

Tablonun son satırı bir doğrulama taşıyor ve dersin en dikkate değer sayısı orada.

Önceki derslerin çizelgeleyicisi, aynı görevleri sekiz işçi ve tek yuvayla koşturunca **31
tik / 49 örtüşen adım** veriyordu. Her adımda teslim eden eş yordam rejimi de **31 tik / 49
örtüşen adım** veriyor. **İki sayı birebir aynı.**

Aynılık bir tesadüf değil, tanımın sonucu. İş parçacığı rejiminde bir görev ilerledikten sonra
sıra ötekine geçiyordu; kararı çizelgeleyici veriyordu. Eş yordam rejiminde de bir görev bir
adım atıp duruyor ve sıra ötekine geçiyor; kararı **kod** veriyor. Görülen çizelge aynı,
çizelgeyi yazan taraf başka.

Bu, eşzamansız programlamanın ne kazandırdığını da yerine koyuyor. Kazandırdığı şey daha çok
örtüşme değil — üst satır bunu gösteriyor, sayı aynı. Kazandırdığı şey örtüşmenin **kaynağı**:
işletim sisteminden iş parçacığı istemeden, tek akışta, bırakma noktalarını kodda yazarak aynı
çizelgeyi elde etmek. Bedeli de aynı yerde: bırakma noktalarını yazmayı unutan gövde, üçüncü
satıra düşer.

#### Bırakan Var, Alan Yok

Son iki satır teslim noktasının **yetmediğini** gösteriyor.

Gerçek olay döngüsünde koşan iki eş yordam var; her biri üç kez denetimi bırakıyor, toplam
**6** teslim noktası. Çıktı `a0 a1 a2 b0 b1 b2`. İkinci eş yordam birincisi bitmeden hiç
ilerlemiyor ve örtüşen adım **0**.

Sebebi `await`'in ne yaptığı: `sirali` gövdesi `basamak("a", ...)` üzerinde bekliyor ve o
bitmeden `basamak("b", ...)` **başlatılmamış** oluyor. Denetim bırakılıyor, ama alacak hazır
bir görev yok; döngü aynı eş yordamı geri sürdürüyor.

Buradan iki koşullu bir kural çıkıyor. Örtüşme için **hem** gövdenin denetimi bırakması **hem
de** o anda ilerlemeye hazır ikinci bir görevin bulunması gerekir. Birincisi `await` ile,
ikincisi eş yordamları döngüye **görev olarak kaydetmekle** sağlanır. İkisinden biri eksikse
tablonun üçüncü satırındaki sonuç çıkar: teslim sayısı kaç olursa olsun, örtüşen adım sıfır.

Bu ayrım, eşzamansız kodda en sık yapılan hatanın da açıklamasıdır: gövde `async` yazılmış,
`await` konmuş, hiçbir şey örtüşmemiştir. Ölçülmesi gereken sayı `await` sayısı değil,
**aynı anda ilerlemeye hazır görev sayısıdır**.

#### Teslimin Modelde Sayılmayan Bedeli

Tablonun ilk satırı en çok teslimi ve en çok örtüşmeyi birlikte veriyor; buradan "ne kadar
çok `await` o kadar iyi" sonucu çıkarılabilir. Model bunu doğrular ama model bir şeyi
saymıyor ve bunu yazmak gerekir.

Her teslim bir defter işidir: eş yordamın durumu saklanır, döngünün sırasına geri konur, sonra
yeniden sürdürülür. Modelde bu işin bedeli **sıfır** sayılıyor; sayılan tek şey adımın hangi
tikte atıldığı. Gerçek bir programda teslim bedelsiz değildir ve her adımda teslim eden bir
gövde, kazandığı örtüşmenin bir kısmını deftere geri öder.

Bunun ölçüme etkisi yönlüdür ve okunabilir: **31** tik, eşzamansız rejimden beklenebilecek
kazancın **üst sınırıdır**. Bedel eklendiğinde bu sayı yalnız büyür. İkinci satırdaki **36**
ise iki bedelin arasında duruyor: daha az teslim yazıyor ama işlemci adımlarını pes peşe
attığı için beş tik kaybediyor.

Pratikte yazılan gövdeler ikinci satıra benzer, çünkü `await` bekleyen çağrıların önüne
yazılır, hesap yapan satırların arasına değil. Ölçümün söylediği şey şu: ikinci satır ile
üçüncü satır arasındaki fark — **44** örtüşen adıma karşı **0** — hesap yapan satırların
uzunluğuna değil, aradaki bekleyen çağrının denetimi bırakıp bırakmadığına bağlı. Uzun bir
işlemci bölümü örtüşmeyi biraz azaltır; bırakmayan tek bir çağrı onu sıfırlar.

#### Özet

- Eş yordam, çağrıldığında koşmayan ve sürdürüldükçe ilerleyen bir gövdedir; `await` bir
  bekleme değil, denetimin bırakıldığı **teslim noktasıdır**.
- Olay döngüsü ne işçi ne yuva ekler; tek akışta koşar. Örtüşmenin tek kaynağı gövdelerin
  denetimi bırakmasıdır ve **örtüşen adım teslim sayısını geçemez**.
- Aynı seksen adım, teslim her adımda yazıldığında **80 teslim / 31 tik / 49 örtüşen adım**,
  yalnız girdi/çıktı adımında yazıldığında **54 / 36 / 44**, hiç yazılmadığında **0 / 80 / 0**
  verir.
- Her adımda teslim eden eş yordam rejimi, sekiz işçili tek yuvalı çizelgeyle **birebir aynı**
  sayıyı verir; değişen şey çizelge değil, çizelgeyi yazan taraftır.
- Teslim noktası örtüşme için gerekli ama yeterli değildir: sıralı `await` ile koşan iki eş
  yordam **6** kez denetimi bırakır ve **0** örtüşen adım verir.

#### Sonraki Adım

Tablonun üçüncü satırı bu dersin sonucu ama bir sonraki dersin sorusu. Gövdenin denetimi hiç
bırakmaması her zaman yazarın tercihi değildir: çağrılan bir işlev bırakmayı bilmiyorsa gövde
onu bekler ve döngü durur. Sonraki ders bunu ölçer — akıştaki bir çağrı denetimi bırakmadığında
örtüşen adım kaç düşer, bütün çağrılar bırakmadığında ne kalır ve bırakmayı bilmeyen bir
çağrıyı akışa geri sokmanın bedeli kaç adımdır?

### Eşzamansız Kütüphane Uyumu

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/eszamansiz-kutuphane-uyumu

Her görevde tek bir engelleyen çağrı örtüşen adımı 44'ten 37'ye düşürür, sekiz görevin yedisi engellediğinde örtüşme 6 teslime rağmen 0'a iner; çağrıyı yürütücü havuzuna devretmek örtüşmeyi 44'e döndürür ama bedelin görev başına mı çağrı başına mı düştüğü tik sayısını 44 ile 90 arasında ayırır.

Önceki dersin tablosunda üç satır vardı ve üçüncüsü şuydu: denetimi hiç bırakmayan eş yordam
**0 teslim, 80 tik, 0 örtüşen adım**. `async def` ile yazılmış, gerçek bir eş yordamdı ve
sonucu tek akıştan ayırt edilemiyordu.

O satır bir yazım hatası gibi görünür ama pratikte hiç öyle çıkmaz. Gövdeyi yazan kişi
genellikle `await` yazmayı unutmaz; unutan **çağırdığı işlevdir**. Denetimi bırakmayı bilmeyen
bir çağrı, onu çağıran eş yordamın da bırakmasını engeller. Bu dersin ölçtüğü şey o çağrının
faturasıdır: akıştaki bir çağrı denetimi bırakmadığında örtüşen adım kaç düşer, bütün çağrılar
bırakmadığında ne kalır ve bırakmayı bilmeyen bir çağrıyı akışa geri sokmanın bedeli kaç
adımdır?

#### Engelleyen Kod Teslim Noktası Üretmeyen Koddur

**Engelleyen kod (blocking code)**, denetimi olay döngüsüne geri vermeden işini bitiren
koddur. Adı yanıltıcı olabilir: engellenen şey çağıran gövde değil, **döngünün kendisidir.**
Gövde zaten sonucu bekleyecekti; asıl kayıp, o beklerken başka hiçbir görevin ilerleyememesi.

Bir çağrının engelleyen olup olmadığı ne yaptığından değil, **nasıl yazıldığından** gelir.
Aynı iş — bir dosyayı okumak, bir yanıtı beklemek — teslim noktası üreten bir biçimde de
yazılabilir, üretmeyen bir biçimde de. Eşzamansız bir akışta ikisi arasındaki fark, önceki
dersin tablosunda ölçülen farkın kendisidir.

Buradan bu dersin dar sorusu doğuyor. Bir programda çağrıların hepsi birden değişmez; kimi
teslim eder, kimi etmez. Karışım hâlinde ne çıkar?

#### Bir Çağrı Yeri, Sekiz Görev

Ölçüm iki ekseni ayırıyor, çünkü ikisi karıştırıldığında sonuç okunamaz hâle geliyor.

**Birinci eksen çağrı sayısı:** her görevin akışındaki kaç girdi/çıktı çağrısının engellediği.
Bir görevin akışında on adım var ve bunların bir kısmı girdi/çıktı adımı; bu adımların kaçının
teslim etmeyen bir çağrıdan geçtiği ölçülüyor. Burada dikkat edilecek nokta şu: kodda **tek
bir çağrı yeri** engelleyen olabilir ama o çağrı yerinden sekiz görev de geçer. Bir satırlık
kusur, sekiz akışa birden yazılır.

**İkinci eksen görev sayısı:** kaç görevin baştan sona engelleyen çağrılar kullandığı. Bir
programın bir bölümü eşzamansız yazılmışken bir bölümünün eski biçimde kalması bu eksende
ölçülür.

Ölçümün varsayımları:

- **EM48** — Görev kurgusu önceki derslerin aynısıdır: sekiz görev, görev başına on adım,
  toplam seksen adım, girdi/çıktı ağırlıklı yük, tek yuva.
- **EM49** — Eş yordamlar ve onları sürdüren döngü önceki dersin aynısıdır; değişen tek şey
  gövdenin hangi adımda teslim ettiğidir.
- **EM50** — Uyumlu çağrı, girdi/çıktı adımında denetimi bırakan çağrıdır. Engelleyen çağrı,
  aynı adımı denetimi bırakmadan atan çağrıdır. **İkisi de aynı işi yapar ve aynı adımı
  harcar.**
- **EM51** — İşlemci adımları hiçbir rejimde teslim etmez; ölçülen değişken yalnız girdi/çıktı
  çağrılarının biçimidir.
- **EM52** — Birinci eksende her görevin **ilk** `k` girdi/çıktı çağrısı engeller; hangi
  çağrıların seçildiği kurguya yazılıdır ve koşumlar arasında değişmez.
- **EM53** — İkinci eksende ilk `k` görev baştan sona engeller, kalanı uyumludur.
- **EM54** — Yürütücü havuzuna devir modelde **eklenen bir işlemci adımıdır**: iş havuzda
  koşar, döngü teslim noktasını geri kazanır, karşılığında bir adım daha sayılır.
- **EM55** — Devir bedelinin iki yerleşimi ölçülür: görev başına bir kez ve çağrı başına bir
  kez. İkisi de aynı işi devreder, yalnız bedelin düştüğü yer değişir.
- **EM56** — Havuzun genişliği bu ölçümde sınırsızdır; havuz genişliğinin etkisi sonraki
  dersin konusudur.
- **EM57** — Süre ölçülmez; sayılan birim adım, tik, örtüşen adım ve teslimdir.
- **EM58** — Ölçüm tek koşumdur ve tohum sabittir.

#### Ölçüm

```python
"""Engelleyen kod: teslim etmeyen bir cagri ortusmeyi ne kadar goturur."""

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):
    """Her gorev bir adim dizisi; gc_payi/10 orani girdi/cikti adimi."""
    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


class Bekleyis:
    """Es yordamin denetimi olay dongusune biraktigi nokta."""

    def __init__(self, tur):
        self.tur = tur

    def __await__(self):
        return (yield self.tur)


def uyumlu_yapici(engelleyen_cagri=0):
    """Her gorevin ilk 'engelleyen_cagri' g/c cagrisi teslim etmez."""
    async def gorev(adimlar, gunluk):
        kalan = engelleyen_cagri
        for a in adimlar:
            if a == GC and kalan > 0:
                kalan -= 1              # engelleyen cagri: denetim birakilmaz
            elif a == GC:
                await Bekleyis(a)       # uyumlu cagri: denetim birakilir
            gunluk.append(a)
    return gorev


def karma_yapici(engelleyen_gorev=0):
    """Ilk 'engelleyen_gorev' gorev hic teslim etmez, kalani uyumludur."""
    uyumlu = uyumlu_yapici(0)

    def yapici(adimlar, gunluk):
        yapici.sira += 1
        if yapici.sira <= engelleyen_gorev:
            return uyumlu_yapici(len(adimlar))(adimlar, gunluk)
        return uyumlu(adimlar, gunluk)
    yapici.sira = 0
    return yapici


def dongu(yapici, gorevler, islemci_yuvasi=1):
    """El yazimi belirlenimci olay dongusu. Donen: tik, ortusen, teslim."""
    gunluk = [[] for _ in gorevler]
    coro = [yapici(g, gunluk[i]) for i, g in enumerate(gorevler)]
    istek = [None] * len(coro)
    canli = [True] * len(coro)
    borc = [0] * len(coro)
    teslim = 0

    def surdur(i, pesin=0):
        nonlocal teslim
        onceki = len(gunluk[i])
        try:
            istek[i] = coro[i].send(None)
            teslim += 1
        except StopIteration:
            istek[i] = None
            canli[i] = False
        borc[i] += max(0, len(gunluk[i]) - onceki - pesin)

    for i in range(len(coro)):
        surdur(i)
    tik = ortusen = 0
    while any(canli) or any(borc):
        mesgul = [i for i in range(len(coro)) if borc[i] > 0]
        if mesgul:
            borc[mesgul[0]] -= 1
            tik += 1
            continue
        yuva, ilerleyen = islemci_yuvasi, 0
        for i in range(len(coro)):
            if not canli[i] or istek[i] is None:
                continue
            if istek[i] == ISLEMCI:
                if yuva <= 0:
                    continue
                yuva -= 1
            surdur(i, 1)
            ilerleyen += 1
        if ilerleyen == 0:
            break
        tik += 1
        ortusen += max(0, ilerleyen - 1)
    return tik, ortusen, teslim


g = gorevler(gc_payi=7)
print("sekiz görev, seksen adım, girdi/çıktı ağırlıklı yük, tek yuva")
print()
print("A — her görevde kaç girdi/çıktı çağrısı engelliyor")
print(f"{'engelleyen çağrı':>17s} {'teslim':>7s} {'tik':>5s} {'örtüşen':>8s}")
for k in range(9):
    tik, ortusen, teslim = dongu(uyumlu_yapici(k), g)
    print(f"{k:17d} {teslim:7d} {tik:5d} {ortusen:8d}")

print()
print("B — kaç görev baştan sona engelliyor")
print(f"{'engelleyen görev':>17s} {'teslim':>7s} {'tik':>5s} {'örtüşen':>8s}")
for k in range(9):
    tik, ortusen, teslim = dongu(karma_yapici(k), g)
    print(f"{k:17d} {teslim:7d} {tik:5d} {ortusen:8d}")

print()
print("C — engelleyen çağrıyı yürütücü havuzuna devretmek")
devir_gorev = [[ISLEMCI] + list(t) for t in g]
devir_cagri = [[s for a in t for s in ([ISLEMCI, GC] if a == GC else [a])]
               for t in g]
print(f"{'rejim':<28s} {'adım':>5s} {'teslim':>7s} {'tik':>5s} {'örtüşen':>8s}")
for ad, dizi, yapici in (
        ("hepsi engelliyor", g, uyumlu_yapici(10)),
        ("hepsi uyumlu", g, uyumlu_yapici(0)),
        ("devir görev başına bir kez", devir_gorev, uyumlu_yapici(0)),
        ("devir çağrı başına", devir_cagri, uyumlu_yapici(0))):
    tik, ortusen, teslim = dongu(yapici, dizi)
    print(f"{ad:<28s} {sum(len(t) for t in dizi):5d} {teslim:7d} "
          f"{tik:5d} {ortusen:8d}")
```

```
sekiz görev, seksen adım, girdi/çıktı ağırlıklı yük, tek yuva

A — her görevde kaç girdi/çıktı çağrısı engelliyor
 engelleyen çağrı  teslim   tik  örtüşen
                0      54    36       44
                1      46    43       37
                2      38    50       30
                3      30    57       23
                4      22    64       16
                5      14    71        9
                6       8    76        4
                7       5    78        2
                8       2    80        0

B — kaç görev baştan sona engelliyor
 engelleyen görev  teslim   tik  örtüşen
                0      54    36       44
                1      46    44       36
                2      40    50       30
                3      30    58       22
                4      22    64       16
                5      17    69       11
                6      11    75        5
                7       6    80        0
                8       0    80        0

C — engelleyen çağrıyı yürütücü havuzuna devretmek
rejim                         adım  teslim   tik  örtüşen
hepsi engelliyor                80       0    80        0
hepsi uyumlu                    80      54    36       44
devir görev başına bir kez      88      54    44       44
devir çağrı başına             134      54    90       44
```

#### Tek Bir Çağrının Bedeli

A tablosunun ilk iki satırı bu dersin en kısa sonucunu veriyor. Bütün çağrılar uyumluyken
**36 tik / 44 örtüşen adım**. Her görevde **tek bir** girdi/çıktı çağrısı engellediğinde
**43 tik / 37 örtüşen adım**.

Tek çağrı, yedi tik ve yedi örtüşen adım götürüyor. Kodda değişen şey bir satır; ölçümde
değişen şey örtüşmenin altıda biri.

Kayıp oranı, engelleyen çağrının payından **büyük**. Elli dört girdi/çıktı çağrısının sekizi
engelliyor, yani çağrıların yaklaşık yedide biri; örtüşen adımdaki kayıp ise 44'ün 7'si.
Sebebi engelleyen çağrının kendi görevini değil, **o tikte ilerleyebilecek herkesi**
durdurması. Bir görev engellerken öbür yedisi hazır bekliyor olabilir; ilerlemiyorlar.

Tablonun kalanı bu eğrinin sonuna kadar gidiyor: iki çağrıda **30**, dört çağrıda **16**,
altı çağrıda **4** örtüşen adım. Sekiz çağrıda — yani her görevin bütün girdi/çıktı çağrıları
engellediğinde — örtüşen adım **0** ve tik **80**. Rejim tek akışa dönüyor.

Bu son satır, dersin başlığındaki uyumun ne demek olduğunu tanımlıyor: eşzamansız bir akışın
sayısı, akıştaki **en engelleyen** parçaya göre belirleniyor.

#### Yedi Görev Yeter

B tablosu aynı çöküşü öbür eksende gösteriyor ve bir satırı ötekilerden ayrılıyor.

**Yedi görev** engellediğinde tik **80**, örtüşen adım **0**. Sekizinci görev hâlâ uyumlu;
altı kez denetim bırakıyor. Teslim sütunu bunu yazıyor: **6 teslim, 0 örtüşen adım.**

Önceki dersin kuralı burada tam olarak sınanıyor. Teslim örtüşme için gerekliydi ama yeterli
değildi; ikinci bir hazır görev de gerekiyordu. Yedi görev kendi engelleyen çağrılarında
takılıyken sekizinci görev denetimi bırakıyor ve bırakacağı denetimi alacak kimse bulunmuyor.
Döngü aynı görevi geri sürdürüyor.

Bunun pratik okuması sert: **eşzamansız bir akışı bir tek uyumlu parça kurtarmaz.** Bir kod
tabanının bir bölümünü eşzamansız yazmak, kalan bölüm engellediği sürece ölçülebilir bir şey
kazandırmayabilir. B tablosunun ilk yarısı da aynı yönü gösteriyor: bir görev engellediğinde
kayıp **8** örtüşen adım, ikisinde **14**, üçünde **22**. Kayıp görev sayısıyla birlikte
hızlanarak büyüyor, çünkü her engelleyen görev hem kendi örtüşmesini hem başkalarının
örtüşme fırsatını götürüyor.

#### Engelleyenin Görünmezliği

İki tablonun ortak bir özelliği var ve kusurun neden bu kadar sık kaçtığını açıklıyor: hiçbir
satırda **hata yok**. Sekiz görevin sekizi de bütün adımlarını atıyor, hiçbiri yarım kalmıyor,
hiçbir istisna çıkmıyor. Değişen tek şey adımların hangi tike düştüğü.

Çağrı yerine bakarak da anlaşılmıyor. Uyumlu çağrı ile engelleyen çağrı ölçümde aynı işi
yapıyor ve aynı adımı harcıyor; ayrıldıkları yer gövdenin içinde. Bir işlevin engelleyen olup
olmadığını söyleyen tek şey, çağrıldığında sürdürülebilir bir nesne verip vermediğidir —
`async def` ile tanımlanmış bir işlev bir eş yordam nesnesi döndürür, sıradan bir işlev
doğrudan sonucu.

Bunun doğrudan sonucu şu: eşzamansız bir akışta **`await` yazılmayan her çağrı adaydır.**
Sonucu doğrudan dönen bir çağrı ya hiç beklemiyor ya da bekleyip döngüyü tutuyordur; ikisi
çağrı yerinden ayırt edilemez.

Ölçümün buradaki katkısı, kusurun **belirtisini** adlandırmak. Engelleyen bir çağrı hata
üretmez, çıktıyı bozmaz, günlüğe bir şey yazmaz. Ürettiği tek belirti örtüşen adımın
düşmesidir — ve örtüşen adım sayılmıyorsa hiçbir belirti kalmaz. B tablosunun yedinci satırı
bunun en uç hâli: program eşzamansız yazılmış, altı teslim noktası gerçekten çalışıyor ve
sonuç tek akışla aynı.

#### Havuza Devir ve Bedelin Düştüğü Yer

C tablosu düzeltmeyi ölçüyor. Engelleyen çağrının uyumlu bir karşılığı yoksa, çağrı olduğu
gibi bırakılıp **başka bir yerde** koşturulabilir: bir **yürütücü havuzu**na devredilir, olay
döngüsü o sırada teslim noktasını geri kazanır.

İki uçtaki satırlar bilinen sayılar: hepsi engelliyorsa **80 adım, 0 teslim, 80 tik, 0 örtüşen
adım**; hepsi uyumluysa **80 adım, 54 teslim, 36 tik, 44 örtüşen adım**.

Devir iki satır ekliyor ve ikisinin de örtüşen adımı **44** — devir örtüşmeyi tam olarak geri
kazandırıyor. Ayrıldıkları yer tik sayısı.

**Devir bedeli görev başına bir kez** düştüğünde toplam adım 80'den **88**'e çıkıyor ve tik
**44** oluyor. Engelleyen rejimin **80** tikine göre yarı yarıya kazanç, uyumlu rejimin
**36** tikine göre sekiz tik geride. Bedel sekiz adım, kayıp sekiz tik; hesap birebir.

**Devir bedeli çağrı başına** düştüğünde toplam adım **134**'e çıkıyor ve tik **90** oluyor.
Bu satır dersin en dikkat çekici sayısı: **90, hiçbir şey yapmayıp engellemenin 80 tikinden
büyük.** Örtüşen adım 0'dan 44'e çıkmış olmasına rağmen toplam kötüleşmiş.

Çelişki değil, aritmetik. Devredilen her çağrı bir adım ekliyor ve elli dört çağrı elli dört
adım ediyor; kazanılan kırk dört örtüşen adım bu ekten küçük. Örtüşen adımı tek başına okumak
bu yüzden yanıltır — üçüncü dersin aktarım tablosunda da aynı tuzak vardı. **Bir ölçüm, hangi
adımların sayıya girdiğini söylemeden karşılaştırılamaz.**

Buradan çıkan yönerge somut: devri **çağrı başına** değil, mümkün olan en büyük parça başına
yapmak. Sekiz görevi tek tek devretmek sekiz adım, her çağrıyı ayrı devretmek elli dört adım
ediyor. Devredilecek işin büyüklüğü, devrin kazanıp kazanmayacağını doğrudan belirliyor.

Üç satırın birlikte verdiği sıra da bir öncelik kuralı öneriyor. En iyi sonuç uyumlu çağrıya
geçmek (**36** tik), ikinci en iyi büyük parçalar hâlinde devretmek (**44**), en kötüsü ise
her çağrıyı ayrı devretmek (**90**). Havuz, uyumlu çağrının yerini tutan bir çözüm değil,
uyumlu karşılığı bulunmadığında başvurulan bir çözümdür ve bedeli her zaman sayılabilir.

#### Özet

- Engelleyen kod, denetimi olay döngüsüne geri vermeyen koddur; engellediği şey çağıran gövde
  değil **döngünün kendisidir**.
- Her görevde tek bir engelleyen girdi/çıktı çağrısı, örtüşen adımı **44**'ten **37**'ye
  düşürür ve tik'i **36**'dan **43**'e çıkarır; bütün çağrılar engellediğinde sonuç **80 tik /
  0 örtüşen adım**, yani tek akıştır.
- Sekiz görevin **yedisi** engellediğinde örtüşen adım **0**'dır; sekizinci görev **6** kez
  denetim bıraksa da bırakılan denetimi alacak hazır görev yoktur.
- Çağrıyı yürütücü havuzuna devretmek örtüşen adımı **44**'e geri getirir; devir bedeli görev
  başına düştüğünde tik **44**, çağrı başına düştüğünde **90** olur.
- Çağrı başına devir, hiç düzeltmemekten daha çok tik üretir (**90**'a karşı **80**): kazanılan
  örtüşme, eklenen adım sayısından küçük kaldığında ölçüm ters işaret verir.

#### Sonraki Adım

Bu dersin son bölümü havuzu bir düzeltme aracı olarak kullandı ve genişliğini hiç sormadı;
havuz sınırsız sayıldı. Gerçek bir havuzun genişliği vardır ve arkasında ne olduğu — iş
parçacığı mı, süreç mi — ölçümü değiştirir. Sonraki ders bunu sorar: aynı arayüzle iş
dağıtmak, arkasında iş parçacığı varken ve süreç varken **aynı görevde** hangi sayıları verir
ve arayüzün aynı olması seçimin de aynı olduğu anlamına gelir mi?

### Yürütücü Havuzları

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/yurutucu-havuzlari

Aynı arayüz iki arka uçta aynı görevi ayrı sayılarla bitirir: sekiz genişlikte iş parçacığı havuzu üç yükte 31, 42 ve 73 tik verirken süreç havuzu üçünde de 10 tik ve 70 örtüşen adım verir; arayüzün aynı olması seçimin de aynı olduğu anlamına gelmez.

Önceki ders engelleyen bir çağrıyı bir yürütücü havuzuna devretti ve havuzun genişliğini hiç
sormadı; ölçümde havuz sınırsız sayılmıştı. Gerçek bir havuzun genişliği vardır ve arkasında
ne olduğu — iş parçacığı mı, süreç mi — ölçümü değiştirir.

Bu ders o soruyu sorar. **Yürütücü havuzu (executor pool)** iş dağıtmanın ortak arayüzüdür:
aynı çağrı biçimi, arkasında iş parçacığı da olsa süreç de olsa aynı yazılır. Bu dersin ölçtüğü
şey arayüzün altındaki fark: aynı görev, aynı genişlik, iki arka uç — hangi sayılar çıkar?

#### Ortak Arayüz

Yürütücü havuzu üç şey verir. Bir iş gönderilir ve karşılığında **sonuç nesnesi** alınır; bir
işlev bir dizi girdi üzerinde eşlenir ve sonuçlar **girdi sırasıyla** döner; havuz kapatılır ve
gönderilmiş işler tamamlanır. Bu üç işlem iki arka uçta da aynı adla, aynı imzayla çağrılır.

Arayüzün taşıdığı bir güvence bu dersin ölçüm koşulunu da veriyor: eşleme işleminin çıktısı
girdinin sırasına bağlıdır, çizelgelemeye değil. Hangi işin hangi işçide ne zaman koştuğu
belirsiz olsa da sonuç listesi belirlenimcidir. Aşağıdaki ilk blok bunu gösteriyor.

Kuyruk, arka plan işçisi ve geri basınç **Önbellekleme, Kuyruklar ve Eşzamansız İşleme**
kursunda servis tarafında kuruldu; burada tekrarlanmaz. Buradaki havuz bir servis bileşeni
değil, tek bir programın içindeki bir iş dağıtım arayüzüdür.

#### Arayüz Aynı, Arka Uç İki Sayı

Modelin diliyle iki arka uç arasındaki fark tek bir yerde toplanıyor.

**İş parçacığı arka ucu** genişlik kadar **işçi** verir ama tek bir yorumlayıcının içindedir;
**yuva birde kalır**. **Süreç arka ucu** genişlik kadar işçi verir ve her biri ayrı bir
yorumlayıcı olduğu için **yuva da genişlik kadardır**.

İkinci dersin ve üçüncü dersin sonuçları bu tanımın içinde duruyor. Yeni olan şey, ikisinin
**aynı arayüzün** arkasında olması: çağıran taraf tek bir satır değiştirerek birinden öbürüne
geçer ve sayılar değişir.

Ölçümün varsayımları:

- **EM59** — Görev kurgusu önceki derslerin aynısıdır: sekiz görev, görev başına on adım,
  toplam seksen adım; üç yük yalnız girdi/çıktı payıyla ayrılır.
- **EM60** — İş parçacığı havuzunun modeldeki karşılığı **işçi = genişlik, yuva = 1**'dir.
- **EM61** — Süreç havuzunun modeldeki karşılığı **işçi = genişlik, yuva = genişlik**'tir.
- **EM62** — Gerçek süreç başlatılmaz. Gerçek iş parçacığı yalnız arayüzü göstermek için
  kullanılır ve verilen iş **saf**tır: aynı girdiye aynı sonucu verir, yan etkisi yoktur ve
  çıktının sırası eşleme işleminin sözleşmesinden gelir, çizelgeden değil.
- **EM63** — Ölçüm hiçbir yerde süre saymaz; havuzun karşılaştırıldığı birim tik ve örtüşen
  adımdır.
- **EM64** — Görevler bağımsızdır; havuza gönderilen işler birbirini beklemez.
- **EM65** — Havuza iş göndermenin kendisi bu ölçümde bedelsizdir.
- **EM66** — Süreç arka ucunun veri devri bedeli bu ölçüme **dahil değildir**; o bedel üçüncü
  derste ayrıca ölçüldü. Buradaki süreç sütunu bu yüzden bir **üst sınırdır**.
- **EM67** — Genişlik taraması aynı görev dizisiyle yapılır; yalnız genişlik değişir.
- **EM68** — Genişlik hiçbir koşumda görev sayısını aşmaz.
- **EM69** — Son satırdaki sayım, üç yükün verdiği **ayrı** tik sayısıdır; kaç ayrı sonuç
  çıktığını sayar, sonuçların büyüklüğünü değil.
- **EM70** — Ölçüm tek koşumdur ve tohum sabittir.

#### Ölçüm

```python
"""Yurutucu havuzlari: ayni arayuz, iki arka uc, ayni gorevde ayri ortusme."""

from concurrent.futures import ThreadPoolExecutor

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):
    """Her gorev bir adim dizisi; gc_payi/10 orani girdi/cikti adimi."""
    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):
    """Donen: tik, ortusen adim, islemci adimi, g/c adimi."""
    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 havuz(arka_uc, genislik):
    """Arayuz ayni, arka uc iki ayarla anlatilir: isci ve yuva.

    is parcacigi : genislik kadar isci, tek yuva  (tek yorumlayici)
    surec        : genislik kadar isci, genislik kadar yuva (ayri yorumlayici)
    """
    return (genislik, 1) if arka_uc == "iş parçacığı" else (genislik, genislik)


def kare(n):
    """Havuza verilen is: saf, yan etkisiz, sirasi onemsiz."""
    return n * n


YUKLER = (("girdi/çıktı ağırlıklı", 7), ("dengeli", 5), ("işlemci ağırlıklı", 1))
ARKA = ("iş parçacığı", "süreç")

with ThreadPoolExecutor(max_workers=4) as havuz_nesnesi:
    print("aynı arayüz:", list(havuz_nesnesi.map(kare, range(6))))
print("map çıktısının sırası girişten gelir, çizelgeden değil")

print()
print("genişlik sekiz — aynı görev, iki arka uç")
print(f"{'yük':<22s} " + " ".join(f"{ad:>22s}" for ad in ARKA))
print(f"{'':22s} " + " ".join(f"{'tik / örtüşen':>22s}" for _ in ARKA))
for ad, pay in YUKLER:
    g = gorevler(gc_payi=pay)
    hucre = []
    for arka in ARKA:
        isci, yuva = havuz(arka, 8)
        tik, ortusen, _, _ = kosum(g, isci, yuva)
        hucre.append(f"{f'{tik} / {ortusen}':>22s}")
    print(f"{ad:<22s} " + " ".join(hucre))

for arka in ARKA:
    print()
    print(f"{arka} havuzu, genişlik arttıkça")
    print(f"{'genişlik':>9s} " + " ".join(f"{ad:>22s}" for ad, _ in YUKLER))
    print(f"{'':9s} " + " ".join(f"{'tik / örtüşen':>22s}" for _ in YUKLER))
    for genislik in (1, 2, 4, 8):
        hucre = []
        for ad, pay in YUKLER:
            isci, yuva = havuz(arka, genislik)
            tik, ortusen, _, _ = kosum(gorevler(gc_payi=pay), isci, yuva)
            hucre.append(f"{f'{tik} / {ortusen}':>22s}")
        print(f"{genislik:9d} " + " ".join(hucre))

print()
for arka in ARKA:
    sayi = {kosum(gorevler(gc_payi=pay), *havuz(arka, 8))[0] for _, pay in YUKLER}
    print(f"{arka} havuzu, üç yükün verdiği ayrı tik sayısı: {len(sayi)} "
          f"({sorted(sayi)})")
```

```
aynı arayüz: [0, 1, 4, 9, 16, 25]
map çıktısının sırası girişten gelir, çizelgeden değil

genişlik sekiz — aynı görev, iki arka uç
yük                              iş parçacığı                  süreç
                                tik / örtüşen          tik / örtüşen
girdi/çıktı ağırlıklı                 31 / 49                10 / 70
dengeli                               42 / 38                10 / 70
işlemci ağırlıklı                      73 / 7                10 / 70

iş parçacığı havuzu, genişlik arttıkça
 genişlik  girdi/çıktı ağırlıklı                dengeli      işlemci ağırlıklı
                   tik / örtüşen          tik / örtüşen          tik / örtüşen
        1                 80 / 0                 80 / 0                 80 / 0
        2                44 / 36                52 / 28                 73 / 7
        4                34 / 46                44 / 36                 73 / 7
        8                31 / 49                42 / 38                 73 / 7

süreç havuzu, genişlik arttıkça
 genişlik  girdi/çıktı ağırlıklı                dengeli      işlemci ağırlıklı
                   tik / örtüşen          tik / örtüşen          tik / örtüşen
        1                 80 / 0                 80 / 0                 80 / 0
        2                40 / 40                40 / 40                40 / 40
        4                20 / 60                20 / 60                20 / 60
        8                10 / 70                10 / 70                10 / 70

iş parçacığı havuzu, üç yükün verdiği ayrı tik sayısı: 3 ([31, 42, 73])
süreç havuzu, üç yükün verdiği ayrı tik sayısı: 1 ([10])
```

#### Aynı Görev, İki Sonuç

İlk tablo dersin iddiasını üç satırda veriyor. Sol sütun ile sağ sütun arasındaki tek fark
arka uç; görevler aynı nesne, genişlik aynı sayı, çizelgeleyici aynı işlev.

Girdi/çıktı ağırlıklı yükte iş parçacığı havuzu **31 / 49**, süreç havuzu **10 / 70** veriyor.
Dengeli yükte **42 / 38** ile **10 / 70**. İşlemci ağırlıklı yükte **73 / 7** ile **10 / 70**.

Sol sütun aşağı indikçe kötüleşiyor, sağ sütun hiç değişmiyor. Bunun kaynağı iki ayarın
tanımında: iş parçacığı havuzu yükün işlemci payını **görüyor**, çünkü tek yuvada işlemci
adımları sıraya giriyor. Süreç havuzunda yuva görev sayısına eşit olduğu için işlemci adımı
kısıt olmaktan çıkıyor ve yükün bileşimi görünmez oluyor.

Buradan bu dersin cümlesi çıkıyor: **aynı arayüzün arkasında aynı seçim yoktur.** Bir kod
tabanında havuz nesnesini değiştirmek bir satırlık iştir; ölçümde karşılığı ise yüke göre
hiçbir şeyden yedi kat farka kadar değişir.

#### Genişlik İki Arka Uçta İki Ayrı Biçimde Kazandırır

Alt iki tablo genişliği birden sekize çıkarıyor ve iki apayrı biçim veriyor.

**İş parçacığı havuzunda** her sütun kendi eğrisini izliyor. Girdi/çıktı ağırlıklı sütun
80'den 44'e, 34'e, 31'e iniyor — azalarak kazanıyor. İşlemci ağırlıklı sütun ikinci genişlikte
**73**'e iniyor ve orada duruyor; dört ve sekiz genişlik hiçbir şey eklemiyor. Bu havuzda
genişliği artırmanın karşılığı **yükün girdi/çıktı payı kadardır**.

**Süreç havuzunda** üç sütun da aynı: 80, 40, 20, 10. Genişlik iki katına çıktığında tik yarıya
iniyor ve bu üç yükte de aynı. Sebebi işçi ile yuvanın birlikte büyümesi: her genişlikte canlı
görevlerin hepsi ilerleyebiliyor ve kısıt yalnız görev başına düşen on adım.

İki tablonun ilk satırı da aynı: genişlik bir olduğunda iki arka uç da **80 / 0** veriyor.
Havuzun arkasında ne olursa olsun, tek işçili havuz tek akıştır. Bir karşılaştırma havuz
genişliğini yazmıyorsa okunamaz — ilk satır bunu gösteriyor.

Genişliği seçerken bakılacak sayı da bu tablolarda: iki ardışık satır arasındaki fark. İş
parçacığı havuzunda işlemci ağırlıklı sütun için ikinci satırdan sonra fark **0**; genişliği
dörde ya da sekize çıkarmak ölçülebilir bir şey eklemiyor. Girdi/çıktı ağırlıklı sütunda aynı
fark dörtten sekize **3** tik. Süreç havuzunda ise fark her adımda büyük ve üç sütunda da
aynı. Genişlik için tek bir doğru sayı yok; olan şey, iki ölçüm arasındaki farkın küçüldüğü
yerde durmak.

Bir uyarı bu tabloların dışında duruyor ve varsayımlarda yazılı: süreç sütunu **veri devrini
saymıyor**. Üçüncü ders o bedeli ayrıca ölçmüştü; her göreve iki uçta on beşer aktarım adımı
eklendiğinde dört yuvanın kazancı sıfırlanıyordu. Buradaki süreç sütunu bu yüzden ulaşılabilir
en iyi sayıyı, yani bir **üst sınırı** veriyor. İş parçacığı sütununda böyle bir dış bedel yok,
çünkü aynı adres alanında taşınacak bir şey de yok.

#### Havuza Verilen İşin Biçimi

Arayüzün aynı olması, verilebilecek işin de aynı olduğu anlamına gelmiyor ve seçim çoğu zaman
sayılardan önce burada belirleniyor.

**Süreç arka ucu, işin taşınabilir olmasını ister.** Gönderilen işlev ve girdisi öbür uca
aktarılabilir bir gösterime çevrilir, sonuç geri taşınır. Taşınamayan bir değer havuza
giremez. Üstelik bu taşıma, üçüncü derste ölçülen aktarım adımlarının kendisidir: iş küçük ve
veri büyük olduğunda kazanç orada erir.

**İş parçacığı arka ucu taşıma istemez** ama başka bir şey ister: işin **kilidi bırakması**.
Bırakmayan bir iş, tabloların işlemci ağırlıklı sütununa düşer ve genişliği artırmak onu
kurtarmaz — ikinci genişlikten sonra sütun kımıldamıyordu.

**İkisi de işlerin bağımsız olmasını ister.** Ölçümdeki sekiz görev birbirini beklemiyor; bir
görevin sonucunu öbürü kullansaydı bekleyen görev o tikte ilerlemez ve örtüşen adım düşerdi.
Havuz arayüzü bu bağımlılığı görmez, çünkü gönderilen her iş kendi başına gönderilir.

Ölçümdeki sayılar bu üç koşul sağlandığı için çıkıyor. Koşullardan biri bozulduğunda tablo
aşağı doğru kayar; hiçbiri bozulmadığında yukarıdaki sayılar ulaşılabilir en iyi durumdur.
Havuz seçerken sorulacak ilk soru bu yüzden genişlik değil: **işin kendisi hangi arka ucun
istediği biçimde mi?**

#### Arayüzün Söylemediği

Son iki satır bu konunun kapanış sayısı: **iş parçacığı havuzu üç yükte 3 ayrı tik sayısı
veriyor ([31, 42, 73]); süreç havuzu 1 ([10]).**

Bu iki sayı, arka uç seçiminin ne olduğunu tanımlıyor. İş parçacığı havuzuna geçmek, ölçümü
**yüke duyarlı** kılmak demektir: hangi yükü verdiğinizi bilmeden sonucu kestiremezsiniz.
Süreç havuzuna geçmek — devir bedeli sayılmadığı sürece — ölçümü yükten **bağımsız** kılar.

Arayüz ikisini de aynı biçimde çağırıyor ve bu bir kolaylık. Ama aynı çağrı biçimi, aynı
davranışı vaat etmiyor. Ortak arayüzün gizlediği tam olarak bu dersin ölçtüğü şey: hangi adımın
örtüşebildiği. Bir kod tabanında havuz nesnesini iki satırla değiştirip sonucun aynı kalmasını
beklemek, bu iki sayıyı — **3** ile **1**'i — aynı saymak demektir.

Bu bölümün bütün rejimleri şu ana kadar aynı soruyu yanıtladı: **hangi rejim ne kazandırır?**
Beş ders boyunca yanıt hep aynı biçimde çıktı — kazanç, örtüşebilen adım sayısına ve o adımları
kaç yuvaya dağıtabildiğinize bağlı. Yanıtın her seferinde bir girdisi vardı ve o girdi
sorulmadı: **hangi adımın hangi tür olduğu.** Kurgu bunu bize verdi; gerçek bir programda
kimse vermez.

#### Özet

- Yürütücü havuzu iş dağıtmanın ortak arayüzüdür; aynı çağrı biçimi iki arka uçla da
  kullanılır ve eşleme işleminin çıktı sırası girdiden gelir, çizelgeden değil.
- Modelde iş parçacığı arka ucu **işçi = genişlik, yuva = 1**; süreç arka ucu **işçi =
  genişlik, yuva = genişlik** demektir. Bütün fark bu iki ayardadır.
- Sekiz genişlikte aynı görev, iş parçacığı havuzunda üç yükte **31 / 49**, **42 / 38** ve
  **73 / 7** verir; süreç havuzunda üçünde de **10 / 70**.
- Genişliği artırmak iş parçacığı havuzunda yükün girdi/çıktı payı kadar kazandırır ve işlemci
  ağırlıklı yükte ikinci genişlikte durur; süreç havuzunda üç yükte de tik'i yarıya indirir.
- İş parçacığı havuzu üç yükte **3** ayrı tik sayısı verir, süreç havuzu **1**. Aynı arayüzün
  arkasında aynı seçim yoktur; süreç sütunu ayrıca veri devrini saymayan bir üst sınırdır.

#### Sonraki Adım

Buraya kadar sayılan şey hep rejimdi: hangi rejim, hangi yükte, ne kazandırır. Her ölçümde
görevlerin adım dizisi elimizin altındaydı — hangi adımın işlemci adımı, hangisinin girdi/çıktı
adımı olduğunu kurgu söylüyordu. Gerçek bir programda o dizi yazılı değildir. Elde bir kod
tabanı vardır ve hangi çağrının ağır olduğu, hangi satırın sıcak yolda durduğu görünmez.
Sonraki ders bu boşluğu kapatır: bir programın hangi adımının ağır olduğu, süre ölçmeden,
**çağrı sayarak** nasıl bulunur?

## Başarım

Çağrı sayısıyla profil çıkarma, nesne yükü ve veri yapısı seçiminin bellek etkisi, sıcak yolun alt seviyeye taşınması, ve bir ölçümün neyi saydığını bildirmeden karşılaştırılamayacağı.

### Profil Çıkarma

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/profil-cikarma

Belirlenimci profil aracı çağrı sayısını verir, süre sütunlarını değil; aynı 80 adımlık iş iki yükte de 80 kez ilerletilir ama sıcak yoldaki işlev girdi/çıktı ağırlıklı yükte 136, işlemci ağırlıklı yükte 322 kez çağrılır.

Önceki konu altı derste dört rejimi yan yana koydu ve her birinin hangi yükte ne
kazandırdığını saydı. Son ders yürütücü havuzlarını ölçtükten sonra şu soruyu bıraktı:
buraya kadar **hangi rejimin** ne kazandırdığı sayıldı — peki bir programda **hangi adımın**
ağır olduğunu nereden bilirim?

Soru boş bir soru değil. Rejim seçmek, ağır adımın hangi türden olduğunu — işlemci adımı mı,
girdi/çıktı adımı mı — **bildiğini varsayar**. Varsayım yanlışsa seçim de yanlış olur:
işlemci ağırlıklı bir yüke işçi eklemek 80 tiki ancak 73'e indirir. Bu dersin yanıtı şudur:
bilinmez, ölçülür. Ve bu kursta ölçülen şey süre değil, **çağrı sayısıdır (call count)**.

#### Belirlenimci Sayaç

**Profil çıkarma (profiling)**, bir koşumun hangi parçada ne kadar iş yaptığını kayda geçirme
işidir. İki aile araç vardır. Örnekleyen araç koşumu düzenli aralıklarla yoklar ve nerede
olduğunu not eder; topladığı şey bir örneklemdir ve iki koşumda aynı çıkmaz. Belirlenimci
araç ise her çağrıyı ve her dönüşü görür; kaçırdığı olay yoktur.

Standart kitaplığın belirlenimci profil aracı iki tür veri toplar: bir işlevin **kaç kez**
çağrıldığı ve o çağrılarda **ne kadar** zaman geçtiği. Bu dersin tablosunda ikincisi
yoktur ve nedeni tek cümleyle şudur: aynı kod aynı makinede iki kez koştuğunda süre
sütunları farklı çıkar, çağrı sayısı ise değişmez — **karşılaştırılabilir olan sayıdır.**

Bunun bir sonucu var: aracın hazır dökümünü olduğu gibi basmıyoruz, çünkü o döküm süre
sütunlarını da taşır. Onun yerine toplanan kayıt açılıyor ve her satırdan **yalnız çağrı
sayısı** okunuyor. Bu bir kısıtlama gibi görünür; dersin sonunda görüleceği gibi, elde kalan
sayı sorunun tamamını yanıtlamaya yetiyor.

Kaydın biçimi bunu doğrudan destekler. Araç, koşum bittiğinde her işlev için bir giriş
tutar; girişin anahtarı işlevi tanımlayan üçlüdür — dosya, satır ve ad — değeri ise o
işlevle ilgili sayıların demeti. Demetin ilk iki alanı çağrı sayılarıdır: özyinelemesiz
çağrı sayısı ve toplam çağrı sayısı. Özyineleme olmayan bir programda ikisi eşittir. Kalan
alanlar zamanla ilgilidir ve bu derste okunmaz.

#### Sıcak Yol

**Sıcak yol (hot path)**, bir koşumda en sık çalıştırılan kod parçasıdır. Adı yanıltıcı
olabilir: sıcaklık kodun ne yaptığıyla değil, **kaç kez** çalıştığıyla ilgilidir. Bir kez
çağrılan ağır bir işlev sıcak yol değildir; yüzlerce kez çağrılan üç satırlık bir işlev
sıcak yoldur.

Sıcak yolu sezgiyle bulmak, kodun uzun görünen kısmıyla çok çalışan kısmını karıştırmak
demektir. Uzunluk göz kararı ölçülür, çağrı sayısı ölçülmez — sayılır. Bu derste sıcak yol
tek bir ölçüte bağlanır: **en çok çağrılan işlev.**

Bu ölçüt tek başına bir eniyileme kararı vermez, ama arama alanını daraltır. Bir programın
çoğu işlevi koşumda birkaç kez çağrılır; bir avuç işlev ise yüzlerce kez. İlk gruptaki bir
işlevi yeniden yazmanın toplam üzerindeki etkisi, çağrı sayısı kadarıyla sınırlıdır.
İkinci gruptaki bir işlevde kazanılan her adım, çağrı sayısıyla çarpılarak geri döner.

#### Ölçülecek Program

Ölçüm için yeni bir program yazmıyoruz; ortak tanımın çizelgeleyicisini ölçüyoruz. Bir engel
var: `kosum` tek bir işlevdir ve profil aracının tablosunda tek bir satır üretir. Bir satır
sıcak yolu göstermez.

Bu yüzden gövde adlandırılmış parçalara bölünür: `etkinleri_sec`, `islemci_adimi_mi`,
`ilerlet` ve tek bir tiki yürüten `tik_at`. **Davranış değişmez** — bölünmüş işlev, dört
rejim ile üç yükün on iki bileşiminin hepsinde özgün işlevle aynı dörtlüyü döndürür.
Değişen tek şey, aracın görebildiği **sınır sayısıdır.**

Buradan profil çıkarmanın ilk kuralı çıkar: **araç yalnız çağrı sınırlarını görür.** Bir
döngü gövdesi ayrı bir işlev değilse, tabloda satırı da yoktur. Ölçümün çözünürlüğünü aracın
kendisi değil, kodun bölünme biçimi belirler.

Aynı sayıları elle konan sayaçlarla da toplayabilirdik: her işlevin başına bir artırma
satırı koyup sonunda basmak. İki farkı var. Elle konan sayaç **nereye bakacağını önceden
bilmeyi** gerektirir; sıcak yolun hangisi olduğunu bilseydik zaten ölçmeye gerek kalmazdı.
İkincisi, sayaç satırları kodun kendisine karışır ve ölçüm bittiğinde tek tek sökülmeleri
gerekir. Araç ise kodu değiştirmeden dışarıdan takılır ve çıkarılır; ölçülen program,
yazılan programın aynısı kalır.

Ölçümün varsayımları:

- **BS1** — Görev kümesi ortak tanımdan gelir: sekiz görev, görev başına on adım, toplam
  **80 adım**. Kâhin kurgunun kendisidir; toplam adım sayısını ölçmeden biliyoruz.
- **BS2** — Bölünmüş `kosum`, özgün işlevle **aynı** dörtlüyü döndürür. Bölme bir yeniden
  yazım değil, ölçüm için sınır açmadır; `gorevler` ve `kosum` davranışı korunur.
- **BS3** — Ölçülen rejim sekiz işçili, tek yuvalı rejimdir. İki yük denenir — girdi/çıktı
  ağırlıklı ve işlemci ağırlıklı — ve **yükten başka hiçbir şey değişmez.**
- **BS4** — Kayıttan yalnız çağrı sayısı okunur. Süre alanları araç tarafından toplanır ama
  **yazılmaz**; yazılsalardı iki koşum arasında değişen tek sütun onlar olurdu.
- **BS5** — Tabloya yalnız dersin kendi işlevleri alınır; yerleşiklerin ve standart
  kitaplığın çağrıları sayılmaz.
- **BS6** — Aracın kendisi koşuma bir yük ekler. Bu yük çağrı sayısını **değiştirmez**,
  çünkü sayaç çağrıları saymakla yetinir, yeni çağrı üretmez.

#### Ölçüm

```python
"""Profil cikarma: ayni is, iki yuk; cagri sayisi sicak yolu gosterir."""

import cProfile
import pstats

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 etkinleri_sec(kalan, isci):
    return [i for i, g in enumerate(kalan) if g][:isci]


def islemci_adimi_mi(adim):
    return adim == ISLEMCI


def ilerlet(kalan, i):
    kalan[i].pop(0)


def tik_at(kalan, isci, islemci_yuvasi):
    etkin = etkinleri_sec(kalan, isci)
    yuva, ilerleyen, ia, ga = islemci_yuvasi, 0, 0, 0
    for i in etkin:
        if islemci_adimi_mi(kalan[i][0]):
            if yuva <= 0:
                continue
            yuva -= 1
            ia += 1
        else:
            ga += 1
        ilerlet(kalan, i)
        ilerleyen += 1
    return ilerleyen, ia, ga


def kosum(gorevler, isci, islemci_yuvasi):
    """Ortak tanimla ayni sonucu verir; govdesi adlandirilmis parcalara bolunmustur."""
    kalan = [list(g) for g in gorevler]
    tik = ortusen = islemci_adim = gc_adim = 0
    while any(kalan):
        ilerleyen, ia, ga = tik_at(kalan, isci, islemci_yuvasi)
        if not ilerleyen:
            break
        islemci_adim += ia
        gc_adim += ga
        tik += 1
        ortusen += max(0, ilerleyen - 1)
    return tik, ortusen, islemci_adim, gc_adim


IZLENEN = ("kosum", "tik_at", "etkinleri_sec", "islemci_adimi_mi", "ilerlet")


def cagrilari_say(gc_payi, isci=8, yuva=1):
    """Yalniz cagri sayisi okunur; sure sutunlari alinmaz."""
    g = gorevler(gc_payi=gc_payi)
    olcer = cProfile.Profile()
    olcer.enable()
    sonuc = kosum(g, isci, yuva)
    olcer.disable()
    sayim = {ad: 0 for ad in IZLENEN}
    for (_, _, ad), veri in pstats.Stats(olcer).stats.items():
        if ad in sayim:
            sayim[ad] = veri[1]
    return sonuc, sayim


gc_sonuc, gc_sayim = cagrilari_say(7)
is_sonuc, is_sayim = cagrilari_say(1)
print(f"{'işlev':<18s} {'girdi/çıktı ağırlıklı':>22s} {'işlemci ağırlıklı':>19s}")
for ad in IZLENEN:
    print(f"{ad:<18s} {gc_sayim[ad]:22d} {is_sayim[ad]:19d}")
print(f"{'toplam çağrı':<18s} {sum(gc_sayim.values()):22d} "
      f"{sum(is_sayim.values()):19d}")
print()
print(f"girdi/çıktı ağırlıklı: {gc_sonuc[0]} tik, {gc_sonuc[1]} örtüşen adım")
print(f"işlemci ağırlıklı    : {is_sonuc[0]} tik, {is_sonuc[1]} örtüşen adım")
print(f"iki koşumda da ilerlet çağrısı: {gc_sayim['ilerlet']} ve "
      f"{is_sayim['ilerlet']}; kâhinin verdiği toplam adım "
      f"{sum(len(x) for x in gorevler())}")
print("üç ardışık koşumda islemci_adimi_mi çağrısı:",
      [cagrilari_say(1)[1]["islemci_adimi_mi"] for _ in range(3)])
```

```
işlev               girdi/çıktı ağırlıklı   işlemci ağırlıklı
kosum                                   1                   1
tik_at                                 31                  73
etkinleri_sec                          31                  73
islemci_adimi_mi                      136                 322
ilerlet                                80                  80
toplam çağrı                          279                 549

girdi/çıktı ağırlıklı: 31 tik, 49 örtüşen adım
işlemci ağırlıklı    : 73 tik, 7 örtüşen adım
iki koşumda da ilerlet çağrısı: 80 ve 80; kâhinin verdiği toplam adım 80
üç ardışık koşumda islemci_adimi_mi çağrısı: [322, 322, 322]
```

#### Eşit Kalan Satır

Tablodaki en önemli satır, iki sütunu **eşit** olan satırdır: `ilerlet` her iki yükte de
**80** kez çağrılıyor. Kâhin bunu doğruluyor — kurgunun toplam adım sayısı 80'dir ve her
adım tam olarak bir kez atılır. **İş değişmedi.**

Değişen satırlar bunun etrafında duruyor. `tik_at` **31**'den **73**'e, `etkinleri_sec`
aynı biçimde **31**'den **73**'e, `islemci_adimi_mi` **136**'dan **322**'ye çıkıyor. Toplam
çağrı **279**'dan **549**'a. Aynı 80 adım, iki katına yakın çağrı.

Buradan dersin ana okuması çıkıyor: **çağrı sayısı yapılan işi değil, işe varma çabasını
sayar.** İki koşumda da 80 adım atıldı; fark, o 80 adıma ulaşmak için kaç kez soru
sorulduğundadır.

Eşit kalan satırın ikinci bir işlevi daha var: ölçümü doğruluyor. `ilerlet` çağrısının
80 çıkması, kurgudan bağımsız olarak bilinen bir sayıyla profil tablosunun uyuşması
demektir. Uyuşmasaydı, tabloyu değil aracı ya da bölmeyi sorgulamak gerekirdi. **Bilinen bir
sayıyı da ölçmek**, ölçümün kendisini sınamanın en ucuz yoludur; bir profil tablosunda
kâhinden gelen böyle bir satır varsa, kalan satırlar da güvenle okunur.

#### Sıcak Yol Hangisi

En çok çağrılan işlev iki sütunda da `islemci_adimi_mi`. Sıcak yol budur ve dikkat çeken
şey sayının **80'den büyük** olmasıdır: 136 ve 322. Adım başına birden çok çağrı demektir.

Nedeni çizelgeleyicinin kendisinde. Bir tikte bakılan **her** görev bu işlevi çağırır —
ilerleyebilen de, yuva bulamadığı için geri çevrilen de. Girdi/çıktı ağırlıklı yükte
adımların çoğu yuva istemiyor, görevler erken tükeniyor ve toplam bakış 136'da kalıyor.
İşlemci ağırlıklı yükte tek yuva her tikte bir görev dışındakileri geri çeviriyor; geri
çevrilen görev tükenmeden duruyor, sonraki tikte yeniden bakılıyor ve her bakış bir çağrıya
mal oluyor. Aradaki fark **186 çağrıdır.**

Bölme işlemi bu açıklamayı sınıyor. Girdi/çıktı ağırlıklı koşumda tik başına ortalama bakış
136/31, yani **4,39**; işlemci ağırlıklı koşumda 322/73, yani **4,41**. İki sayı neredeyse
aynı. Demek ki bir tikte bakılan görev sayısı yükten pek etkilenmiyor; farkı tümüyle **tik
sayısı** üretiyor. Sıcak yol sıcaklığını kendi içindeki bir şeyden değil, **kaç tur
döndüğünden** alıyor.

Bu, sıcak yolla ilgili sık yapılan bir yanlışı düzeltir: en çok çağrılan işlevin kendisi
kusurlu olmak zorunda değildir. `islemci_adimi_mi` üç satırlık, tek karşılaştırmalı bir
işlev; içinde düzeltilecek bir şey yok. Onu 322 kez çağıran şey, dışarıdaki yuva kısıtıdır.
**Sıcak yol nedeni değil, göstergesidir.**

`tik_at` ile `etkinleri_sec` satırları ayrı bir şey söylüyor: değerleri tik sayısına
**birebir** eşit — 31 ve 73. Profil tablosu, hiç tik saymadan tik sayısını verdi. Bu bir
rastlantı değil; iki işlev de tik başına tam bir kez çağrılıyor. Kural şu: **bir işlevin
çağrı sayısı, o işlevin temsil ettiği olayın sayısıdır.** Ölçümün neyi sayacağını aracın
kendisi değil, kodun bölünme biçimi belirler — ve bu, `tik_at` adında bir işlev yazma
kararının ölçüm kararı olduğu anlamına gelir.

Son satır belirlenimciliği gösteriyor: aynı koşum üç kez yinelendiğinde `islemci_adimi_mi`
çağrısı **322, 322, 322**. Süre sütunları alınsaydı üçü de farklı olurdu.

#### Gözlemcinin Etkisi

Belirlenimci aracın bedeli vardır: her çağrı sınırında sayaç devreye girer, kaydı günceller
ve akışı geri verir. Profil altında koşan program, profilsiz koşan programın aynısı
değildir. Bu, ölçüm araçlarının bilinen kusurudur ve süre ölçen bir tabloda doğrudan sonuca
sızar — çok çağrılan küçük işlevler, sayaç yükü kendi paylarına eklendiği için olduklarından
ağır görünür.

Çağrı sayısı bu kusurdan etkilenmez ve nedeni tanımın kendisindedir: sayaç çağrıları
**sayar**, üretmez. `islemci_adimi_mi` sayaç varken de yokken de tam 322 kez çağrılır,
çünkü onu çağıran şey aracın varlığı değil, görev listesinin içeriğidir. Ölçüm aracının
gözlediği şeyi değiştirmediği ender durumlardan biridir bu — ve kursun süre yerine adım
saymasının ikinci gerekçesidir.

Buradan pratik bir sonuç çıkar: bir programın çağrı dökümü, profil altında bir kez alınıp
sonraki koşumlar için de geçerli sayılabilir. Süre dökümü için aynı şey söylenemez.

#### Çağrı Sayısının Söylemediği

Tablonun ilk satırı bu sınırı gösteriyor: `kosum` yalnız **1** kez çağrıldı. Çağrı sayısına
bakan bir okuma onu en soğuk işlev sayar; oysa koşumun tamamı onun içinde geçiyor. Bir
işlevi bir kez çağırmakla ucuza getirmiş olmuyoruz.

İkinci sınır kapsama sınırıdır. `IZLENEN` demeti dışında kalan her şey — yerleşik çağrılar,
liste işlemleri, biçimlendirme — tabloya girmiyor. Onlar da çalıştı; sayılmadılar. Bir
profil tablosu, **neyi izlediğini söylemeden okunamaz** ve bu dersin tablosu beş işlev
izliyor.

Üçüncü sınır en önemlisi. Çağrı sayısı bir işlevin kaç kez çalıştığını verir, o çalışmanın
neye mal olduğunu vermez. `ilerlet` 80 kez çağrıldı; bu 80 çağrının her biri bir listenin
başından bir öğe çıkarıyor ve o işin maliyeti listenin uzunluğuna bağlı. Çağrı sayısı bunu
göstermez. Bunun için başka bir sayıya bakmak gerekir — kaç nesnenin var edildiğine ve
kaçının ayakta tutulduğuna.

#### Gösterge Tasarımı Bu Dersin Konusu Değil

Gözlemlenebilirlik ve Güvenilirlik kursu ile Gözlemlenebilirlik ve İşletim kursu gösterge
tasarımını kurdu: hangi göstergenin toplanacağı, nasıl adlandırılacağı, hangi eşiğin uyarı
üreteceği. **Tekrarlanmaz**, atıf yeterlidir.

Aradaki fark yön farkıdır. Gösterge tasarımı **önden** seçim yapar: ne toplanacağına koşum
başlamadan karar verilir ve toplanmayan şey sonradan geri getirilemez. Profil çıkarma
**sonradan** okur: koşum bittikten sonra çağrı dökümü açılır ve hangi işlevin sıcak olduğu
oradan çıkar. Biri işletim aracı, öbürü tanı aracıdır; ikisi de aynı programda bulunabilir
ve birbirinin yerini tutmaz.

#### Özet

- Belirlenimci profil aracı her çağrıyı ve her dönüşü görür; topladığı iki veriden **çağrı
  sayısı** yinelenen koşumlarda değişmez, süre alanları değişir — bu yüzden tabloda yalnız
  çağrı sayısı yazılır.
- Sıcak yol, en çok **çağrılan** koddur; uzunlukla değil çağrı sayısıyla tanımlanır ve
  ölçülmeden bilinemez.
- Araç yalnız çağrı sınırlarını görür; bir gövde ayrı işlev değilse tabloda satırı yoktur.
  Ölçümün çözünürlüğünü kodun bölünme biçimi belirler.
- Aynı 80 adımlık iş iki yükte de **80** `ilerlet` çağrısıyla bitiyor, ama toplam çağrı
  **279**'dan **549**'a çıkıyor: sayılan şey iş değil, işe varma çabasıdır.
- Sıcak yol `islemci_adimi_mi`; çağrısı **136**'dan **322**'ye çıkıyor çünkü tek yuva geri
  çevirdiği her görevi sonraki tikte yeniden bakılmaya bırakıyor.

#### Sonraki Adım

Çağrı sayısı bir işlevin kaç kez çalıştığını söyler. Söylemediği bir şey var: o koşum
sırasında **kaç nesnenin aynı anda ayakta durduğu**. `ilerlet` iki yükte de 80 kez çağrıldı,
ama görev listelerinin bellekte kapladığı yapı iki koşumda da aynı mıydı — ve aynı 80 adım
başka bir kapta tutulsaydı kaç nesne var olurdu? Sonraki ders bu soruya bakar ve yine bayt
değil, **nesne** sayar.

### Bellek Kullanımı

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/bellek-kullanimi

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.

Ö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

```python
"""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?

### Yerel Uzantılar

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/yerel-uzantilar

Sıcak yolun alt seviyeye taşınması modelde üç etkiyle ölçülür; işlemci ağırlıklı yükte on iki dörtlü grup 80 adımı 44'e, 73 tiki 25'e indirir, sınır maliyeti kazancı 48'den 36'ya çeker ve aynı düzenek girdi/çıktı ağırlıklı yükte hiçbir şey kazandırmaz.

Önceki iki ders sıcak yolu iki ayrı sayıyla çevreledi: işlemci ağırlıklı yükte
`islemci_adimi_mi` 322 kez çağrılıyor ve 80 adımı taşıyan yapı biçimine göre 1 ile 172
nesne arasında yer tutuyor. İkisi de aynı yeri gösteriyor — işin ağırlığı işlemci
adımlarında.

Bu noktada sık başvurulan bir yol var: sıcak yolu dilin dışına, daha alt bir seviyeye
taşımak. Kazancı çoğunlukla süreyle anlatılır ve o anlatım bu kursta kabul edilmez. Bu ders
aynı soruyu tek bir biçimde sorar: **sıcak yolun adım sayısı düştüğünde toplam tik ne
oluyor?**

#### Yerel Uzantı Nedir

**Yerel uzantı (native extension)**, kodu yorumlayıcının adım adım yürütmesi yerine önceden
makine yönergelerine çevrilmiş biçimde tutan ve dile bir modül gibi görünen bileşendir.
Programın geri kalanı onu sıradan bir işlev çağrısıyla kullanır; çağrı bir **sınırdan** geçer,
argümanlar alt seviyenin beklediği biçime çevrilir, iş orada yapılır ve sonuç geri çevrilir.

Bu ders bir uzantının nasıl yazılacağını değil, **ne kazandırdığını** ölçer. Araçtan,
derleyiciden ve paketten söz edilmez; ölçülen şey yaklaşımın kendisidir, onu gerçekleştiren
belirli bir düzenek değil. Yaklaşımın kendisini tanımlayan iki özellik yeter: **iş dilin
yürütme adımlarının dışında yapılır** ve **her giriş çıkış bir sınırdan geçer.** Bu iki
özellik hangi araçla sağlanırsa sağlansın ölçüm aynı kalır; bir sonraki tablo bu iki
özelliğin doğrudan karşılığıdır.

Gerçek bir uzantı da derlenmiyor ve bunun gerekçesi kursun ölçüsüyle aynı. Derlenmiş bir
uzantının kazancı ortama, derleme seçeneklerine ve işin türüne bağlıdır; iki makinede iki
ayrı sayı verir. Modelde ölçülen adım ise her koşumda aynı çıkar. **Yeniden üretilemeyen
bir sayı bir kazanç iddiasını taşıyamaz.**

#### Modelde Üç Etki

Yerel uzantı modele üç ayrı etkiyle girer ve üçünü ayrı ayrı ölçmek dersin asıl işidir.

**Birincisi adım sayısıdır.** Sıcak yolda ardışık duran birkaç işlemci adımı, uzantıya tek
bir çağrıyla devredilir; yorumlayıcının görmesi gereken adım sayısı düşer. Dört adımın tek
çağrıya inmesi, o dört adımın yerine bir adım demektir.

**İkincisi sınır maliyetidir.** Çağrı bedavaya geçmez: argümanların çevrilmesi, verinin
sınırın öbür tarafına aktarılması ve dönüş değerinin kurulması iş yapar. Modelde bu, çağrı
başına sabit sayıda ek adımdır.

**Üçüncüsü kilittir.** Alt seviyede çalışan bir kod, işini yaparken genel yorumlayıcı
kilidini bırakabilir. Bunun çizelgeleyicideki karşılığı doğrudandır: kilidi bırakan adım
işlemci yuvası istemez, dolayısıyla başka görevlerin adımlarıyla **örtüşebilir**. Bu yeni
bir davranış değil; ortak tanımın çizelgeleyicisi yuva istemeyen her adıma zaten böyle
davranır.

Üç etki birbirinden bağımsız açılıp kapanabilir ve tablo bunu yapıyor: kimi satırda yalnız
adım düşüyor, kimisinde sınır maliyeti ekleniyor, kimisinde kilit bırakılıyor.

Modelin dışında kalanları da söylemek gerekir, çünkü bir model neyi kapsamadığını
söylemeden okunamaz. Sınır maliyeti burada çağrı başına sabittir; gerçekte aktarılan verinin
büyüklüğüne bağlı olabilir ve büyük veri kopyalanan bir çağrıda sabit sayı yanıltır.
Belleğin sınırın hangi tarafında sahiplenildiği, alt seviyede oluşan bir hatanın dile nasıl
taşındığı ve uzantının taşınabilirliği de modelde yoktur. Bunlar kazanç sorusunun değil,
**maliyet sorusunun** parçalarıdır; tablo yalnız kazancı ölçüyor ve bu sınırı açık tutuyor.

Ölçümün varsayımları:

- **BS13** — Yerel uzantı modelde yalnız bu üç etkiyle temsil edilir: adım birleştirme,
  sınır maliyeti, kilidin bırakılması. Başka hiçbir etki modellenmez.
- **BS14** — Yalnız **ardışık ve tam** gruplar birleşir. Grubu dolduramayan adımlar
  yorumlanmaya devam eder; bir uzantı da ancak sıcak yolun tamamını devralabildiği yerde
  devralır.
- **BS15** — Sınır maliyeti çağrı başına sabit ve tam sayıda adımdır. Argüman dönüşümü, veri
  aktarımı ve dönüş değerinin kurulması bu tek sayıda toplanır.
- **BS16** — Kilidi bırakan adım çizelgeleyici için işlemci adımı değildir: yuva istemez ve
  örtüşebilir. `gorevler` ve `kosum` işlevlerinin davranışı **değiştirilmez**; yalnız yeni
  bir adım türü eklenir.
- **BS17** — Gerçek bir uzantı derlenmez, süre ölçülmez. Kazanç yalnız **adım ve tik**
  olarak yazılır.
- **BS18** — Karşılaştırma tabanı, aynı görev kümesinin yoğunlaştırılmamış koşumudur. İki
  yük ayrı ayrı ölçülür ve rejim dışında hiçbir şey değişmez; her iki koşum da sekiz işçili,
  tek yuvalı rejimdedir.

#### Ölçüm

```python
"""Yerel uzanti modeli: sicak yol kisaldiginda toplam tik ne oluyor."""

TOHUM = 20260817
ISLEMCI, GC, YEREL = "islemci", "g_c", "yerel"


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 yogunlastir(gorev, kat, sinir=0, tur=ISLEMCI):
    """Ardisik kat adet islemci adimini tek adima indirir; sinir kadar adim ekler."""
    yeni, biriken = [], 0
    for adim in gorev:
        if adim == ISLEMCI:
            biriken += 1
            if biriken == kat:
                yeni.append(tur)
                yeni.extend([ISLEMCI] * sinir)
                biriken = 0
        else:
            yeni.extend([ISLEMCI] * biriken)
            biriken = 0
            yeni.append(adim)
    yeni.extend([ISLEMCI] * biriken)
    return yeni


REJIM = (
    ("taban: her adım yorumlanıyor", 1, 0, ISLEMCI),
    ("dört adım tek çağrıda", 4, 0, ISLEMCI),
    ("dört adım, sınır 1 adım", 4, 1, ISLEMCI),
    ("dört adım, kilit bırakılıyor", 4, 0, YEREL),
    ("dört adım, kilit + sınır 1", 4, 1, YEREL),
    ("iki adım, kilit + sınır 1", 2, 1, YEREL),
    ("on adım, kilit bırakılıyor", 10, 0, YEREL),
)

for yuk, pay in (("işlemci ağırlıklı", 1), ("girdi/çıktı ağırlıklı", 7)):
    G = gorevler(gc_payi=pay)
    taban = kosum(G, 8, 1)[0]
    print(f"{yuk:<30s} {'adım':>5s} {'tik':>4s} {'örtüşen':>8s} {'kazanç':>7s}")
    for ad, kat, sinir, tur in REJIM:
        Y = [yogunlastir(g, kat, sinir, tur) for g in G]
        tik, ortusen, _, _ = kosum(Y, 8, 1)
        print(f"  {ad:<28s} {sum(len(g) for g in Y):5d} {tik:4d} {ortusen:8d}"
              f" {taban - tik:7d}")
    print()

G = gorevler(gc_payi=1)
D = [yogunlastir(g, 4, 0, YEREL) for g in G]
print(f"işlemci ağırlıklı yükte yoğunlaştırma öncesi adım {sum(len(g) for g in G)}, "
      f"sonrası {sum(len(g) for g in D)}")
print(f"tek çağrıya inen dörtlü grup {sum(g.count(YEREL) for g in D)}, "
      f"yorumlanmaya devam eden işlemci adımı {sum(g.count(ISLEMCI) for g in D)}")
H = gorevler(gc_payi=7)
E = [yogunlastir(g, 4, 0, YEREL) for g in H]
print(f"girdi/çıktı ağırlıklı yükte dörtlü grup {sum(g.count(YEREL) for g in E)}, "
      f"adım {sum(len(g) for g in H)} -> {sum(len(g) for g in E)}")
```

```
işlemci ağırlıklı               adım  tik  örtüşen  kazanç
  taban: her adım yorumlanıyor    80   73        7       0
  dört adım tek çağrıda           44   37        7      36
  dört adım, sınır 1 adım         56   49        7      24
  dört adım, kilit bırakılıyor    44   25       19      48
  dört adım, kilit + sınır 1      56   37       19      36
  iki adım, kilit + sınır 1       80   44       36      29
  on adım, kilit bırakılıyor      53   44        9      29

girdi/çıktı ağırlıklı           adım  tik  örtüşen  kazanç
  taban: her adım yorumlanıyor    80   31       49       0
  dört adım tek çağrıda           80   31       49       0
  dört adım, sınır 1 adım         80   31       49       0
  dört adım, kilit bırakılıyor    80   31       49       0
  dört adım, kilit + sınır 1      80   31       49       0
  iki adım, kilit + sınır 1       80   24       56       7
  on adım, kilit bırakılıyor      80   31       49       0

işlemci ağırlıklı yükte yoğunlaştırma öncesi adım 80, sonrası 44
tek çağrıya inen dörtlü grup 12, yorumlanmaya devam eden işlemci adımı 25
girdi/çıktı ağırlıklı yükte dörtlü grup 0, adım 80 -> 80
```

#### Adımın Düşmesi

İşlemci ağırlıklı yükte taban **80 adım, 73 tik**. Dörtlü grupları tek çağrıya indirmek adımı
**44**'e, tiki **37**'ye çekiyor; kazanç **36 tik**. Alt bloktaki döküm bunun nereden
geldiğini söylüyor: **12** dörtlü grup birleşmiş, 48 adım 12 adıma inmiş, geriye
**25** işlemci adımı yorumlanmak üzere kalmış.

Dikkat çeken şey örtüşen adım sütununun **7**'de kalması. Adım sayısı düştü, tik düştü, ama
örtüşme hiç artmadı. Bunun nedeni tek yuvadır: birleşmiş adım da bir işlemci adımıdır ve
yine yuva ister. **Daha az iş yapıldı, ama iş yine sırayla yapıldı.**

Kilit bırakıldığında tablo değişiyor: aynı 44 adım **25 tik**te bitiyor ve örtüşen adım
**7**'den **19**'a çıkıyor. Kazanç **36**'dan **48**'e. Aradaki 12 tik, tümüyle 12 birleşik
adımın yuva istemeyi bırakmasından geliyor. **Aynı adım azaltması, kilit bırakılmadan
üçte iki, bırakıldığında tam kazanç veriyor.**

#### Sınırın Ödettiği

Sınır maliyeti tabloda iki satır olarak duruyor ve okuma kolay.

Kilitsiz durumda çağrı başına bir adımlık sınır, adımı **44**'ten **56**'ya çıkarıyor —
12 çağrı, 12 ek adım. Tik **37**'den **49**'a, kazanç **36**'dan **24**'e iniyor. Sınır,
kazancın **üçte birini** geri aldı.

Kilitli durumda aynı sınır kazancı **48**'den **36**'ya indiriyor. Buradaki sayı ilginç:
**36**, kilidin hiç bırakılmadığı ama sınırın da hiç ödenmediği satırın kazancıyla
birebir aynı. Yani sınır maliyeti, kilit bırakmanın getirdiği kazancı tam olarak yedi.
İki farklı düzenek, aynı toplam. Bir ölçüm bu iki satırdan yalnız kazanç sayısını gösterse,
aralarında hiçbir fark görünmezdi.

Bu, uzantı kararının en kırılgan yeridir: **kazanç grup büyüklüğüyle artar, sınır maliyeti
çağrı sayısıyla artar.** Küçük gruplar çok çağrı demektir ve çok çağrı çok sınır demektir.

#### Adım Düşmeden Gelen Kazanç

Altıncı satır bunu uçta gösteriyor. İki adımı tek çağrıya indirip çağrı başına bir adım
sınır ödeyince toplam adım **80**'de kalıyor — iki adım gitti, bir çağrı ve bir sınır adımı
geldi; hesap başa döndü. Buna karşın tik **73**'ten **44**'e iniyor ve örtüşen adım
**7**'den **36**'ya çıkıyor.

Yapılan iş hiç azalmadı; kazanç **29 tik**. Tümü kilidin bırakılmasından geliyor. Bu satır,
kursun ölçü ekseninin bu derse düşen payıdır: **bir eniyilemenin kazancı yaptığı işi
azaltmasından gelmek zorunda değildir; o işin örtüşmesine izin vermesinden de gelebilir.**

#### Grubu Büyütmenin Sınırı

Son satır sezginin ters yöne çalıştığı yerdir. Grup dört yerine on yapıldığında — daha çok
adımı tek çağrıya devretmek, daha büyük kazanç beklenir — adım **44** yerine **53**, tik
**25** yerine **44** çıkıyor.

Neden ortak tanımın kurgusunda. On adımlık görevlerde art arda **tam on** işlemci adımının
gelmesi ender; girdi/çıktı adımları araya girip grubu bölüyor. Grubu dolduramayan adımlar
birleşmiyor, tek tek yorumlanıyor. **Grup büyüklüğü sıcak yolun gerçek uzunluğunu aşarsa
kazanç büyümez, küçülür.**

Bu satırı bir üstündekiyle yan yana koymak ikinci bir şey öğretiyor. Onlu grup **53** adımda
**44** tik veriyor; dörtlü grup, sınır maliyetiyle birlikte, **56** adımda **37** tik.
Yani daha **az** adımlı rejim daha **çok** tik harcıyor. Toplam adım sayısı tek başına tiki
belirlemiyor; belirleyen şey o adımların **hangi türden** olduğu. Onlu grupta yalnız birkaç
adım kilidi bırakabildiği için örtüşen adım **9**'da kalıyor, dörtlü grupta **19**'a
çıkıyor. **Adım saymak yetmez; hangi adımın örtüşebildiğini de saymak gerekir.**

#### Sıcak Yol Orada Değilse

İkinci tablo dersin en sert sonucudur. Girdi/çıktı ağırlıklı yükte yedi satırın beşi
tabanla **birebir aynı**: 80 adım, 31 tik, 49 örtüşen adım, kazanç **0**.

Alt blok nedenini tek sayıyla veriyor: bu yükte ardışık dört işlemci adımından oluşan grup
sayısı **0**. Birleştirilecek bir şey yok. Uzantı yazıldı, sınır kuruldu, kilit bırakıldı —
ve tablo hiç kımıldamadı.

Tek kazançlı satır ikili grup: **31**'den **24** tike, kazanç **7**. Bu yükte ardışık iki
işlemci adımı bulunabiliyor ve kazanç yine adımdan değil örtüşmeden geliyor; adım yine
**80**'de kalıyor.

Sonuç şudur: **sıcak yolu alt seviyeye taşımanın kazancı, sıcak yolun gerçekten orada
olmasına bağlıdır.** İki tablo aynı düzeneği, aynı grup büyüklüğünü ve aynı sınır
maliyetini ölçüyor; biri 48 tik kazandırıyor, öbürü hiç. Aradaki fark koddan değil,
**yükün bileşiminden** geliyor — ve o bileşimi bilmenin yolu, önceki iki dersin yaptığı gibi
ölçmektir.

İlk dersin çağrı dökümü bu ayrımı zaten göstermişti. `islemci_adimi_mi` girdi/çıktı ağırlıklı
yükte **136**, işlemci ağırlıklı yükte **322** kez çağrılıyordu; ilkinde işlemci adımları
yuvada birikmiyor, ikincisinde birikiyordu. Uzantı kararı için gereken bilgi orada
duruyordu. Profil çıkarmadan uzantıya geçmek, ikinci tablodaki beş satırı yazmakla aynı
şeydir: yapılmış iş, ölçülmüş sıfır kazanç.

#### Özet

- Yerel uzantı modelde üç etkiyle temsil edilir: sıcak yolun adım sayısının düşmesi, çağrı
  başına sabit sınır maliyeti ve kilidin bırakılmasıyla adımın yuva istemeyi bırakması.
- İşlemci ağırlıklı yükte **12** dörtlü grup 80 adımı **44**'e, 73 tiki **37**'ye indiriyor;
  kilit de bırakılırsa tik **25**'e iniyor ve örtüşen adım **7**'den **19**'a çıkıyor.
- Çağrı başına bir adımlık sınır maliyeti kazancı **48**'den **36**'ya çekiyor — kilit
  bırakmanın getirdiği kazancın tamamını yiyor. Kazanç grup büyüklüğüyle, maliyet çağrı
  sayısıyla artar.
- Toplam adım hiç düşmeden de kazanç olabilir: ikili gruplarda adım **80**'de kalırken tik
  **73**'ten **44**'e iniyor, kazancın tümü örtüşmeden geliyor.
- Grup büyüklüğü sıcak yolun gerçek uzunluğunu aşarsa kazanç küçülür: onlu grup **44** tik
  verirken dörtlü grup **25** veriyor.
- Aynı düzenek girdi/çıktı ağırlıklı yükte hiçbir şey kazandırmıyor, çünkü o yükte ardışık
  dörtlü grup sayısı **0**.

#### Sonraki Adım

Bu ders bir tablo boyunca aynı şeyi yaptı: her satırda neyin sayıldığını satırın adında
yazdı — kaç adım birleşti, sınır ödendi mi, kilit bırakıldı mı. Adlar kaldırılsaydı geriye
yalnız sayılar kalırdı ve **37** ile **37** yan yana durup aynı şeyi ölçtüklerini
söylerdi; oysa biri sınır ödeyen kilitli rejim, öbürü sınır ödemeyen kilitsiz rejimdi.
Kursun son dersi bu tehlikeyi kendi başına ölçer: aynı iş, aynı rejim, tek fark ölçüme neyin
dahil edildiği — ve iki sayı arasında ne kadar fark çıkıyor.

### Ölçüm Disiplini

Source: https://academia.sh/tr/kurslar/python-eszamanlilik/olcum-disiplini

Aynı koşum altı ayrı bildirimle 26 ile 80 arasında altı ayrı sayı verir; kurulum sayılmadığında 31, sayıldığında 37 tik çıkar ve %19,35'lik bu fark, yalnız bir rejimde sayıldığında iki rejimin sıralamasını tersine çevirir.

Önceki ders bir tablo boyunca her satırın neyi ölçtüğünü satırın adına yazdı ve iki satır
aynı sayıyı verdi: **37**. Biri sınır maliyeti ödeyen ama kilidi bırakan rejimdi, öbürü
sınır ödemeyen ama kilidi bırakmayan. Adlar silinseydi geriye iki eşit sayı kalırdı ve
ikisinin aynı şeyi ölçtüğü sanılırdı.

Bu kursun son dersi tam bu tehlikeyi ölçer. Sorusu şudur: **bir ölçüm, neyi saydığını
söylemeden karşılaştırılabilir mi?** Yanıt hem gösterilecek hem de kursun kendi yöntemine
uygulanacak.

#### Karşılaştırmalı Ölçüm Ne Gerektirir

**Karşılaştırmalı ölçüm (benchmark)**, iki seçeneği aynı iş üzerinde yan yana koyan
ölçümdür. "Aynı iş" koşulu yerine geldiğinde bile karşılaştırma kendiliğinden geçerli olmaz;
üç bildirim daha gerekir.

**Birimin bildirimi:** sayılan şey nedir — tik mi, adım mı, çağrı mı, nesne mi? Bu kursun her
ölçümü birimini adıyla yazdı ve bunun bir nedeni vardı.

**Rejimin bildirimi:** kaç işçi, kaç yuva, hangi yük? Aynı kurgu sekiz işçi ve tek yuvada 31,
tek akışta 80 tik verir; rejim bildirilmeden sayı bir şey söylemez.

**Sınırın bildirimi:** ölçüm nerede başlıyor ve nerede bitiyor? İşin hangi bölümü sayıma
dahil? Bu üçüncüsü en sık atlanandır ve bu dersin ölçtüğü şeydir.

Üçünün üstünde bir dördüncüsü durur: **kâhin.** Ölçümün doğru sonucu verdiğini ne
söylüyor? Bu kursta kâhin kurgunun kendisidir — görevlerin adım dizisini biz ürettik, bu
yüzden toplam adımın 80 olduğunu ölçmeden biliyoruz ve her tablo bu sayıyla sınanabiliyor.
Kâhini olmayan bir ölçüm büyük sayıyı küçükten ayırabilir ama doğruyu yanlıştan ayıramaz;
eniyilemenin sonucu bozduğu durumda tablo yine güzel görünür.

#### Kurulum Sayılır mı

Bir işin başlamadan önce ödenen bir bedeli vardır: yürütme bağlamının kurulması, görevlerin
dağıtılması, ilk turda atlanamayan hazırlık. Modelde bu bir süre değil, **sabit sayıda
adımdır** ve kurulum adı verilir.

Kurulumun sayılıp sayılmaması bir doğru–yanlış sorusu değildir. Uzun süre çalışacak bir
hizmette kurulum bir kez ödenir ve toplamın yanında önemsizleşir; kısa ömürlü bir işte her
koşumda yeniden ödenir ve doğrudan maliyettir. **İki bildirim de savunulabilir.**
Savunulamaz olan, hangisinin yapıldığını yazmadan sayıyı vermektir.

Ölçümün varsayımları:

- **BS19** — Ölçülen iş bütün satırlarda aynıdır: ortak tanımın sekiz görevi, toplam **80
  adım**, girdi/çıktı ağırlıklı yük. Değişen tek şey **neyin sayıldığının bildirimidir.**
- **BS20** — Kurulum bir süre değil, ilk turda atlanamayan **sabit adım sayısıdır.**
- **BS21** — Kurulum iş parçacığı rejimi için **6**, çoklu süreç rejimi için **25** adım
  olarak **bildirilir**. Bu sayılar ölçülmüş değil, modelin varsayımıdır; ayrı bir yürütme
  bağlamı kurmanın daha çok adım gerektirmesine dayanır ve tabloda böyle okunur.
- **BS22** — Altı bildirimin altısı da aynı koşumdan okunur ve **hiçbiri yanlış değildir.**
  Yanlış olan, hangisinin okunduğunu söylemeden sayıyı yazmaktır.
- **BS23** — Kâhin kurgudur: toplam adım **80**'dir ve hangi rejim seçilirse seçilsin 80
  kalır. Rejim adım sayısını değil, adımların kaç tike dağıldığını değiştirir.
- **BS24** — Hiçbir satırda süre ölçülmez. Kurulum dahil ölçüm de bir süre ölçümü değil,
  adım sayımıdır; dersin iddiası zaman üzerine değil, **sayma sınırı** üzerinedir.

#### Ölçüm

```python
"""Olcum disiplini: ayni kosum, ayri bildirimler; hangi sayi karsilastirilabilir."""

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


G = gorevler(gc_payi=7)
TIK, ORTUSEN, IA, GA = kosum(G, 8, 1)
KURULUM = {"iş parçacığı (tek yuva)": 6, "çoklu süreç (dört yuva)": 25}

print("aynı koşum, altı bildirim")
for ad, sayi in (("kurulum hariç, tik", TIK),
                 ("kurulum dahil, tik", TIK + KURULUM["iş parçacığı (tek yuva)"]),
                 ("kurulum hariç, işlemci adımı", IA),
                 ("kurulum hariç, girdi/çıktı adımı", GA),
                 ("örtüşen adım", ORTUSEN),
                 ("toplam adım", sum(len(g) for g in G))):
    print(f"  {ad:<34s} {sayi:4d}")
k = KURULUM["iş parçacığı (tek yuva)"]
print(f"\nkurulum sayılmadan {TIK} tik, sayılınca {TIK + k} tik; "
      f"fark {k} tik ({k / TIK:.4f})")

IP = kosum(G, 8, 1)[0]
CS = kosum(G, 8, 4)[0]
ki, kc = KURULUM["iş parçacığı (tek yuva)"], KURULUM["çoklu süreç (dört yuva)"]
print(f"\n{'kurulum bildirimi':<28s} {'iş parçacığı':>13s} {'çoklu süreç':>12s} "
      f"{'kazanan':>14s}")
for ad, a, b in (("ikisinde de sayılmıyor", IP, CS),
                 ("ikisinde de sayılıyor", IP + ki, CS + kc),
                 ("yalnız süreçte sayılıyor", IP, CS + kc),
                 ("yalnız iş parçacığında", IP + ki, CS)):
    kazanan = "iş parçacığı" if a < b else ("çoklu süreç" if b < a else "eşit")
    print(f"  {ad:<26s} {a:13d} {b:12d} {kazanan:>14s}")
print(f"\nmodelde bildirilen kurulum: iş parçacığı {ki} adım, çoklu süreç {kc} adım")
```

```
aynı koşum, altı bildirim
  kurulum hariç, tik                   31
  kurulum dahil, tik                   37
  kurulum hariç, işlemci adımı         26
  kurulum hariç, girdi/çıktı adımı     54
  örtüşen adım                         49
  toplam adım                          80

kurulum sayılmadan 31 tik, sayılınca 37 tik; fark 6 tik (0.1935)

kurulum bildirimi             iş parçacığı  çoklu süreç        kazanan
  ikisinde de sayılmıyor                31           10    çoklu süreç
  ikisinde de sayılıyor                 37           35    çoklu süreç
  yalnız süreçte sayılıyor              31           35   iş parçacığı
  yalnız iş parçacığında                37           10    çoklu süreç

modelde bildirilen kurulum: iş parçacığı 6 adım, çoklu süreç 25 adım
```

#### Bir Koşum, Altı Sayı

Üst tablonun altı satırı **tek bir koşumdan** okunuyor. Görevler bir kez üretildi, çizelge
bir kez işletildi; değişen tek şey sonuca hangi soruyla bakıldığı. Çıkan sayılar **26, 31,
37, 49, 54, 80**.

Altısı da doğru. "Bu iş 26 birim sürdü" diyen de, "80 birim sürdü" diyen de aynı koşumdan
söz ediyor. Aralarındaki oran üç katından fazla ve bu farkın tamamı ölçümden değil
**bildirimden** geliyor.

Buradan dersin kuralı çıkıyor: **çıplak bir sayı bir ölçüm değildir.** Ölçüm, sayı ile
sayının neyi saydığını söyleyen bildirimin toplamıdır. Bildirim eksikse elde kalan şey
karşılaştırmaya sokulamaz — çünkü karşı tarafın hangi satırdan okuduğu bilinmez.

#### Üçüncü İddia: 31'e Karşı 37

Orta satır kursun üçüncü iddiasını tek başına taşıyor. Aynı görev kümesi, aynı rejim, aynı
çizelgeleyici: kurulum sayılmadığında **31 tik**, sayıldığında **37 tik**. Fark **6 tik**,
oran **%19,35**.

Hiçbir şey değişmedi. Kod aynı, veri aynı, yuva sayısı aynı, işçi sayısı aynı. Değişen tek
şey ölçümün sınırının nereye çekildiği. Yine de iki sayı, aralarında beşte bire yakın fark
olan iki ayrı sayı.

Bu, bir başarım raporunda görülebilecek en sıradan farktır. Bir eniyilemenin %19'luk kazanç
verdiği söylense, ciddiye alınırdı. Oysa buradaki %19,35'in arkasında hiçbir eniyileme yok
— yalnızca iki farklı sayma sınırı var.

#### Sıralamanın Dönmesi

Alt tablo aynı kusurun karşılaştırmaya nasıl sızdığını gösteriyor. İki rejim yan yana:
sekiz işçili tek yuva ile sekiz işçili dört yuva.

İlk iki satır dürüst bildirimlerdir. Kurulum iki tarafta da sayılmazsa **31**'e karşı
**10**; iki tarafta da sayılırsa **37**'ye karşı **35**. İkisinde de kazanan **çoklu
süreç**. Kurulumun sayılması aradaki farkı 21 tikten 2 tike indiriyor — bildirim sonucu
değiştirmiyor, ama **farkın büyüklüğünü** değiştiriyor.

Üçüncü satır dürüst değil ve tabloda kazananın değiştiği tek satır o. Kurulum yalnız çoklu
süreçte sayıldığında **31**'e karşı **35** çıkıyor ve **iş parçacığı** kazanıyor. Gerçekte
kaybeden rejim, ölçümün sınırı bir tarafta genişletildiği için kazanmış görünüyor.

Böyle bir tablo hiçbir satırında yalan söylemez: 31 doğrudur, 35 doğrudur. Yanlış olan
ikisinin **yan yana konmasıdır**. İki sayı ancak aynı sınırla üretilmişse karşılaştırılır.

Dördüncü satır tersini deniyor ve sıralama dönmüyor: kurulum yalnız iş parçacığında
sayılsa bile çoklu süreç **10**'la kazanıyor. Yani bildirim kusuru her zaman sonucu
çevirmez — **çevirip çevirmediği ancak doğru bildirimle bilinir.** Kusuru fark etmenin tek
yolu, doğru ölçümü de yapmış olmaktır.

#### Kursun Kendi Üzerine

Bu ders kurs boyunca uygulanan bir kararı gerekçelendirmek zorunda: neden hiçbir yerde süre
yazılmadı?

Yanıt üstteki tablonun aynısıdır, bir adım ötesinde. Bir sayının bildirimi eksikse
karşılaştırılamaz; bir sayı ayrıca **yeniden üretilemiyorsa** bildirim de onu kurtarmaz.
Süre, aynı makinede aynı kod için bile iki koşumda iki ayrı değer verir. O değerin hangi
sınırla ölçüldüğünü tam olarak bildirseniz bile, ikinci koşum sayıyı değiştirir.

Adım, tik, çağrı ve nesne bu kusuru taşımaz. Profil çıkarma dersinde aynı koşum üç kez
yinelendi ve çağrı sayısı **322, 322, 322** çıktı. Bellek dersinde altı biçim her koşumda
aynı nesne sayısını verdi. Örtüşen adım kurgudan hesaplanıyor ve kurgu tohumla sabit.

Bu bir üstünlük iddiası değil, bir kapsam kararıdır. Süre ölçümünün yeri vardır ve gerçek
bir sistemde sonunda o da ölçülür; ama **bir kavramı öğretmek için kullanılacak sayının
yeniden üretilebilir olması gerekir.** Bu kurs örtüşen adımı seçti çünkü örtüşme, süre
farkının **nedenidir**; süre ise sonucudur ve nedenden çok daha gürültülüdür.

#### Özet

- Karşılaştırmalı ölçüm üç bildirim ister: sayılan **birim**, ölçülen **rejim** ve ölçümün
  **sınırı**. Üçüncüsü en sık atlanandır.
- Tek bir koşum altı ayrı bildirimle **26, 31, 37, 49, 54, 80** verir; altısı da doğrudur ve
  aralarındaki fark ölçümden değil bildirimden gelir.
- Kurulum sayılmadığında **31**, sayıldığında **37** tik çıkar: aynı iş, aynı rejim, **6
  tik** ve **%19,35** fark, sıfır değişiklik karşılığında.
- Kurulum yalnız bir rejimde sayıldığında sıralama dönüyor: **31**'e karşı **35** ile kaybeden
  rejim kazanmış görünüyor. Hiçbir satır yanlış değil; yanlış olan yan yana konmaları.
- Bildirim kusuru sonucu her zaman çevirmez, ama çevirip çevirmediği ancak doğru bildirimle
  bilinir; kusuru görmek için doğru ölçümü de yapmış olmak gerekir.
- Bu kurs süre yerine adım saydı çünkü bir öğretme sayısının yeniden üretilebilir olması
  gerekir; örtüşen adım farkın nedenidir, süre ise gürültülü sonucudur.

#### Kurs Kapanışı

Kurs tek bir kurguyu on ders boyunca çevirdi: sekiz görev, görev başına on adım, toplam 80
adım. Hiçbir derste süre yazılmadı; sayılan şey adım, tik, örtüşen adım, çağrı ve nesne oldu.

| ders | ölçülen rejim | tik / örtüşen adım ya da çağrı |
|---|---|---|
| Genel Yorumlayıcı Kilidi | tek akış, tek yuva | 80 tik / 0 örtüşen adım |
| İş Parçacıkları | sekiz işçi, tek yuva | 31 / 49 (girdi/çıktı), 73 / 7 (işlemci) |
| Çoklu Süreç | sekiz işçi, dört ve sekiz yuva | 20 / 60 ve 10 / 70 (işlemci) |
| Eşzamansız Programlama | olay döngüsü, tek akış, üç teslim rejimi | 31 / 49, 36 / 44 ve 80 / 0 |
| Eşzamansız Kütüphane Uyumu | olay döngüsü, engelleyen çağrı ve havuza devir | 43 / 37, 80 / 0 ve devirde 44 ya da 90 tik |
| Yürütücü Havuzları | sekiz genişlik, iki arka uç, üç yük | iş parçacığı 31 / 49, 42 / 38, 73 / 7; süreç 10 / 70 |
| Profil Çıkarma | sekiz işçi, tek yuva, iki yük | 80 ilerlet çağrısı; sıcak yol 136 ve 322 çağrı |
| Bellek Kullanımı | sekiz işçi, tek yuva, altı biçim | 31 / 49; ayakta duran nesne 1–172 |
| Yerel Uzantılar | sekiz işçi, tek yuva, yoğunlaştırılmış sıcak yol | 25 / 19 (işlemci), 31 / 49 ve kazanç 0 (girdi/çıktı) |
| Ölçüm Disiplini | sekiz işçi, tek yuva | 31 tike karşı 37 tik |

Tablonun okunuşu tek cümlede toplanır: **80 adım hiç değişmedi, değişen yalnız o adımların
kaç tike dağıldığı oldu.** Rejim işi azaltmaz, örtüştürür; örtüşme de her adımda mümkün
değildir. Bu yüzden aynı düzenek bir yükte 49 tik kazandırırken öbüründe 7 kazandırır ve
bu yüzden sıcak yolu alt seviyeye taşımak bir yükte 48 tik, öbüründe **0** verir.

Son ders bunun üzerine bir katman daha koydu: bu sayıların hiçbiri, neyi saydığı
söylenmeden bir başkasının sayısıyla yan yana konamaz. Sınırını yazmayan bir sayı, doğru
olsa bile karşılaştırılamaz — ve bu kural kursun kendi tablolarına da uygulanır; her satır
hangi rejimde, hangi yükte ve hangi birimle ölçüldüğünü kendi adında taşıyor.

Sonraki kurs, Python Projeleri: Paketleme ve Test, aynı disiplini işin başka iki yüzüne
uygular. Bir kütüphanenin hangi ortamda, hangi bağımlılıklarla ve hangi sürüm kuralıyla
kurulduğunu yazmak, bir ölçümün sınırını yazmakla aynı türden bir iştir; bir testin neyi
sınadığını ve kapsam sayısının neyi söylemediğini bilmek de öyle. **Ölçmek bir disiplindir;
paketlemek ve sınamak da öyle.**

