İçeriğe geç
academia.sh

Ders 09 / 19

Profil Çıkarma Araçları

Profil aracının zaman çizelgeleri ve işaretlerle uygulamanın nerede zaman harcadığını göstermesi; iş birimi bütçesinin ve çizilen metin denetiminin doğru ve kusurlu sürümü ayırıp ayırmadığının ölçümü.

Yayın Güncelleme

Bu derste

  • Profil çıkarmanın test yazmaktan farkını ve hangi soruyu cevapladığını anlatır
  • İşlemci, bellek ve çizim çizelgelerinin hangi sorulara karşılık geldiğini ayırır
  • İş birimi bütçesine bakan denetimin kusurlu hücreyi ayırdığını, çizilen metne bakan denetimin ayırmadığını ölçer
  • Bütçenin gevşek ya da sıkı seçilmesinin aynı denetimin kategorisini değiştirdiğini gösterir
İçindekiler

Önceki derste (Test Planları ve Kapsam) bir testin hangi satıra girdiğinin, o satırın doğru olduğunu söylemediğini gördük. Bu derste doğru olup yavaş olan koda bakıyoruz. Bir liste hücresi her çizildiğinde aynı biçimlendiriciyi baştan kuruyorsa, ekranda görünen yazı doğrudur ve test yeşil kalır; ama kaydırma sırasında uygulama gereğinden çok iş yapar.

Ölçü ekseni yine ayrımdır: aynı denetimi doğru sürüme ve kusurlu sürüme uygularız. Ayıran denetim doğru sürümde geçer, kusurlu sürümde kalır. Kör denetim ikisinde de geçer. Gürültülü denetim ikisinde de kalır. Ters denetim doğru sürümde kalır, kusurlu sürümde geçer. Çekirdeği, her ders kendi başına çalıştığı için, yeniden veriyoruz.

// Separation.swift — bir denetimin dogru surumu kusurlu surumden ayirip ayirmadigini siniflandirir
struct Contrast<Output: CustomStringConvertible> {
    let label: String
    let correct: Output
    let faulty: Output
    let passesCorrect: Bool
    let passesFaulty: Bool

    var separation: String {
        switch (passesCorrect, passesFaulty) {
        case (true, false): return "ayiran"
        case (true, true): return "kor"
        case (false, false): return "gurultulu"
        case (false, true): return "ters"
        }
    }

    func describe() -> String {
        let onCorrect = passesCorrect ? "gecti" : "kaldi"
        let onFaulty = passesFaulty ? "gecti" : "kaldi"
        print("\(label) | dogru=\(correct)/\(onCorrect) kusurlu=\(faulty)/\(onFaulty) -> \(separation)")
        return separation
    }
}

func contrast<Output>(_ label: String, correct: Output, faulty: Output, check: (Output) -> Bool) -> Contrast<Output> {
    Contrast(label: label, correct: correct, faulty: faulty, passesCorrect: check(correct), passesFaulty: check(faulty))
}

func countSeparations(_ results: [String]) {
    for name in ["ayiran", "kor", "gurultulu", "ters"] {
        print("\(name): \(results.filter { $0 == name }.count)")
    }
}

Contrast iki sürümün çıktısını ve denetimin her biri üzerindeki kararını tutar; separation etiketi bu iki karardan türer. contrast aynı kapanışı (closure) iki çıktıya uygular, countSeparations dört kategoriyi sayar. Kural nedeni: bir denetimin değeri geçmesinde değil, iki sürümü ayırmasındadır.

Profil Çıkarma Nedir

Profil çıkarma (profiling) çalışan bir uygulamanın zamanını, belleğini ve çizim işini ölçmektir. Test bir soruya evet ya da hayır der; profil aracı “nerede” sorusuna cevap verir. Tek bir isteği, tek bir kaydırmayı ya da tek bir ekranı kaydedersiniz ve araç kaydın zaman çizelgesini gösterir.

