İçeriğe geç
academia.sh

Ders 11 / 19

Başlangıç ve Kaydırma Başarımı

Açılışta yapılan işi ertelemek ve satır nesnelerini yeniden kullanmak; kayıtlı bir temel değerle karşılaştıran başarım denetiminin temel değerin hangi sürümden alındığına göre ayırıp ters dönebildiğinin ölçümü.

Yayın Güncelleme

Bu derste

  • Açılışta ilk kareden önce yapılan işin ertelenebilir kısmını ayırt eder
  • Satır nesnelerinin yeniden kullanımının kaydırmada oluşturulan nesne sayısını nasıl sınırladığını açıklar
  • Temel değerin kusurlu sürümden alınmasının denetimi nasıl tersine çevirdiğini ölçer
  • Her koşuda yenilenen temel değerin denetimi neden kör bıraktığını gösterir
İçindekiler

Önceki derste (Bellek Sızıntısı Tespiti) kusur, ekran kapandıktan sonra görünen bir nesnenin yaşamaya devam etmesiydi. Bu derste kusur kullanıcının ilk gördüğü iki yerde: uygulama açılırken ve liste kaydırılırken. İkisinde de çıktı doğrudur; ekran açılır, satırlar çizilir. Fark, o sonuca varmak için yapılan işin miktarında.

Başarım denetimleri çoğunlukla bir temel değerle (baseline) karşılaştırır: önceki bir koşuda ölçülen değer kaydedilir ve yeni koşunun değeri onu aşmamalıdır. Bu dersin sorusu, temel değerin hangi sürümden alındığının denetimi ne yaptığıdır.

Ölçü ekseni 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. Çekirdek önceki derslerle aynıdır; 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 hakkındaki kararını tutar; separation etiketi bu iki karardan türer. contrast aynı kapanışı 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ında yatar.

Açılışta Yapılan İş

Kullanıcı simgeye dokunduğunda ilk kare çizilene kadar geçen süre, uygulamanın ilk izlenimidir. Bu sürede çalışan her iş kullanıcıyı bekletir. Açılışta yapılan işin hepsi ilk kare için gerekli değildir: bir analiz kütüphanesini başlatmak, önbelleği ısıtmak ya da ikinci bir ekranın verisini hazırlamak ilk kareden sonraya bırakılabilir.

// uygulama girisi — KAYNAK
@main
struct ShopApp: App {
    init() {
        configureTheme()
        restoreSession()
        // Ilk kare icin gerekmiyor; sonraya kalir.
        // warmImageCache()
        // startAnalytics()
    }

    var body: some Scene {
        WindowGroup { RootView() }
            .task { await deferredStartup() }
    }
}

Bu parça çalıştırılmaz; configureTheme, restoreSession ve deferredStartup burada tanımlı değildir. Görünümün task değiştiricisi, görünüm belirmeden önce eşzamansız bir görev başlatır; görev ilk await noktasında askıya alınır ve ertelenen işin geri kalanı ilk kareyi bekletmez. Kural basit: ilk kareyi çizmek için gereken iş girişte, gerisi sonrada.

Gerçek uygulamalarda açılış süresi ve kaydırma akıcılığı kullanıcıların telefonlarından da toplanır. Platformun MetricKit çerçevesi bu verileri günlük özetler olarak uygulamaya teslim eder.

// toplanan olcumler — KAYNAK
import MetricKit

final class MetricsReceiver: NSObject, MXMetricManagerSubscriber {
    func didReceive(_ payloads: [MXMetricPayload]) {
        for payload in payloads {
            if let launch = payload.applicationLaunchMetrics {
                record(launch.histogrammedTimeToFirstDraw)
            }
        }
    }
}

Özetler gerçek kullanıcıların gerçek koşullarındaki dağılımı verir; geliştirme sırasında alınan tek bir ölçüm bunu veremez. Bu dersin ölçümleri ise koşullara bağlı olmayan, sayılan işe dayanır: kaç adım çalıştı, kaç nesne oluşturuldu. Süre ölçülmez ve yazılmaz.

Kaydırmada Oluşturulan Nesneler

Uzun bir listede ekranda aynı anda yalnız birkaç satır görünür. Satır ekrandan çıktığında nesnesi atılmaz; bir havuza konur ve yeni görünen satır için yeniden kullanılır. Bu, satır başına nesne oluşturma ve bellek ayırma işini ortadan kaldırır. Yeniden kullanım yoksa her satır için yeni bir nesne oluşturulur ve kaydırma uzadıkça iş büyür.

// satir olusturma — KAYNAK
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Row", for: indexPath)
    cell.textLabel?.text = items[indexPath.row].title
    return cell
}

dequeueReusableCell(withIdentifier:for:) havuzdan boşta bir satır alır, yoksa yenisini oluşturur. Kusurlu sürüm her seferinde yeni bir satır nesnesi kurar.

