---
title: 'Karşılaştırmalı Ölçüm'
source: 'https://academia.sh/tr/kurslar/go-uretim/karsilastirmali-olcum'
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:
    - 'On bir bilgiyi kaynak, araç ve koşum sınıflarına ayırıp 2/5/4 dağılımını koşturarak üretir'
    - 'Aynı işin iki yazımının ayırma sayısını beş koşumda karşılaştırıp hangisinin tekrarlanabilir olduğunu okur'
    - 'İş başına ayırma ile toplam ayırma sayısının ayrı ölçüler olduğunu, aynı karşılaştırmayı iki biçimde okuyarak sınırlar'
---

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

On bir bilgi bir programda toplanıyor ve üç söyleyene bölünüyor — 2'si kaynakta yazılı, 5'i araçtan koşum olmadan geliyor, 4'ü yalnız koşarak öğreniliyor. Karşılaştırmalı ölçüm aracı bu dört koşum bilgisinden birini üretiyor: aynı işin iki yazımının ayırma sayısı, beş koşumda hep aynı değeri veriyor; sınırlayıcı ölçüm bu sayının da neyi saydığını söylemeden karşılaştırılamayacağını gösteriyor.

Önceki dört ders, bir programa dair tek tek sorular sordu: bir test geçti mi, bir vaka
kaç alt teste döndü, bir istek hangi durum kodunu tetikledi, bir bağımlılığın gerçeği ile
sahtesi aynı kararı verdi mi. Her soru kendi kaynak/araç/koşum sınıfını taşıyordu, ama
hiçbiri bu sınıfların **toplamda** nasıl dağıldığını sormadı. Bu ders bir adım geri
çekiliyor ve aynı soruyu topluca soruyor: bir program hakkında bilinebilecek on bir ayrı
şeyi bir arada toplarsak, kaçı kaynakta yazılı, kaçını bir araç koşum olmadan söylüyor,
kaçı yalnız koşarak ortaya çıkıyor?

Bu derste ayrıca kursun adının işaret ettiği ölçüm — karşılaştırmalı ölçüm — ilk kez
ortaya çıkıyor. Bu ölçüm türü kendi başına iki sayı üretiyor: bir işin ne kadar bellek
ayırdığı ve ne kadar sürdüğü. Bu kursta yalnız birincisi kullanılıyor; ikincisinin neden
dışarıda bırakıldığı bu dersin ikinci yarısının konusu.

## On Bir Bilgi, Üç Söyleyen

Aşağıdaki ölçüm küçük bir kaynağı — bir yapı, iki işlev, üç vakalık bir test tablosu, bir
derleme etiketiyle korunan fazladan bir dosya — geçici bir modüle yazıyor, sonra bu tek
kaynak üzerinde on bir ayrı soru soruyor. Her soru `gozle` yordamına, sorunun yanıtını
kimin verdiğiyle (kaynak, araç, koşum) birlikte veriliyor; `gozle` bu üç sınıfı sayıyor.
Bu, önceki dört derste ayrı ayrı sorulan soruların hepsini tek bir çatı altında topluyor;
her soru kendi başına daha önce görülmüş bir düzenekle (kaynağı bir dizgi olarak okuyup
saymak, bir aracı program içinden çağırıp yalnız sonucunu okumak) yanıtlanıyor.