Profil çıkarmanın iki kuralı vardır. Birincisi, ölçüm yayın yapılandırmasıyla alınır; hata ayıklama yapılandırması eniyileştirmeleri kapatır ve sayıları çarpıtır. İkincisi, ölçüm mümkünse gerçek aygıtta alınır; simülatör masaüstü işlemcisinde koşar ve gerçek aygıtın bellek ve çizim sınırlarını taşımaz.

Profil aracı birden çok çizelge sunar. İşlemci çizelgesi hangi işlevlerin zaman harcadığını çağrı ağacı olarak gösterir; en ağır çağrı çoğu zaman aranan yerdir. Bellek çizelgesi ayrılan nesneleri ve bunların ömrünü izler. Çizim çizelgesi her karenin ne kadar sürede hazırlandığını gösterir. Her çizelge farklı bir soruya cevap verir: işlemci “ne çalışıyor”, bellek “ne birikiyor”, çizim “ekran yetişiyor mu”.

İşaretlerle Ölçmek

Araç tüm uygulamayı gösterir, ama sizin ilgilendiğiniz bölüm küçük olabilir. İşaret (signpost) koda koyduğunuz bir başlangıç ve bitiş çiftidir; araç bu çifti zaman çizelgesinde adlı bir aralık olarak gösterir.

// hucre cizim isareti — KAYNAK
import os

let signposter = OSSignposter(subsystem: "com.example.shop", category: "list")

func configure(cell: PriceCell, price: Decimal) {
    let state = signposter.beginInterval("configure")
    cell.show(price)
    signposter.endInterval("configure", state)
}

Bu parça çalıştırılmaz; profil aracının içinde anlam taşır. Aralığın adı ve kategorisi çizelgede süzgeç olarak kullanılır. İşaret koymanın bedeli düşüktür ve işaretler yayın yapılandırmasında bırakılabilir. Eski, işlev tabanlı biçimi os_signpost ile yazılır; yeni biçim OSSignposter nesnesini kullanır. İkisi de aynı şeyi yapar.

Şimdi kusurlu hücreyi görelim. Her çağrıda yeni bir biçimlendirici kurmak bir fiyat hücresinde en sık görülen yavaşlıklardan biridir, çünkü biçimlendirici pahalı bir nesnedir.

// hucre yapilandirmasi — KAYNAK
import UIKit

final class PriceCell: UITableViewCell {
    func show(_ price: Decimal) {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        textLabel?.text = formatter.string(from: price as NSDecimalNumber)
    }
}

show(_:) her çağrıldığında bir NumberFormatter kurar. Liste kaydırılırken yüzlerce hücre çizilir, yani yüzlerce biçimlendirici kurulur. Düzeltme biçimlendiriciyi bir kez kurmak ve her hücrede paylaşmaktır.

// hucre yapilandirmasi, duzeltilmis — KAYNAK
import UIKit

final class PriceCell: UITableViewCell {
    private static let formatter: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        return formatter
    }()

    func show(_ price: Decimal) {
        textLabel?.text = Self.formatter.string(from: price as NSDecimalNumber)
    }
}

İki sürüm ekranda aynı metni gösterir. Fark yalnız kaç kez nesne kurulduğundadır.

İş Birimi Modeli

Aşağıdaki model, profil aracının ne ölçtüğünü kurar; aracın kendisi değildir. Süre ölçmeyeceğiz: milisaniye makineden makineye değişir ve burada basılan bir sayı tekrarlanamaz. Bunun yerine her işi bir iş birimi olarak sayarız. Bir biçimlendirici kurmak bir birim, bir fiyatı biçimlendirmek bir birimdir. Hücre sayısı aynıyken kurulan nesne sayısı iş birimini belirler.

Doğru sürüm biçimlendiriciyi bir kez kurar ve her satırda yeniden kullanır. Kusurlu sürüm her satırda yeniden kurar. Yirmi satır çizilir.

// Cells.swift — fiyat hucresinin dogru ve kusurlu surumu, is birimi sayaci
struct Rendered {
    let firstRow: String
    let units: Int
}

