---
title: Profilleme
source: 'https://academia.sh/tr/kurslar/go-uretim/profilleme'
course: 'Üretim Go: Test, Araçlar ve Başarım'
language: tr
updated: '2026-09-13T05:08:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Aynı işlevin iki çağrı biçiminin ayırma sayılarını AllocsPerOp ile ölçüp karşılaştırır'
    - 'Hiç çalışmayan bir dalın ayırdığı değerlerin profilde hiç görünmediğini koşturarak gösterir'
    - 'Profilin "nerede ayrıldığını" yanıtladığını, "neden yavaş olduğunu" yanıtlamadığını ayırt eder'
---

# Profilleme

Profil bir koşum bilgisidir ve bu derste sayıyla okunur: aynı işlevin iki çağrı biçiminde kaç değer ayrıldığı. Sınırlayıcı ölçüm: profil yalnız koşan yolu görüyor — hiç çalışmayan bir dalın ayırdığı değerler profilde hiç görünmüyor, kaynakta yazılı olsalar bile.

Önceki ders kapsama oranının koşulan satırları saydığını, doğru sınandığını söylemediğini
gösterdi. Bu ders aynı soruyu belleğe soruyor: bir program çalışırken nerede ayırma yaptığı
kaynağa bakılarak görülemiyor, ama koşum bittiğinde bir profil bunu **sayıyla** söylüyor.
Profil de kapsama gibi bir koşum bilgisi — kaynağı okuyan biri hangi işlevin kaç değer
ayırdığını göremez, yalnız çalıştırıp sorabilir.

Bu paylaşılan biçim tesadüfi değil: kursun açılışından beri her ders aynı iskeleti
kullanıyor — bir bilginin kaynakta mı, araçta mı, koşumda mı durduğunu önce sınıflandırmak,
sonra o sınıfa özgü sınırı ölçmek. Profilin buradaki yeri nettir: kapsama gibi, yalnız
koşarak öğreniliyor. Ama kapsamadan farklı olarak, profilin okuduğu şey bir doğru/yanlış
kararı değil bir **miktar** — bu farkın ölçüme nasıl yansıdığı bu dersin konusu.

Eşzamanlılık kursu (M09/K03) profil çıkarmayı ve bir ölçüm disiplinini zaten kurdu: bir ölçüm,
neyi saydığını söylemeden karşılaştırılamaz. Bu ders o disiplini Go'nun kendi araçlarıyla
sürdürüyor; kursun ekseni (örtüşen adım) burada kullanılmıyor. Buradaki soru dar: bir profil size
**nereyi** söylüyor, **neyi** söylemiyor?

## Üç Profil Türü, Bir Sayı

Katalog bu konuyu üç profil türüyle tanımlıyor: işlemci profili, bellek profili ve
engelleme profili. Üçü de dilin kendi ölçüm paketiyle üretilebiliyor ama üçü aynı türde
bilgi taşımıyor. İşlemci profili, programın zamanını hangi işlevde geçirdiğini örnekleyerek
ölçüyor — bu ölçümün doğası gereği **süre**dir ve süre bu kursun hiçbir dersinde
yazılmıyor, basılmıyor, ölçülmüyor. Engelleme profili de benzer bir örnekleme yapıyor: bir
goroutine'in ne kadar süre bir kilit ya da kanal beklediğini ölçüyor, yine süre temelli.

Bellek profili farklı bir soruya yanıt veriyor: **kaç** değer ayrıldığı, **ne kadar süre**
geçtiği değil. Bu, bu kursun sayabildiği tek profil türü — sayısı bir koşumdan diğerine
sabit kalıyor, süre gibi makineye ve o anki yüke bağlı değil. Bu ders bu yüzden yalnız
bellek profiline ve onun da tek bir özetine — işlem başına ayırma sayısına — odaklanıyor.
İşlemci ve engelleme profilleri var olmaya devam ediyor; burada ölçülmüyorlar, çünkü
ölçtükleri şey süre.

## Profil: Kaç Değer, Hangi Çağrıda

Bu derste "profil" sözcüğü tek bir sayıya indiriliyor: bir çağrının işlem başına kaç değer
ayırdığı. Bu sayı, dilin kendi ölçüm paketiyle **program içinden** okunuyor — bellek baytı,
süre ya da yığıt izi hiç basılmıyor.