Aşağıdaki model, bunun saf sayım karşılığıdır; bir liste görünümü değildir. Yüz satırlık bir listeyi, aynı anda on satırın göründüğü bir pencereden kaydırır ve kaç satır nesnesi oluşturulduğunu sayar. Doğru sürüm ekrandan çıkan satırı havuza koyar ve sonraki satırda onu kullanır. Kusurlu sürüm havuz kullanmaz.

// Scroll.swift — kaydirma ve acilis modeli, temel degerli olcum
struct Measured: CustomStringConvertible {
    let value: Int
    let baseline: Int
    var description: String { "\(value) temel=\(baseline)" }
}

func cellsCreated(rows: Int, visible: Int, reuse: Bool) -> Int {
    var pool = 0
    var created = 0
    var onScreen = 0
    for _ in 0..<rows {
        if onScreen == visible {
            onScreen -= 1
            if reuse { pool += 1 }
        }
        if reuse && pool > 0 { pool -= 1 } else { created += 1 }
        onScreen += 1
    }
    return created
}

func drawnRows(rows: Int, reuse: Bool) -> Int {
    rows
}

func launchWork(deferHeavy: Bool) -> Int {
    let light = 3
    let heavy = 5
    return deferHeavy ? light : light + heavy
}

cellsCreated kaydırmayı döngüyle yürütür: pencere dolunca en eski satır ekrandan çıkar, yeniden kullanım açıksa havuza girer. drawnRows iki sürümde de çizilen satır sayısıdır. launchWork açılışta ilk kareden önce çalışan adımları sayar: üç hafif adım her zaman, beş ağır adım yalnız ertelenmediğinde. Measured bir değeri kaydedilmiş temel değeriyle birlikte tutar.

Temel Değerin Kaynağı

Üç denetimi aynı kaydırmaya uygularız. Birincisi çizilen satır sayısına bakar. İkincisi “değer temel değere eşit olmalı” der ve temel değeri kusurlu sürümden alır. Üçüncüsü “değer temel değeri aşmamalı” der ve temel değeri doğru sürümden alır.

// main.swift — uc denetim, temel degerin kaynagi
let drawn = contrast("cizilen-satir", correct: drawnRows(rows: 100, reuse: true), faulty: drawnRows(rows: 100, reuse: false), check: { $0 == 100 }).describe()

let faultyBaseline = cellsCreated(rows: 100, visible: 10, reuse: false)
let equalToFaulty = contrast("esit-temel", correct: Measured(value: cellsCreated(rows: 100, visible: 10, reuse: true), baseline: faultyBaseline), faulty: Measured(value: cellsCreated(rows: 100, visible: 10, reuse: false), baseline: faultyBaseline), check: { $0.value == $0.baseline }).describe()

let correctBaseline = cellsCreated(rows: 100, visible: 10, reuse: true)
let notAboveCorrect = contrast("asmaz-temel", correct: Measured(value: cellsCreated(rows: 100, visible: 10, reuse: true), baseline: correctBaseline), faulty: Measured(value: cellsCreated(rows: 100, visible: 10, reuse: false), baseline: correctBaseline), check: { $0.value <= $0.baseline }).describe()

countSeparations([drawn, equalToFaulty, notAboveCorrect])
cizilen-satir | dogru=100/gecti kusurlu=100/gecti -> kor
esit-temel | dogru=10 temel=100/kaldi kusurlu=100 temel=100/gecti -> ters
asmaz-temel | dogru=10 temel=10/gecti kusurlu=100 temel=10/kaldi -> ayiran
ayiran: 1
kor: 1
gurultulu: 0
ters: 1

cizilen-satir kör: iki sürüm de yüz satırı çizer, kullanıcının gördüğü liste aynıdır. esit-temel ters: temel değer kusurlu sürümden, yani yüz nesneden alınmıştır; doğru sürüm on nesneyle bu değere eşit olmadığı için denetim kalır, kusurlu sürüm ise kendi değerine eşit olduğu için geçer. İyileştirme kırmızı, kusur yeşil görünür. asmaz-temel ayıran: temel değer doğru sürümden alınmıştır, doğru sürüm geçer ve kusurlu sürüm aşar.

Ters denetim, ölçülen şey ile temel değer arasındaki ilişkiyi tersine çevirdiği için kör olmaktan daha kötüdür. Kör denetim bir şey söylemez; ters denetim yanlış şey söyler ve ekibi doğru sürümü geri almaya iter. Bu yüzden temel değer kayda alınırken hangi sürümden alındığı da kaydedilir ve “eşit” yerine “aşmaz” denir: bir başarım iyileşmesi hata sayılmamalıdır.

TS1. Temel değerin kusurlu sürümden alınması bir tasarım hatasıdır, ama gerçekte sık olur: ekip kusuru bilmeden bir değer kaydeder ve sonra düzeltir.