func formatPrice(_ cents: Int) -> String {
    let rest = cents % 100
    return "\(cents / 100),\(rest < 10 ? "0" : "")\(rest)"
}

func renderRows(count: Int, rebuildsFormatter: Bool) -> Rendered {
    var units = 0
    var first = ""
    if !rebuildsFormatter { units += 1 }
    for row in 1...count {
        if rebuildsFormatter { units += 1 }
        units += 1
        if row == 1 { first = formatPrice(1000 + row * 25) }
    }
    return Rendered(firstRow: first, units: units)
}

renderRows yirmi satırı çizer. Doğru sürümde biçimlendirici bir kez kurulur (bir birim), sonra her satır bir birim harcar. Kusurlu sürümde her satır iki birim harcar. firstRow ilk satırın metnidir; iki sürümde de aynı fiyat çıkar.

İki denetim uygulanır. Birincisi çizilen metne bakar: ilk satır 10,25 mi. İkincisi iş birimi bütçesine bakar: toplam birim yirmi beşi aşıyor mu. Yirmi beş bu dersin kuralıdır; gerçek bir bütçe değildir.

// main.swift — cizilen metin ve is birimi butcesi
let correct = renderRows(count: 20, rebuildsFormatter: false)
let faulty = renderRows(count: 20, rebuildsFormatter: true)

let text = contrast("cizilen-metin", correct: correct.firstRow, faulty: faulty.firstRow, check: { $0 == "10,25" }).describe()
let budget = contrast("is-birimi-butcesi", correct: correct.units, faulty: faulty.units, check: { $0 <= 25 }).describe()

countSeparations([text, budget])
cizilen-metin | dogru=10,25/gecti kusurlu=10,25/gecti -> kor
is-birimi-butcesi | dogru=21/gecti kusurlu=40/kaldi -> ayiran
ayiran: 1
kor: 1
gurultulu: 0
ters: 0

cizilen-metin kör: iki sürüm de aynı fiyatı gösterir. is-birimi-butcesi ayıran: doğru sürüm yirmi bir birim harcar ve bütçeye sığar, kusurlu sürüm kırk birim harcar ve sığmaz. Kırk, yirmi birin neredeyse iki katıdır ve fark tam olarak her satırda yeniden kurulan biçimlendiriciden gelir.

Burada önemli bir sonuç var: görünen davranışı denetleyen hiçbir test bu kusuru bulamaz. Birim testi de, arayüz testi de, kapsam eşiği de bu kusurlu hücreyi onaylar. Kusuru yalnız iş miktarına bakan bir denetim ya da profil aracının çizelgesi görür.

TS1. Model biçimlendiriciyi kurmakla bir fiyatı biçimlendirmeyi aynı maliyette sayar. Gerçek maliyet oranı ölçülmedi ve bu dersin konusu değildir; yalnız kaç kez kurulduğu sayılır.

TS2. Model yirmi satır çizer. Satır sayısı arttıkça iki sürüm arasındaki fark büyür; bu dersin ölçüsü yalnız yirmi satırı kapsar.

Çizelgeyi Okumak

Bir profil kaydı açıldığında okumanın bir sırası vardır. Önce zaman çizelgesinde yavaşlığın göründüğü aralığı bulun; işaretler bunu kolaylaştırır. Sonra o aralıkta işlemci çizelgesindeki çağrı ağacına bakın ve en çok zaman harcayan çağrıdan başlayın. Çağrı ağacını ters çevirmek, yani en alttaki çağrıları üstte görmek, harcamayı yapan işlevi hızla gösterir.

Yukarıdaki hücrede çağrı ağacı NumberFormatter kurulumunu ağır çağrılar arasında gösterebilirdi. Ağacın kendisi bir kusur teşhisi değildir; “burada zaman gidiyor” der. Zamanın neden gittiğini anlamak ve gerekip gerekmediğine karar vermek yine okuyanın işidir.

Profil çıkarırken bir başka tuzak ölçümü ölçmeye bağımlı kılmaktır. Araç kaydı uygulamayı yavaşlatır ve çok sık işaret koymak ölçülen şeyi değiştirir. Bu yüzden kayıt kısa tutulur ve tek bir senaryo ölçülür.