```go
// profil.go — ayni islevin iki cagri bicimi, profilde kac deger ayirdigini gosteriyor
package main

import "fmt"

type Kayit struct {
	Ad     string
	Miktar int
}

// bicimle bir kaydi tek satira ceviriyor. ayrintili istendiginde ekstra bir
// alan dizisi olusturuyor; istenmediginde o govde hic calismiyor.
func bicimle(k Kayit, ayrintili bool) string {
	satir := fmt.Sprintf("%s:%d", k.Ad, k.Miktar)
	if ayrintili {
		alanlar := make([]string, 0, 3)
		alanlar = append(alanlar, fmt.Sprintf("ad=%s", k.Ad))
		alanlar = append(alanlar, fmt.Sprintf("miktar=%d", k.Miktar))
		alanlar = append(alanlar, fmt.Sprintf("uzunluk=%d", len(k.Ad)))
		for _, a := range alanlar {
			satir += " " + a
		}
	}
	return satir
}
```

**BS1.** `bicimle` tek bir işlev ama iki ayrı yol taşıyor: `ayrintili` yanlışken yalnız
ilk satır çalışıyor, doğruyken üç ayrı biçimlendirme ve bir birleştirme döngüsü ekleniyor.
Kaynağı okuyan biri iki yolun da var olduğunu görür ama hangisinin ne kadar ayırdığını
göremez — bu, koşturmadan bilinemeyen bir bilgi.

## İki Çağrı Biçimini Ölçmek

`bicimle`nin kendisi hiçbir şey ölçmüyor — yalnız bir sonuç üretiyor. Ölçüm ayrı bir katmanda
duruyor: dilin kendi karşılaştırmalı ölçüm işlevi (`func(*testing.B)`) çağrıyı `b.N` kez
tekrarlıyor; ölçüm paketi bu tekrarlar sırasında kaç değerin öbeğe taşındığını sayıyor ve sonuçta
işlem başına ortalama bir sayı veriyor. Aşağıdaki dosya iki ayrı karşılaştırmalı ölçüm işlevi
tanımlıyor — biri `ayrintili=false`, öbürü `ayrintili=true` çağırıyor — ve ikisini de beşer kez
koşturuyor.

```go
// olcum.go — ayni islevin iki cagrisi, bes kosumda ayirma sayisi
package main

import (
	"fmt"
	"testing"
)

var ornekKayit = Kayit{Ad: "vida", Miktar: 12}

func profilAyrintisiz(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_ = bicimle(ornekKayit, false)
	}
}

func profilAyrintili(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_ = bicimle(ornekKayit, true)
	}
}

func besKosum(f func(*testing.B)) (int64, int) {
	degerler := map[int64]bool{}
	var ilk int64
	for i := 0; i < 5; i++ {
		r := testing.Benchmark(f)
		degerler[r.AllocsPerOp()] = true
		if i == 0 {
			ilk = r.AllocsPerOp()
		}
	}
	return ilk, len(degerler)
}

func main() {
	fmt.Println("-- profil: kac deger ayrildi --")
	ayrintisiz, ad1 := besKosum(profilAyrintisiz)
	ayrintili, ad2 := besKosum(profilAyrintili)
	fmt.Printf("%-24s ayirma=%-3d bes kosumda ayri deger=%d\n", "ayrintisiz cagri", ayrintisiz, ad1)
	fmt.Printf("%-24s ayirma=%-3d bes kosumda ayri deger=%d\n", "ayrintili cagri", ayrintili, ad2)
	fmt.Println("fark:", ayrintili-ayrintisiz, "ayirma, yalniz ayrintili dala ait")
}
```

```
-- profil: kac deger ayrildi --
ayrintisiz cagri         ayirma=2   bes kosumda ayri deger=1
ayrintili cagri          ayirma=9   bes kosumda ayri deger=1
fark: 7 ayirma, yalniz ayrintili dala ait
```

**BS2.** `besKosum`, kursun her başarım dersinde geçerli olan kalıbı burada da uyguluyor:
aynı ölçüm beş kez tekrarlanıyor ve kaç ayrı değer geldiği sayılıyor. Süre burada hiç
okunmuyor; `testing.Benchmark`in döndürdüğü sonuçtan yalnız `AllocsPerOp` alınıyor. Beş
koşumun beşinde de tek bir değer çıkması, bu sayının koşumdan koşuma sabit olduğunu — süre
gibi ortama bağlı olmadığını — gösteriyor.