```go
// araclar.go — bu bilgiyi kim soyluyor cercevesinin ortak yordamlari ve iki karsilastirma yazimi
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

var kaynak, arac, kosum int

func gozle(bilgi, soyleyen, kanit string) {
	switch soyleyen {
	case "kaynak":
		kaynak++
	case "arac":
		arac++
	default:
		kosum++
	}
	fmt.Printf("%-34s %-7s %s\n", bilgi, soyleyen, kanit)
}

func paket(dosyalar map[string]string) string {
	dizin, _ := os.MkdirTemp("", "olcum")
	os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module olcum\n\ngo 1.24\n"), 0o644)
	for ad, icerik := range dosyalar {
		os.WriteFile(filepath.Join(dizin, ad), []byte(icerik), 0o644)
	}
	return dizin
}

func aracCalistir(dizin string, ortam []string, arg ...string) (string, bool) {
	cmd := exec.Command("go", arg...)
	cmd.Dir = dizin
	cmd.Env = append(append(os.Environ(), "GOTOOLCHAIN=local"), ortam...)
	cikti, err := cmd.CombinedOutput()
	return string(cikti), err == nil
}

func kacanSayisi(cikti string) int { return strings.Count(cikti, "escapes to heap") }

func kapsamOrani(cikti string) string {
	for _, s := range strings.Split(cikti, "\n") {
		if i := strings.Index(s, "coverage: "); i >= 0 {
			return strings.TrimSpace(s[i+len("coverage: "):])
		}
	}
	return "yok"
}

// dilimBuyut onceden yer ayirmadan sekiz kez ekliyor.
func dilimBuyut(b *testing.B) {
	for i := 0; i < b.N; i++ {
		var d []int
		for j := 0; j < 8; j++ {
			d = append(d, j)
		}
		_ = d
	}
}

// dilimOnceden ayni isi, sekiz elemanlik yeri once ayirarak yapiyor.
func dilimOnceden(b *testing.B) {
	for i := 0; i < b.N; i++ {
		d := make([]int, 0, 8)
		for j := 0; j < 8; j++ {
			d = append(d, j)
		}
		_ = d
	}
}

// besKosumda ayni karsilastirmali olcumu bes kez kosturur; ilk deger ve kac ayri
// deger gorulduguyle dondurur.
func besKosumda(k func(*testing.B)) (ilk int64, ayriDeger int) {
	gorulen := map[int64]bool{}
	for i := 0; i < 5; i++ {
		r := testing.Benchmark(k)
		gorulen[r.AllocsPerOp()] = true
		if i == 0 {
			ilk = r.AllocsPerOp()
		}
	}
	return ilk, len(gorulen)
}
```

**TS13.** Sorulan on bir sorunun ikisi kaynaktan yanıtlanıyor: test tablosundaki vaka
sayısı ve modülün dil sürümü — ikisi de metnin kendisinde yazılı, hiçbir şey
çalıştırmadan okunabiliyor.

```go
// olcum.go — on bir bilgi, uc soyleyene bolunuyor
package main

import (
	"fmt"
	"os"
	"strings"
)

const kaynakMetni = `package main

import "fmt"

type Kayit struct{ Ad string }

func yerelKalan() int {
	k := Kayit{Ad: "vida"}
	return len(k.Ad)
}

func kacan() *Kayit {
	k := Kayit{Ad: "somun"}
	return &k
}

var surum = "bilinmiyor"

func main() {
	fmt.Println(yerelKalan(), kacan().Ad, surum)
}
`

const testMetni = `package main

import "testing"

var vakalar = []struct {
	ad      string
	girdi   string
	uzunluk int
}{
	{"kisa", "ab", 2},
	{"orta", "abcd", 4},
	{"bos", "", 0},
}

func TestUzunluk(t *testing.T) {
	for _, v := range vakalar {
		if len(v.girdi) != v.uzunluk {
			t.Errorf("%s: %d beklendi", v.ad, v.uzunluk)
		}
	}
	if yerelKalan() != 4 {
		t.Error("yerelKalan")
	}
	if kacan().Ad != "somun" {
		t.Error("kacan")
	}
}
`

const etiketliMetin = `//go:build ek

package main