Sınırlayıcı Ölçüm

İş birimi bütçesi ayıran bir denetimdi. Ama bütçenin sınırı denetimin kategorisini belirler. Aşağıda aynı iki sürüm üç farklı bütçeyle denetlenir: yirmi beş, elli ve on.

// main.swift — ayni sayac, uc butce
let correct = renderRows(count: 20, rebuildsFormatter: false)
let faulty = renderRows(count: 20, rebuildsFormatter: true)

let tight = contrast("siki-butce", correct: correct.units, faulty: faulty.units, check: { $0 <= 10 }).describe()
let fitting = contrast("uygun-butce", correct: correct.units, faulty: faulty.units, check: { $0 <= 25 }).describe()
let loose = contrast("gevsek-butce", correct: correct.units, faulty: faulty.units, check: { $0 <= 50 }).describe()

countSeparations([tight, fitting, loose])
siki-butce | dogru=21/kaldi kusurlu=40/kaldi -> gurultulu
uygun-butce | dogru=21/gecti kusurlu=40/kaldi -> ayiran
gevsek-butce | dogru=21/gecti kusurlu=40/gecti -> kor
ayiran: 1
kor: 1
gurultulu: 1
ters: 0

On birim çok sıkıdır: doğru sürüm bile aşar ve denetim gürültülü olur. Elli birim çok gevşektir: kusurlu sürüm de sığar ve denetim kör olur. Yalnız yirmi beş doğru sürümün üstünde, kusurlu sürümün altında durur. Aynı sayaç, aynı denetim türü; yalnız eşik değişti ve kategori üç kez değişti.

Bütçe böyle bir denetimin kendisidir. Bütçeyi koyan kişi doğru sürümün harcadığını ölçmelidir; ölçmeden koyulan bir bütçe ya ilk günden kırmızıdır ya da hiçbir zaman kırmızı olmaz. Ve doğru sürüm yavaşlayıp kusurlu sürüme yaklaştıkça bütçenin payı da değişir.

TS3. Modelin eşikleri dersin kuralıdır. Gerçek bir bütçenin sayısı yalnız ölçümle bulunur ve her uygulamada başkadır.

TS4. Üç ölçüm de aynı yirmi satırlık çizimi kullanır. Başka bir satır sayısında en uygun bütçe başka bir sayı olurdu.

Sık Yapılan Hatalar

  • Hata ayıklama yapılandırmasında profil çıkarmak. Eniyileştirmeler kapalı olduğu için sayılar yanıltır.
  • Yalnız simülatörde ölçmek. Gerçek aygıtın sınırları farklıdır.
  • Her şeye işaret koymak. İşaret çizelgeyi doldurur ve ölçülen işi değiştirir.
  • Görünen çıktıyı denetleyen testlerin yeşil olmasını başarım garantisi saymak.
  • Bütçeyi ölçmeden, tahminle koymak.

Özet

  • Profil çıkarma “nerede” sorusuna cevap verir; test evet ya da hayır der.
  • İşlemci, bellek ve çizim çizelgeleri farklı sorulara karşılık gelir; işaretler ilgilendiğiniz aralığı çizelgede adlandırır.
  • İki denetimden biri kör, biri ayıran çıktı: çizilen metin iki sürümde aynıydı, iş birimi bütçesi yalnız doğru sürümde geçti.
  • Aynı bütçe denetimi eşiğe göre gürültülü, ayıran ya da kör oldu; bütçe ölçülerek konmalıdır.
  • Başarım kusurunu görünen çıktıya bakan testler bulamaz.
  • Bu dersin ayrımı: ayıran.

Sonraki Adım

Zaman kaybı bir başarım sorunudur; bellekte kalan nesne ise ayrı bir kusurdur. Sıradaki derste Bellek Sızıntısı Tespiti, bir ekran kapandıktan sonra hâlâ yaşayan nesneyi tutma döngüsü üzerinden ölçecek.

İ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