`degerler` bir `map[int64]bool` olarak tutuluyor, tek bir sayaç değil; bunun nedeni beş
koşumun aynı değeri verip vermediğini **kanıtlamak**, varsaymamak. Beş koşum aynı sayıyı
verirse `degerler` tek bir anahtar taşır ve `len(degerler)` bir çıkar; farklı bir sayı
gelseydi ikinci bir anahtar eklenir ve bu sayı ikiye çıkardı — tıpkı kursun ortak
çekirdeğinde süre ölçümünün sekiz koşumun ikisinde farklı çıkıp çerçeveden çıkarılması gibi.
Ayırma sayısı için bu hiç olmuyor; aşağıdaki çıktıda iki satırın da `ayri deger=1` vermesi bu
iddiayı doğruluyor.

## Bir İşlevin İçini Profillemek, Bir Servisi Profillemek

Yukarıdaki ölçüm bir profil aracının küçültülmüş bir biçimi. Üretimde çalışan bir servisi
profillemek, gerçek trafik altında hangi işlevin ne kadar bellek ayırdığını **örnekleyerek**
ölçüyor — servis saatlerce çalışıyor, profil aracı bu süre boyunca ayırmaları topluyor ve
sonunda bir dağılım veriyor. Bu dağılım gerçek kullanıcı girdisini yansıtıyor ama koşumdan
koşuma aynı çıkmıyor: bir gün daha çok ayrıntılı kayıt gelirse profil de değişiyor.

Bu derste kullanılan yöntem farklı bir şey yapıyor: tek bir işlevi, tek bir girdiyle, beş kez
koşturup sabit bir sayı arıyor. Bu, bir servisin profilini çıkarmanın yerini tutmuyor —
küçük, yalıtılmış ve yeniden üretilebilir bir ölçüm. Aradaki fark önemli: üretim profili
"gerçekte ne oluyor" sorusuna yanıtlıyor ve yanıtı trafiğe göre değişiyor; buradaki ölçüm "bu
belirli girdiyle bu işlev ne yapıyor" sorusuna yanıtlıyor ve yanıtı sabit. İkisi de profil
sözcüğünü taşıyor, ikisi de koşum bilgisi, ama biri tekrarlanabilir bir deney, öbürü sürekli
bir gözlem.

Bu daraltmanın bir bedeli var: yalıtılmış bir işlevin ölçümü, o işlevin gerçek bir servis
içindeki payını göstermiyor. `bicimle` bir isteğin yüzde biri kadar zaman alıyor olabilir ya
da yüzde doksanı — bu ders bunu hiç söylemiyor, çünkü ölçüm işlevi bağlamından koparıyor. Bir
üretim profilinin asıl gücü tam olarak burada: hangi işlevin **toplam** ayırmanın ne kadarını
oluşturduğunu, bütün program çalışırken gösteriyor. Bu derste kurulan yöntem, o büyük resmin
içindeki tek bir karoyu büyüteçle incelemenin bir modeli — büyük resmin kendisi değil.

## Profil ile Karşılaştırmalı Ölçüm Arasındaki Fark

Kursun ilerleyen bir dersi de aynı ölçüm aracını (`testing.Benchmark`, `AllocsPerOp`)
kullanıyor ama farklı bir soru soruyor: aynı işin iki ayrı **yazımı** hangisi daha az
ayırıyor. Bu ders o soruyu sormuyor — burada tek bir işlev var, `bicimle` ve iki çağrı
biçimi onun **iki dalını** ayırıyor. Karşılaştırmalı ölçüm iki ayrı gerçekleştirimi kıyaslar;
profilleme tek bir gerçekleştirimin neresinin ayırdığını gösterir. Araç aynı, soru farklı:
biri "hangisi daha iyi" diye soruyor, öbürü "nerede" diye soruyor. Bu ayrım önemli çünkü aynı
sayı (`AllocsPerOp`) iki bağlamda da çıkıyor ve hangi soruya yanıt verdiği yalnız ölçümün
kurulumundan anlaşılıyor.

## Sayıların Okunması

`ayrintisiz` çağrı iki değer ayırıyor: `fmt.Sprintf("%s:%d", ...)` çağrısının kendisi bir
dizgi üretiyor ve bunu yaparken en az bir ara değeri öbeğe taşıyor. `ayrintili` çağrı dokuz
değer ayırıyor. Aradaki yedi ayırma tümüyle `if ayrintili` bloğuna ait: üç ayrı
`fmt.Sprintf` çağrısı, bir dilim ayırması ve döngü içindeki birleştirmelerin bir kısmı.
Kaynakta bu iki yol da aynı işlevin gövdesinde yan yana duruyor; profil olmadan hangisinin
ne kadar ayırdığı bilinemez.