func ek() int { return 1 }
`

func main() {
	fmt.Println("-- bu bilgiyi kim soyluyor --")

	dizin := paket(map[string]string{"ana.go": kaynakMetni, "ana_test.go": testMetni, "ek.go": etiketliMetin})
	defer os.RemoveAll(dizin)

	gozle("tablo vaka sayisi", "kaynak", fmt.Sprint(strings.Count(testMetni, "\t{\"")))
	gozle("modul dil surumu", "kaynak", "go.mod satirinda yazili")

	kacisCikti, _ := aracCalistir(dizin, nil, "build", "-gcflags=-m", "-o", os.DevNull, ".")
	gozle("obege kacan deger sayisi", "arac", fmt.Sprint(kacanSayisi(kacisCikti)))

	_, vetTemiz := aracCalistir(dizin, nil, "vet", "./...")
	gozle("statik cozumleme bulgusu", "arac", fmt.Sprint("temiz=", vetTemiz))

	_, caprazOldu := aracCalistir(dizin, []string{"GOOS=linux", "GOARCH=arm64"}, "build", "-o", os.DevNull, ".")
	gozle("baska platforma derlenebilirlik", "arac", fmt.Sprint("derlendi=", caprazOldu))

	etiketsiz, _ := aracCalistir(dizin, nil, "list", "-f", "{{len .GoFiles}}", ".")
	etiketli, _ := aracCalistir(dizin, nil, "list", "-tags", "ek", "-f", "{{len .GoFiles}}", ".")
	gozle("etiketle degisen dosya sayisi", "arac",
		fmt.Sprint("etiketsiz=", strings.TrimSpace(etiketsiz), " etiketli=", strings.TrimSpace(etiketli)))

	_, testGecti := aracCalistir(dizin, nil, "test", "./...")
	gozle("testin verdigi karar", "kosum", fmt.Sprint("gecti=", testGecti))

	kapsamCikti, _ := aracCalistir(dizin, nil, "test", "-cover", "./...")
	gozle("kapsanan satir orani", "kosum", kapsamOrani(kapsamCikti))

	bagliCikti, _ := aracCalistir(dizin, nil, "run", "-ldflags=-X main.surum=1", ".")
	alanlar := strings.Fields(strings.TrimSpace(bagliCikti))
	gozle("ikiliye gomulen surum bilgisi", "arac", alanlar[len(alanlar)-1])

	ilkAyirma, ayriDeger := besKosumda(dilimBuyut)
	gozle("ayirma sayisi", "kosum", fmt.Sprint(ilkAyirma, " bes kosumda ayri deger=", ayriDeger))

	suzgecCikti, _ := aracCalistir(dizin, nil, "test", "-run", "TestUzunluk", "-v", "./...")
	gozle("suzgecin sectigi test sayisi", "kosum", fmt.Sprint(strings.Count(suzgecCikti, "--- PASS")))

	fmt.Printf("bilgi: %d kaynak, %d arac, %d kosum\n", kaynak, arac, kosum)
}
```

```
-- bu bilgiyi kim soyluyor --
tablo vaka sayisi                  kaynak  3
modul dil surumu                   kaynak  go.mod satirinda yazili
obege kacan deger sayisi           arac    3
statik cozumleme bulgusu           arac    temiz=true
baska platforma derlenebilirlik    arac    derlendi=true
etiketle degisen dosya sayisi      arac    etiketsiz=1 etiketli=2
testin verdigi karar               kosum   gecti=true
kapsanan satir orani               kosum   80.0% of statements
ikiliye gomulen surum bilgisi      arac    1
ayirma sayisi                      kosum   4 bes kosumda ayri deger=1
suzgecin sectigi test sayisi       kosum   1
bilgi: 2 kaynak, 5 arac, 4 kosum
```

**TS14.** Beş bilgi bir araçtan geliyor: kaçan değer sayısı, statik çözümlemenin bulgusu,
başka platforma derlenebilirlik, etiketle değişen dosya sayısı, ikiliye gömülen sürüm
bilgisi. Bu beşinin ortak özelliği, **koşum gerektirmemeleri** — derleyici, inceleyici,
çözümleyici ve bağlayıcı her çağrıldığında aynı kaynağa aynı yanıtı veriyor.

**TS15.** Geri kalan dört bilgi yalnız koşarak öğreniliyor: testin verdiği karar, kapsanan
satır oranı, ayırma sayısı, süzgecin seçtiği test sayısı. On bir bilginin toplamı bu
derste ilk kez tek bir koşumda birikiyor: **2 kaynak, 5 araç, 4 koşum**. Bu, konunun
bağlayıcı dağılımı — bu sayı bundan sonraki her derste tek bir cümleyle anılıyor, yeniden
üretilmiyor.

Bu on bir satırın her biri, önceki dört dersin sorduğu sorularla aynı biçimde soruluyor
— yalnız bu kez hepsi tek bir koşumda, tek bir kaynağa karşı. `tablo vaka sayisi` satırı
Tablo Güdümlü Testler'in sorusunu, `testin verdigi karar` satırı Test Paketi
Temelleri'nin sorusunu aynen tekrarlıyor; aradaki fark, burada bu soruların hepsinin
aynı koşumda toplanıp bir dağılıma dönüştürülmesi. On bir bilginin **dokuzu** (araç ve
koşum toplamı) koşum ya da en az bir araç çağrısı gerektiriyor; kaynağı tek başına
okumak bu on birden yalnız ikisini veriyor — programın kendisi hakkında bilinebileceklerin
büyük çoğunluğu, metnin dışında duruyor.

## Karşılaştırmalı Ölçümün İki Sayısından Yalnız Biri Tekrarlanabilir