TS2. Model yalnız oluşturulan nesne sayısını ölçer. Gerçek bir listede satırın içeriği, boyutu ve görüntüsü de kaydırmayı etkiler; bunlar sayılmaz.

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

Temel değerin bir sorunu daha var: yenilenmesi. Bazı ekipler denetimin kırmızıya dönmemesi için temel değeri her koşuda mevcut değere çeker. “Değer temel değeri aşmamalı” denetimi bu durumda her zaman geçer.

Aşağıdaki ölçüm üç şeyi yan yana koyar: sabit temel değerle “aşmaz” denetimini, her koşuda yenilenen temel değerle aynı denetimi ve açılışta ilk kareden önce çalışan işe uygulanan sabit temel değerli denetimi.

// main.swift — sabit ve yenilenen temel deger, acilis
let correctCells = cellsCreated(rows: 100, visible: 10, reuse: true)
let faultyCells = cellsCreated(rows: 100, visible: 10, reuse: false)

let fixed = contrast("sabit-temel", correct: Measured(value: correctCells, baseline: correctCells), faulty: Measured(value: faultyCells, baseline: correctCells), check: { $0.value <= $0.baseline }).describe()
let refreshed = contrast("yenilenen-temel", correct: Measured(value: correctCells, baseline: correctCells), faulty: Measured(value: faultyCells, baseline: faultyCells), check: { $0.value <= $0.baseline }).describe()

let correctLaunch = launchWork(deferHeavy: true)
let launch = contrast("ilk-kare-oncesi-is", correct: Measured(value: correctLaunch, baseline: correctLaunch), faulty: Measured(value: launchWork(deferHeavy: false), baseline: correctLaunch), check: { $0.value <= $0.baseline }).describe()

countSeparations([fixed, refreshed, launch])
sabit-temel | dogru=10 temel=10/gecti kusurlu=100 temel=10/kaldi -> ayiran
yenilenen-temel | dogru=10 temel=10/gecti kusurlu=100 temel=100/gecti -> kor
ilk-kare-oncesi-is | dogru=3 temel=3/gecti kusurlu=8 temel=3/kaldi -> ayiran
ayiran: 2
kor: 1
gurultulu: 0
ters: 0

sabit-temel önceki ölçümün ayıran denetimidir. yenilenen-temel kör: kusurlu sürümün temel değeri de kendi değerine çekildiği için yüz nesne yüz nesneyi aşmaz. Denetim hiçbir zaman kırılmadığı için bir şeyi koruduğunu sanmak yanıltıcıdır. ilk-kare-oncesi-is ayıran: ertelenmemiş açılış sekiz adım çalıştırır ve sabit üç adımlık temel değeri aşar.

Temel değeri yenilemek kendi başına yanlış değildir; bilinçli bir iyileşmeden sonra yeni bir temel kaydetmek gerekir. Yanlış olan, bunu otomatik ve her koşuda yapmaktır. Temel değer bir kararla değişir, bir koşuyla değil.

TS3. Model “adım” ve “nesne” sayar. Gerçek bir açılışta adımların süresi eşit değildir; tek bir ağır adım sekizin hepsinden uzun sürebilir.

TS4. Açılış ölçümü yalnız ilk kareden önceki işi sayar. İlk kareden sonra ertelenen işin de bir bedeli vardır; ertelemek onu ortadan kaldırmaz.

Sık Yapılan Hatalar

  • Temel değeri kusurlu ya da bilinmeyen bir sürümden kaydetmek ve “eşit” demek.
  • Temel değeri her koşuda mevcut değere çekmek. Denetim hiçbir zaman kırılmaz.
  • Açılışta ilk kare için gerekmeyen işi girişte yapmak.
  • Her satır için yeni nesne oluşturmak; yeniden kullanım havuzunu atlamak.
  • Tek bir yerel ölçümü kullanıcıların koşullarının temsili saymak.

Özet

  • İlk kareden önceki iş kullanıcıyı bekletir; yalnız ilk kare için gerekenler girişte yapılır, gerisi ertelenir.
  • Satır nesnelerinin yeniden kullanımı, kaydırmada oluşturulan nesne sayısını görünen satır sayısında tutar.
  • Temel değer kusurlu sürümden alınırsa “eşit” denetimi ters olur: iyileştirme kırmızı, kusur yeşil görünür.
  • Temel değer doğru sürümden alınırsa “aşmaz” denetimi ayırır; her koşuda yenilenen temel değer ise denetimi kör bırakır.
  • Temel değer bir kararla değişir, bir koşuyla değil.
  • Bu dersin ayrımı: ters.

Sonraki Adım

Başarım denetimi bir kural olarak yazılabilir; kod kalitesi denetimleri de öyle. Sıradaki derste Kod Kalitesi Araçları, biçim denetiminin ve kaynak metni tarayan bir kuralın aynı kusuru ayırıp ayıramadığını ö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