Bu, profilin yanıtladığı soruyu netleştiriyor: **nerede** ayrılıyor. Hangi çağrı biçimi
daha çok değer ayırıyor, ölçümden doğrudan okunuyor. Ama profil **neden** yavaş olduğunu
söylemiyor — bu derste hiç ölçülmeyen bir soru, çünkü yanıtı süreye bakmayı gerektiriyor
ve süre bu kursta hiçbir derste yazılmıyor. Ayırma sayısının yüksek olması programın yavaş
çalıştığı anlamına gelmez; yalnız o yolun daha çok bellek istediği anlamına gelir. İkisi
genelde birlikte gider ama profilin kendisi bu ikisini ayırmıyor — ayırma sayısını okuyan
kişi ayırıyor.

`ayrintisiz` çağrının iki değer ayırması da başlı başına bir okuma dersi. Kaynakta tek bir
`fmt.Sprintf` çağrısı görünüyor, ama bu tek satır görünenden fazla adımı gizliyor: `Sprintf`
yeni bir dizgi üretiyor, üstelik `k.Miktar` gibi bir tam sayıyı değişken sayıda argüman alan
parametresine (`...any`) geçirirken onu bir arayüz değerine sarıyor. Kaynakta hiçbir
işaretçi, hiçbir `new`, hiçbir `append` görünmediği hâlde iki ayrı ayırmaya yol açan tam
olarak bu iki adım — dizgi üretimi ve arayüze sarma. Hangi değerin ne zaman böyle sarıldığı,
dilin kendi çağrı biçiminin bir sonucu ve kaçış çözümlemesinin verdiği bir karar. O kararın
kendisi sıradaki dersin konusu; bu ders yalnız kaç değerin ayrıldığını sayıyor, hangi
kararın buna yol açtığını değil.

Kursun açılışı on bir bilgiyi kaynak, araç ve koşum olarak üçe ayırmıştı; bu ders o üç
sınıftan yalnız koşumu kullanıyor, çünkü profil sayısı ancak çalıştırılarak öğreniliyor.

## Sınırlayıcı Ölçüm: Koşmayan Yol Profilde Yok

Yukarıdaki tabloda `ayrintisiz` satırı iki değer gösteriyor — üç `Sprintf` çağrısının,
dilim ayırmasının ve birleştirme döngüsünün **hiçbiri** bu satıra girmiyor. Bunun nedeni bu
değerlerin küçük olması ya da derleyicinin onları optimize etmesi değil: `ayrintili`
yanlış geçildiğinde `if` bloğunun **gövdesi hiç çalışmıyor**. Kaynakta yedi ayırmaya
karşılık gelen kod duruyor, ama `profilAyrintisiz` onu hiç tetiklemiyor; profil de
tetiklenmeyen hiçbir şeyi göremiyor.

**BS3.** Bu, profil çıkarmanın kapsama oranıyla (önceki ders) paylaştığı sınır: ikisi de
yalnız **koşan** yolu ölçüyor. Kapsama oranı koşulmayan satırı saymıyordu; profil koşulmayan
dalın ayırmasını saymıyor. Bir dalın "az ayırdığı" sonucuna varmadan önce o dalın gerçekten
tetiklendiğinden emin olmak gerekiyor — aksi hâlde ölçülen şey o dalın davranışı değil,
hiç çalışmamış olmasıdır. Fark biçimindeki benzerlik de kayda değer: kapsama oranı
koşulmayan satırı sıfır sayarak görmezden geliyordu, profil de koşulmayan dalın
ayırmasını sıfır sayarak görmezden geliyor — ikisi de "yok" ile "ölçülmedi"yi aynı
sembolle (`0`) yazıyor ve okuyan tarafın bu ikisini birbirinden ayırması gerekiyor.