Yukarıdaki on bir satırın onuncusu — ayırma sayısı — bu dersin asıl konusu ve geri kalan
onundan farklı bir biçimde ölçülüyor: `testing.Benchmark` aynı işi beş kez koşturuyor,
`AllocsPerOp` her koşumdan bir sayı döndürüyor ve bu beş sayının kaç **ayrı** değere
düştüğü ayrıca sayılıyor. Yukarıdaki koşumda bu sayı 1 — beş koşum, tek bir değer.

`dilimBuyut` yukarıdaki tam koşumda zaten bir kez kullanılmıştı — "ayirma sayisi"
satırının kanıtı oydu. Aşağıdaki ölçüm onu yeniden çağırıyor, ama bu kez tek başına değil,
karşılaştıracağı ikinci bir yazımla birlikte.

**TS16.** Aşağıdaki ölçüm aynı işin iki yazımını karşılaştırıyor: `dilimBuyut` sekiz
elemanı önceden yer ayırmadan ekliyor, `dilimOnceden` aynı sekiz elemanı önceden ayrılmış
bir yere ekliyor. İkisi de beşer kez koşturuluyor.

```go
// karsilastirma.go — ayni isin iki yazimi, ayirma sayisiyla karsilastiriliyor
package main

import "fmt"

func main() {
	fmt.Println("-- karsilastirmali olcum: ayirma sayisi ile iki yazim --")

	onceliksizIlk, onceliksizAyri := besKosumda(dilimBuyut)
	fmt.Println("onceden yer ayirmadan, is basina ayirma:", onceliksizIlk,
		"bes kosumda ayri deger:", onceliksizAyri)

	oncedenIlk, oncedenAyri := besKosumda(dilimOnceden)
	fmt.Println("onceden yer ayirarak, is basina ayirma:", oncedenIlk,
		"bes kosumda ayri deger:", oncedenAyri)
}
```

```
-- karsilastirmali olcum: ayirma sayisi ile iki yazim --
onceden yer ayirmadan, is basina ayirma: 4 bes kosumda ayri deger: 1
onceden yer ayirarak, is basina ayirma: 0 bes kosumda ayri deger: 1
```

Ölçüm aracının kendisi burada, tıpkı önceki koşumda olduğu gibi, iki ayrı sayı döndürüyor — iş
başına ayırma sayısı ve geçen süre. Bu ders **süreyi hiçbir yerde basmıyor, ölçmüyor,
karşılaştırmıyor**: `testing.Benchmark`'ın döndürdüğü değerden yalnız `AllocsPerOp` okunuyor;
süreyi taşıyan alan hiç kullanılmıyor. Bunun nedeni bir tercih değil, bir gözlem: süre aynı
makinede, aynı kaynakla, art arda beş kez ölçülse bile koşumdan koşuma değişiyor — işletim
sisteminin o an başka neyi çalıştırdığına, önbelleğin o anki durumuna, hatta ölçümün kendisinin
makineyi ne kadar ısıttığına bağlı. Ayırma sayısı böyle değil: `dilimBuyut` her koşumda tam olarak
dört kez bellek ayırıyor, çünkü dilimin alt dizisi sekiz elemana ulaşana kadar aynı büyüme
kararlarından geçiyor ve bu karar zinciri koşumdan koşuma değişmiyor.

`dilimOnceden` ise sıfır kez ayırıyor — sekiz elemanlık yer `make([]int, 0, 8)` ile bir
kerede ayrılıyor ve derleyicinin kaçış çözümlemesi (bu ayrımın kendisi konunun sonraki
konusunda ölçülüyor) bu diziyi hiç öbeğe taşımadan yığıtta tutabiliyor. Dört ile sıfır
arasındaki fark, önceden yer ayırmanın somut karşılığı: aynı sekiz eleman, aynı sırayla
ekleniyor, ama biri dilimin kendi büyüme mekanizmasına (kapasitesi doldukça iki katına
çıkarma) bırakılırken öbürü baştan yeteri kadar yer istiyor.

## Sınırlayıcı Ölçüm: Ayırma Sayısı da Neyi Saydığını Söylemeden Karşılaştırılamaz

