İçeriğe geç
academia.sh

Ders 01 / 10

Genel Yorumlayıcı 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.

İçindekiler

Ö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.
  • EM10iz, 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

"""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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat