---
title: 'Profil Çıkarma'
source: 'https://academia.sh/tr/kurslar/python-eszamanlilik/profil-cikarma'
course: 'Eşzamanlılık ve Başarım'
language: tr
updated: '2026-08-17T18:10:25+00:00'
license: 'CC BY-SA 4.0'
---

# Profil Çıkarma

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.