**TS17.** İki yazımın ayırma sayısı — 4 ve 0 — kendi başına "biri daha iyi" demiyor;
hangi sayının **neyi** saydığı söylenmeden bu iki değer karşılaştırılamıyor.
`AllocsPerOp`'un döndürdüğü sayı **iş başına** ayırma: `testing.Benchmark`, `b.N`'i
kendi belirliyor ve iç döngüyü o kadar kez koşturup toplam ayırmayı `b.N`'e bölerek bu
sayıyı üretiyor. Toplam ayırma sayısı — bütün koşum boyunca kaç kez bellek ayrıldığı —
ayrı bir sayı ve `b.N` büyüdükçe büyüyor; iş başına sayı sabit kalırken toplam sayı
hiç sabit kalmıyor.

Bu ayrım gözden kaçtığında yanıltıcı bir karşılaştırma doğuyor: "birinci yazım ikinci
yazımdan dört kat fazla bellek ayırıyor" cümlesi doğru, ama "birinci yazım toplamda dört
kat fazla bellek ayırdı" cümlesi başka bir iddia — ikinci cümle, iki koşumun `b.N`
değerinin aynı olduğunu varsayıyor ve bu varsayım hiçbir yerde denetlenmedi. Bu dersin
ölçtüğü sayı hep iş başına ayırma; toplam ayırma bu derste hiç hesaplanmadı, çünkü
konunun sorusu "hangi yazım her bir işlemde daha az bellek ayırıyor" — "bu koşum toplamda
ne kadar bellek ayırdı" değil. Bir karşılaştırmalı ölçüm sonucu paylaşılırken bu iki
sayının hangisinin kastedildiği söylenmezse, aynı iki sayı (4 ve 0) tamamen farklı bir
iddiaya kanıt gösterilebilir.

`besKosumda` yordamının kendisi de bu ayrımı örtük olarak taşıyor: döndürdüğü `ilk`
değeri her koşumun `AllocsPerOp`'u — yani zaten iş başına bir sayı, `testing.Benchmark`
tarafından `b.N`'e bölünmüş hâliyle. Bu yordam toplam ayırma sayısını hiç görmüyor,
çünkü `testing.Benchmark`'ın kendisi bu sayıyı dışarı vermiyor; yalnız iş başına ayırma
ve iş başına geçen süre döndürüyor. Bu, ölçüm aracının kendi tasarımının bir sonucu —
karşılaştırmalı ölçüm araçları genellikle iş başına sayıyı tercih ediyor, çünkü farklı
`b.N` değerleriyle koşan iki ölçümü karşılaştırılabilir kılan tek şey bu normalize
edilmiş biçim.

## Özet

- On bir bilgi bir koşumda toplandı ve üç sınıfa ayrıldı: **2 kaynak, 5 araç, 4 koşum** —
  bu dağılım konunun bağlayıcı sayısı, bundan sonraki derslerde yeniden üretilmiyor.
- Beş araç bilgisi koşum gerektirmiyor, aynı kaynağa her çağrıda aynı yanıtı veriyor; dört
  koşum bilgisi yalnız çalıştırılarak öğreniliyor.
- Karşılaştırmalı ölçüm aracı iki sayı döndürüyor, ama bu ders yalnız birini —
  iş başına ayırma sayısını — okudu; süre hiçbir yerde basılmadı, hiçbir yerde
  karşılaştırılmadı.
- Ayırma sayısı beş koşumda hep aynı değere düştü (1 ayrı değer); bu, sayının
  tekrarlanabilir olduğunun kanıtı.
- Sınırlayıcı ölçüm: iş başına ayırma ile toplam ayırma ayrı sayılar; hangisinin
  kastedildiği söylenmeden bir karşılaştırma okunamaz.

## Sonraki Adım

Bu derste bir programa dair on bir bilginin üç sınıfa nasıl ayrıldığı ve karşılaştırmalı
ölçümün iki sayısından yalnız birinin tekrarlanabilir olduğu ölçüldü. Beş araç bilgisi
ve dört koşum bilgisi bu dersin verdiği örnekte kaldı; her ikisi de bundan sonraki
derslerde kendi köşesinde yeniden karşımıza çıkıyor, ama on bir satırlık tam tablo bir
daha basılmıyor. Bu on bir bilginin biri — kapsanan satır oranı — bu derste yalnız tek
bir sayı olarak geçti: `80.0% of statements`. Sıradaki ders bu sayının tam olarak neyi
saydığını, hangi satırların kapsama dışında kaldığını ve üç ayrı test kümesinin bu oranı
nasıl değiştirdiğini ölçüyor — konunun kapanış dersi.
