Ders 07 / 19
Başlangıç Süresi
Soğuk, ılık ve sıcak başlatma ilk kareye kadar farklı miktarda iş yapar. Ders sekiz açılışın ilk kare bütçesini kaç örnekte aştığını ölçer; aynı işin iki bütçede iki aşım verdiğini ve ertelenen işin yok olmadığını gösterir.
Yayın Güncelleme
Bu derste
- Soğuk, ılık ve sıcak başlatmayı ilk kareye kadar yapılan işe göre ayırır
- Sekiz açılışın ilk kare bütçesini kaç örnekte aştığını azında, çoğunda gibi dört aşımdan biriyle sınıflandırır
- Aynı işlevin iki farklı bütçede iki farklı aşım verebildiğini gösterir
- Ağır bir başlatmanın ilk kareden sonraya ertelenmesinin işi yok etmediğini, başka bir bütçeye taşıdığını ölçer
İçindekiler
Önceki derste statik çözümleme koda çalıştırmadan baktı; temel değer dosyası eski ihlalleri sayımdan düşürdü ve yalnız yeni ihlal taşıyan modüller aştı. Bu derste koda çalışırken bakıyoruz. Uygulama kullanıcının aygıtında açılır ve ilk kareyi çizene kadar belli bir miktar iş yapar. Soru, bu işin bir bütçeye sığıp sığmadığıdır.
Kursun ölçüsü değişmez: bir ölçümün bütçesi kaç örnekte aşılıyor. Dört yanıt vardır. Hiçbir örnek bütçeyi aşmıyorsa içinde, örneklerin yarısından azı aşıyorsa azında, yarısı ya da fazlası aşıyor ama hepsi aşmıyorsa çoğunda, hepsi aşıyorsa hepsinde. Sınıra eşit örnek aşım sayılmaz. Çekirdekte sekiz örnek her aşımdan birer kez verdi; bu derste aynı sınıflandırma, sekiz açılışa uygulanıyor.
// Overrun.kt — bir olcumun butcesini kac ornekte astigini siniflandirir
class Budget(val name: String, val limit: Int)
class Overrun(val label: String, val budget: Budget, samples: List<Int>) {
val total: Int = samples.size
val over: Int = samples.count { it > budget.limit }
val verdict: String = when {
over == 0 -> "icinde"
over == total -> "hepsinde"
over * 2 < total -> "azinda"
else -> "cogunda"
}
fun line() {
println("$label: butce=${budget.name}<=${budget.limit} | asan=$over/$total -> $verdict")
}
}
fun countOverruns(overruns: List<Overrun>) {
for (name in listOf("icinde", "azinda", "cogunda", "hepsinde")) {
println("$name: ${overruns.count { it.verdict == name }}")
}
}
Budget bir ölçümün adını ve sınırını taşır. Overrun örnekleri sınırla karşılaştırır ve aşan örnekleri sayar. countOverruns dört aşımı sabit sırayla sayar. Bu dosya kursun her dersinde aynıdır ve bir daha değişmez.
Üç Başlatma
Uygulama üç durumdan birinde açılabilir. Soğuk başlatma (cold start) sürecin henüz var olmadığı durumdur: platform süreci kurar, uygulamanın başlangıç kodu çalışır, ilk ekran oluşturulur ve ilk kare çizilir. Ilık başlatma (warm start) soğuk başlatmadaki işlerin bir alt kümesidir: çoğu durumda süreç hâlâ bellektedir ve yalnız ekranın yeniden oluşturulması gerekir; platform süreci bellekten attıysa süreç de yeniden kurulur, ama ekran kaydedilmiş durumundan yararlanır. Sıcak başlatma (hot start) ekranın bellekte durduğu ve yalnızca öne getirildiği durumdur; nesne ve arayüz kurulumu çoğunlukla tekrarlanmaz, yalnız ekran yeniden çizilir.
İlk kare, kullanıcının uygulamadan gördüğü ilk çizimdir. Başlatma süresi, açılma isteğiyle bu kare arasındaki sürenin adıdır. Bu derste süre ölçmüyoruz. Ölçeceğimiz şey ilk kareye kadar yapılan işin adım sayısıdır ve bu sayı modelin içinden gelir.
// Startup.kt — baslatma turune gore ilk kareye kadar yapilan isi adim olarak hesaplar
class Phase(val name: String, val steps: Int)
fun startupPhases(kind: String): List<Phase> = when (kind) {
"soguk" -> listOf(
Phase("surec", 2),
Phase("uygulama", 1),
Phase("bagimlilik", 4),
Phase("ekran", 2),
Phase("cizim", 1),
)
"ilik" -> listOf(Phase("ekran", 2), Phase("cizim", 1))
else -> listOf(Phase("cizim", 1))
}
fun startupWork(kind: String): Int = startupPhases(kind).sumOf { it.steps }
Phase ilk kareye giden yolda bir aşamayı adlandırır. startupPhases başlatma türüne göre aşamaları verir; startupWork aşamaların adımlarını toplar. Üç türün neye benzediğine bakalım.
// Main.kt — uc baslatma turunun ilk kareye kadar yaptigi isi doker
fun main() {
for (kind in listOf("soguk", "ilik", "sicak")) {
val parts = startupPhases(kind).joinToString(" ") { "${it.name}=${it.steps}" }
println("$kind: $parts | toplam=${startupWork(kind)}")
}
}
soguk: surec=2 uygulama=1 bagimlilik=4 ekran=2 cizim=1 | toplam=10 ilik: ekran=2 cizim=1 | toplam=3 sicak: cizim=1 | toplam=1
BA1. Modelde adım, bir aşamanın ağırlığını gösteren sayıdır; saniye ya da kare değildir. Aşamaların adımları ders kararıdır: bağımlılık başlatması soğuk başlatmada en ağır aşama diye kurulmuştur. Gerçek bir uygulamada hangi aşamanın ağır olduğunu bir ölçüm söyler; model yalnız kuralın nasıl çalıştığını gösterir.
Soğuk başlatmanın toplamı ılığınkinin üç katından fazladır ve bunun nedeni görünür: süreç, uygulama ve bağımlılık aşamaları ılık başlatmada hiç yoktur. Bu yüzden başlangıç işinin kaynağını arayan biri önce soğuk başlatmaya bakar. Platformda bu aşamaları izlemek ve ilk etkileşimin bittiğini bildirmek için aşağıdaki parçalar kullanılır.
// Platform kodu: baslatma isini izleme ve ilk etkilesimin bitisini bildirme
import android.app.Application
import android.os.Bundle
import android.os.Trace
import androidx.appcompat.app.AppCompatActivity
class SampleApp : Application() {
override fun onCreate() {
super.onCreate()
Trace.beginSection("uygulama-baslatma")
try {
prepareCore()
} finally {
Trace.endSection()
}
}
}
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.home)
}
// ilk veri yuklemesi bitince cagrilir
private fun showFirstContent(items: List<String>) {
render(items)
reportFullyDrawn()
}
}
Bu parça platform kodudur ve burada çıktı, süre ya da dosya yolu iddia etmiyoruz. Trace çağrıları başlangıç kodunun bir bölümünü geliştirme ortamının profil aracında adlı bir dilim olarak gösterir; reportFullyDrawn ise uygulamanın kullanıma hazır olduğu anı platforma bildirir. Model, bu ölçümün kuralını kurar; model aracın kendisi değildir. Ayrıca soğuk başlatmanın bir kısmı, yani derlenmiş kodun hazır olması, temel profil denen bir dosyayla öne alınabilir. Temel profilin kazancı uygulamaya ve aygıta bağlıdır ve burada bir sayıyla anılmaz.
Sekiz Açılış
Şimdi bir bütçe koyalım. İlk kare bütçesi altı adım olsun. Sekiz açılışın türleri sabit bir diziden gelir: iki soğuk, iki ılık, dört sıcak. Her açılışın işi startupWork ile hesaplanır, yani örnekler bir model işlevinin dönüşüdür.
// Main.kt — sekiz acilis ilk kare butcesine gore olculur
fun main() {
val launches = listOf("soguk", "sicak", "ilik", "sicak", "sicak", "ilik", "soguk", "sicak")
val samples = launches.map { startupWork(it) }
println("is: $samples")
val overrun = Overrun("acilis", Budget("ilk-kare", 6), samples)
overrun.line()
countOverruns(listOf(overrun))
}
is: [10, 1, 3, 1, 1, 3, 10, 1] acilis: butce=ilk-kare<=6 | asan=2/8 -> azinda icinde: 0 azinda: 1 cogunda: 0 hepsinde: 0
Sekiz açılışın ikisi, soğuk olanlar, altı adımı aştı: 2/8, yani azında. Altı ılık ve sıcak açılış bütçenin çok altında kaldı. Bu sonuç ortalama bir açılışın iyi olduğunu söylemez; ortalama burada hesaplanmıyor. Söylediği şey, bütçeyi aşan örneklerin azınlıkta olduğudur. Bir uygulamanın açılışlarının çoğu ılık ya da sıcaksa bu sayı küçük kalır ve yine de kullanıcının ilk karşılaşması, yani soğuk başlatma, bütçeyi hep aşıyor olabilir.
BA2. Dizinin iki soğuk, iki ılık ve dört sıcak olması modelin varsayımıdır. Gerçek kullanımda türlerin oranı kullanıcıya, aygıtın bellek baskısına ve uygulamanın ne sıklıkla kapatıldığına göre değişir ve bu oran burada ölçülmüş bir veri olarak sunulmuyor. Kural türlerin oranını bilmez; yalnız elindeki örnekleri sayar.
Kuralın görmediği iki şey var. Birincisi, aşımın büyüklüğü: soğuk başlatma bütçeyi bir adım da aşsa dört adım da aşsa, asan aynı kalır. İkincisi, aşımın nedeni: soğuk başlatmanın toplamı on, ama kural bunun bağımlılık aşamasından mı, süreç kurulumundan mı geldiğini bilmez. Neden startupPhases çıktısındadır, kuraldaki satırda değil. Kural bedelin nerede ödendiğini değil, bütçenin kaç örnekte aşıldığını sayar.
Aynı İş, İki Bütçe
Aynı örnekleri üç farklı bütçeye vuralım. Bütçe işlevin bir özelliği değil, ona yöneltilen bir sorudur; sorunun sıkılığı değişince aynı işlev başka bir aşım verir.
// Main.kt — ayni sekiz acilis uc butceyle olculur
fun main() {
val launches = listOf("soguk", "sicak", "ilik", "sicak", "sicak", "ilik", "soguk", "sicak")
val samples = launches.map { startupWork(it) }
val overruns = listOf(
Overrun("genis", Budget("ilk-kare", 10), samples),
Overrun("olculu", Budget("ilk-kare", 6), samples),
Overrun("siki", Budget("ilk-kare", 2), samples),
)
overruns.forEach { it.line() }
countOverruns(overruns)
}
genis: butce=ilk-kare<=10 | asan=0/8 -> icinde olculu: butce=ilk-kare<=6 | asan=2/8 -> azinda siki: butce=ilk-kare<=2 | asan=4/8 -> cogunda icinde: 1 azinda: 1 cogunda: 1 hepsinde: 0
Onluk bütçe hiçbir örneği aşmadı, çünkü en ağır açılış tam on adımdı ve sınıra eşit örnek aşım sayılmaz. Altılık bütçe iki, ikilik bütçe dört örneği aştı. İkilik bütçede ılık açılışlar da aşan oldu: üç adımlık ılık başlatma iki adımlık sınırı geçti. Dört aşan örnek sekizin tam yarısıdır ve yarı çoğunda sayılır; bir örnek eksik olsaydı sonuç azında olurdu.
BA3. Üç bütçenin hangisinin doğru olduğunu ders söylemez. Bütçeyi koyan bir ürün kararıdır ve kural yalnız o karara göre sayar. Burada önemli olan şudur: açılış kodu hiç değişmedi, yalnız soru değişti ve aşım içinden çoğuna geçti. Bu yüzden bir aşım tek başına iyi ya da kötü bir uygulamayı göstermez; hangi bütçeyle, hangi örneklerle sayıldığı birlikte okunur.
Bütçeyi Nasıl Seçmeli
Bu derste üç bütçe denedik ve hiçbirini doğru ilan etmedik. Bunun nedeni bütçenin bir ölçüm sonucu değil, bir ürün kararı olmasıdır. Bütçeyi koyan kişi, kullanıcının uygulamayı açtığında en çok ne kadar beklemeye razı olduğunu varsayar; kural bu varsayımı örneklere uygular. Bütçe çok genişse her açılış içinde görünür ve gerileme fark edilmez. Bütçe çok darsa her açılış aşar ve rapor bir şey söylemez hâle gelir, çünkü her şey kırmızıdır.
İyi bir bütçe iki yerden beslenir. Birincisi, uygulamanın kendi geçmişidir: yayımlanmış sürümün ölçümü, sonraki sürümler için bir tavandır. İkincisi, başlatma türüdür. Soğuk, ılık ve sıcak başlatma aynı işi yapmadığı için tek bir bütçeyle hepsini yargılamak yanıltır. Türe göre ayrı bütçe koymak, ya da ölçümü tek bir türe sabitlemek, aşımın neyden geldiğini daha okunur kılar.
Bütçe ayrıca ölçülen anla ilgilidir. Bu derste ilk kare bütçesi ile ilk etkileşim bütçesi ayrı sorulardır: biri kullanıcının ekranı gördüğü ana, öteki ekranın dokunuşa yanıt verebildiği ana bakar. İkisini tek bir sayıda birleştirmek, ertelenen işin nerede ödendiğini gizler.
Başlatma ölçümünde bir tuzak da ölçümün başladığı noktadır. Kullanıcı simgeye dokunduğu anda saati başlatır; uygulamanın kendi kodu ise süreç kurulduktan sonra çalışır. Uygulama içinden alınan bir ölçüm bu yüzden platformun süreç kurma payını görmez. Bu derste modelin surec aşaması o payı ayrı bir satır olarak tutar; gerçek bir ölçümde hangi noktadan başlandığı yazılmadan sayı karşılaştırılmaz.
Başlatmayı iyileştiren biri çoğu zaman soğuk başlatmaya odaklanır, çünkü en çok iş orada yapılır. Ama ılık başlatma, kullanıcının uygulamayı gün içinde en sık yaşadığı durum olabilir. İki türü ayrı bütçelerle izlemek, birinin iyileşirken ötekinin geriye gittiğini fark etmeyi sağlar.
Aynı nedenle bir başlatma bütçesi, hangi türün hedeflendiğini adında taşır. “İlk kare bütçesi” belirsiz bir addır; “soğuk başlatma, ilk kare, altı adım” ise kimin neyi neye karşı saydığını söyler. Adı belirli olmayan bir bütçe, yıllar sonra kimsenin yorumlayamadığı bir sayıya dönüşür.
Yaygın Yanılgılar
“Sıcak başlatma ölçülürse başlangıç ölçülmüş olur.” Sıcak başlatmada süreç ve uygulama nesnesi zaten bellektedir; yapılan iş esas olarak ekranı öne getirmektir. Bu türü ölçmek başlatma işinin büyük kısmını dışarıda bırakır. Modelde de sıcak açılış tek adımdır ve hiçbir bütçeyi aşmadı; bu, uygulamanın başlatma kodunun iyi olduğunu göstermez.
“İşi ilk kareden sonraya ertelemek işi kaldırır.” Ertelenen iş hâlâ yapılır; yalnız kullanıcının ilk kareyi gördüğü ana kadar beklemesi gerekmez. İlk etkileşim bütçesi bu yüzden ayrı bir ölçümdür. Erteleme, işi bir bütçeden öbürüne taşır; iki bütçeyi birlikte okumadan kazanç ilan edilemez.
“Tek bir ölçüm yeterlidir.” Aynı açılış farklı koşullarda farklı iş yapabilir. Bu yüzden ders sekiz açılışlık bir dizi kurdu ve kuralı örnek sayısı üzerinden işletti. Tek bir örnekte aşım ancak içinde ya da hepsinde olabilir; ara kategoriler ancak birden çok örnekle ortaya çıkar.
“Aşım sayısı uygulamanın kalitesini söyler.” Sayı, yalnız seçilen bütçe ve seçilen örneklerle birlikte anlam taşır. Aynı uygulama üç bütçede üç farklı aşım verdi; uygulama hiç değişmedi.
Sınırlayıcı Ölçüm
Soğuk başlatmanın en ağır aşaması bağımlılık başlatmasıydı. Yaygın bir çözüm, bu işi ilk kareden sonraya ertelemektir: ilk kare çizilir, kullanıcı ekranı görür, bağımlılık arkada hazırlanır. Modelde bunu iki işlevle kurarız. Biri ilk kareye kadar kalan işi, öteki ertelenen işi verir.
// Main.kt — agir bagimlilik ilk kareden sonraya ertelenir; is ilk etkilesime tasinir
fun beforeFirstFrame(kind: String): Int =
startupPhases(kind).filter { it.name != "bagimlilik" }.sumOf { it.steps }
fun afterFirstFrame(kind: String): Int =
startupPhases(kind).filter { it.name == "bagimlilik" }.sumOf { it.steps }
fun main() {
val launches = listOf("soguk", "sicak", "ilik", "sicak", "sicak", "ilik", "soguk", "sicak")
val first = launches.map { beforeFirstFrame(it) }
val later = launches.map { afterFirstFrame(it) }
println("ilk kare: $first")
println("ilk etkilesim: $later")
val overruns = listOf(
Overrun("kare-isi", Budget("ilk-kare", 6), first),
Overrun("etkilesim-isi", Budget("ilk-etkilesim", 3), later),
)
overruns.forEach { it.line() }
countOverruns(overruns)
}
ilk kare: [6, 1, 3, 1, 1, 3, 6, 1] ilk etkilesim: [4, 0, 0, 0, 0, 0, 4, 0] kare-isi: butce=ilk-kare<=6 | asan=0/8 -> icinde etkilesim-isi: butce=ilk-etkilesim<=3 | asan=2/8 -> azinda icinde: 1 azinda: 1 cogunda: 0 hepsinde: 0
İlk kare bütçesi artık içinde: soğuk başlatma altı adıma indi ve altı, sınıra eşit olduğu için aşım sayılmadı. Ama iş yok olmadı. Dört adımlık bağımlılık başlatması ilk etkileşime kaldı ve o ayrı bütçe, üç adımlık sınırıyla, yine iki örnekte aşıldı: azında. Erteleme bedeli ilk kare satırından alıp etkileşim satırına yazdı.
BA4. İlk etkileşim bütçesinin üç adım olması ders kararıdır. Kullanıcının ilk dokunuşunun ne kadar işi kaldırabileceği, uygulamanın ne yaptığına göre değişir. Ertelemenin her zaman kötü olduğu da söylenmiyor: kullanıcı ilk etkileşimden önce çok daha uzun süre ekrana bakabilir ve ertelenen iş bu sürede biter. Model bu paralelliği kurmaz. Kurduğu tek şey, ertelenen işin başka bir bütçede yeniden sayıldığıdır.
Kural burada da aşımın sayısını görür, büyüklüğünü ve nedenini görmez. İlk kare satırındaki iyileşme gerçektir, ama bütün resmi yalnız o satır vermez. Başlangıç işini azaltmak için işi başka bir yere taşımak ile işi gerçekten kaldırmak ayrı şeylerdir; kural bunları ancak iki bütçe yan yana konursa ayırır.
Özet
- Bu dersin aşımı: azında. Sekiz açılışın ikisi, soğuk olanlar, altı adımlık ilk kare bütçesini aştı.
- Soğuk başlatma süreç, uygulama, bağımlılık, ekran ve çizim aşamalarını taşır; ılık başlatma ekranı ve çizimi, sıcak başlatma yalnız çizimi yapar.
- Aynı sekiz örnek on adımlık bütçede içinde, altı adımlıkta azında, iki adımlıkta çoğunda çıktı. Aşım işlevin değil, işlevle bütçenin ortak özelliğidir.
- Sınıra eşit örnek aşım sayılmaz: altı adımlık ilk kare işi altı adımlık bütçeyi aşmaz.
- Bağımlılık başlatmasını ilk kareden sonraya ertelemek ilk kareyi içinde yaptı; iş yok olmadı, ayrı bir ilk etkileşim bütçesinde azında olarak yeniden göründü.
Sonraki Adım
İlk kare çizildi, ama uygulamanın işi bitmedi. Kullanıcı bir listeyi kaydırdığında her kare yeniden çizilir ve her kare kendi bütçesine sahiptir. Sıradaki ders, Çizim Akıcılığı, kaydırılan bir listede her karenin bütün öğeleri yeniden oluşturmasının kare bütçesini kaç karede aştığını ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.