Bu sınır pratikte iki yönlü çalışıyor. Bir yandan, seyrek çalışan bir dal (örneğin yalnız
hata durumunda giren bir kod yolu) günlük profillerde hiç görünmeyebilir ve gözden kaçabilir.
Öte yandan, iki çağrı biçimini karşılaştırırken kullanılan girdinin her iki dalı da gerçekten
tetiklediğinden emin olunmalı; aksi hâlde karşılaştırma yanlış yola bakıyor demektir. Yukarıdaki
ölçümde `profilAyrintisiz` yalnız `ayrintili=false`, `profilAyrintili` yalnız `ayrintili=true`
çağırıyor — bu seçim bilinçli, çünkü karşılaştırmanın amacı tam olarak bu iki dalı ayrı ayrı
görmek.

Aynı sınır, tek bir çağrı biçiminin içinde de geçerli. `bicimle` yalnız iki durumla
çağrıldı: `ayrintili` ya tümüyle doğru ya tümüyle yanlış. Gerçek bir çağıran kümesi bu
işlevi karma bir dağılımla çağırabilir — kayıtların onda biri ayrıntılı, geri kalanı değil —
ve o dağılımın ürettiği ortalama ayırma sayısı yukarıdaki iki uçtan hiçbirine eşit olmaz.
Profil, ölçüldüğü girdiyi anlatıyor; başka bir girdi dağılımının profili başka bir tablo
üretir. Bu yüzden bir profil sayısı, hangi girdiyle üretildiği yazılmadan aktarılamaz —
tıpkı önceki dersteki kapsama oranının hangi test kümesiyle üretildiği yazılmadan
aktarılamaması gibi.

## Sayı Neye Karar Verdirmemeli

İki satırlık tablo tek başına bir eylem önermiyor. `ayrintili` çağrının yedi değer daha çok
ayırması, `ayrintili` dalının kaldırılması gerektiği anlamına gelmiyor — o dal muhtemelen
gerçek bir ihtiyacı karşılıyor, üç ayrı alanı ayrı ayrı biçimlendirmek başka bir yerde
gerçekten kullanılıyor olabilir. Profil sayısı yalnız **nerede** ayrıldığını gösteriyor;
o ayırmanın gerekli olup olmadığına, hangi çağıranın hangi sıklıkla hangi dalı kullandığına
bakmadan karar verilmiyor. Bu, sıradaki dersin de tekrar edeceği bir uyarı: bir sayının
küçülmesi otomatik olarak bir iyileşme değil, çünkü sayı tek başına neyin karşılığında
küçüldüğünü söylemiyor.

Bu, kursun ilk dersinden beri süren bir kalıbın da tekrarı: bir aracın söylediği bilgi,
aracın **neyi ölçtüğüyle** sınırlı. Kapsam aracı yalnız hangi satırın koştuğunu söylüyordu,
doğruluğu değil; profil aracı yalnız hangi çağrının kaç değer ayırdığını söylüyor, o
ayırmanın gerekip gerekmediğini değil. Her iki durumda da sınırı aşan yorum, aracın
kendisinden değil, okuyan kişinin aracın sınırını unutmasından geliyor.

## Özet

- Profil bir koşum bilgisidir: hangi çağrının kaç değer ayırdığı kaynağa bakılarak değil,
  yalnız çalıştırılarak öğreniliyor.
- Aynı işlevin iki çağrı biçimi (`ayrintisiz`, `ayrintili`) `AllocsPerOp` ile ölçüldü;
  ayrıntılı çağrı yedi değer daha çok ayırdı ve bu fark beş koşumda değişmedi.
- Profil **nerede** ayrıldığını yanıtlıyor, **neden yavaş** olduğunu yanıtlamıyor — süre bu
  kursta hiçbir derste ölçülmüyor.
- Koşmayan bir dalın ayırdığı değerler profilde hiç görünmüyor; kaynakta duran kod bunu
  değiştirmiyor, yalnız o yolun gerçekten çalışması değiştiriyor.
- Bu sınır, önceki dersteki kapsama oranının sınırıyla aynı biçimde: ikisi de yalnız koşan
  yolu ölçüyor.

## Sonraki Adım

Profil bir değerin nereye ayrıldığını koşumdan sonra söylüyor. Ama bir değerin yığıtta mı
öbekte mi duracağı kararı koşumdan çok önce, kaynak daha derlenirken veriliyor — bu ders bu
kararın **sonucunu** sayıyla gördü, kararın kendisini hiç görmedi. Sıradaki ders bu kararın
mekanizmasına bakıyor: derleyici bu kararı nasıl bildiriyor, hangi kalıplar bir değeri
öbeğe taşıyor ve aynı işlevin küçük bir değişiklikle kararı nasıl değiştiği.
