Ders 10 / 19
Ağ ve Pil Profili
Bir eşitleme döngüsünde gönderilen istek sayısı pili ve ağı belirler. Ders istekleri tek tek, toplu ve yoklamayla göndermeyi aynı değişiklik akışında ölçer; yoklamanın değişiklik olmayan döngülerde de bütçeyi aştığını gösterir.
Yayın Güncelleme
Bu derste
- Ağ isteğinin uyandırma ve toplu gönderim kavramlarıyla pil harcamasına nasıl bağlandığını açıklar
- Bir eşitleme döngüsünde gönderilen istek sayısını tek tek ve toplu gönderim için modelden ölçer
- Yoklamanın hiç değişiklik gelmeyen döngülerde de her döngüde istek yolladığını gösterir
- İstek sayısı kuralının hangi tasarrufu görüp hangisini göremediğini ayırt eder
İçindekiler
Bellek, uygulamanın kendi içinde tuttuğu şeydi. Ağ ve pil ise uygulamanın dışarıya karşı harcadığı şeydir. Bir sızıntı belleği sessizce doldurur; sık gönderilen küçük istekler de pili sessizce boşaltır. Kullanıcı bunu tek bir ekranda görmez, günün sonunda pilin ne kadar çabuk bittiğine bakınca fark eder. Bu dersin sorusu o yüzden şudur: eşitleme çalıştığında bir döngüde kaç istek gidiyor ve bu sayı bütçeyi kaç döngüde aşıyor.
İsteğin pile maliyeti, gönderdiği veriden çok onu göndermek için aygıtın uyanmasından gelir. Aygıt boştayken ağ donanımı düşük güçte bekler; bir istek donanımı uyandırma (wake-up) durumuna sokar ve istek bittikten sonra da bir süre uyanık kalır. Üç küçük isteği arka arkaya göndermek, aynı üç değişikliği tek bir istekte göndermekten genellikle daha pahalıdır, çünkü uyandırma bir kez ödenir ve beş kez ödenmez. Değişiklikleri biriktirip birlikte yollamaya toplu gönderim (batching) diyoruz. Sunucuyu belirli aralıklarla “yeni bir şey var mı” diye sormaya ise yoklama (polling) diyoruz.
Ölçü yine kursun ölçüsüdür: bir döngüde gönderilen istek sayısı, istek bütçesini kaç döngüde aştı.
// 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 ölçümün adını ve sınırını taşır, Overrun aşan örnekleri sayıp dört aşımdan birini verir. Bu dosya kursun her dersinde aynıdır.
Eşitleme Döngüsü Modeli
Modelde bir döngü, eşitlemenin bir turudur. Her turda uygulamaya belli sayıda yerel değişiklik gelir: kullanıcının eklediği bir not, silinen bir kayıt, değişen bir ayar. Bu sayılara varış diyelim; sekiz turluk akış 3, 0, 5, 1, 0, 4, 2, 0 olsun. Eşitleme kipi, bu değişikliklerin kaç isteğe dönüştüğünü belirler.
// Sync.kt — bir esitleme dongusunde gonderilen istekleri sayar
fun requestCount(arrivals: List<Int>, mode: String): List<Int> =
arrivals.map { count ->
when (mode) {
"teker" -> count
"toplu" -> if (count > 0) 1 else 0
"yoklama" -> 1
else -> 0
}
}
requestCount her tur için o turda gönderilen istek sayısını döndürür. teker kipinde her değişiklik ayrı bir istektir. toplu kipinde tur boyunca biriken değişiklikler tek istekle gider ve hiç değişiklik yoksa istek gitmez. yoklama kipinde ise değişiklik gelse de gelmese de her turda bir istek gider. Önce tek tek göndermeyi, istek bütçesi iki olacak şekilde ölçelim.
// Main.kt — her degisiklik ayri bir istek olarak gonderilir
fun main() {
val arrivals = listOf(3, 0, 5, 1, 0, 4, 2, 0)
val samples = requestCount(arrivals, "teker")
println("istek: $samples")
val overrun = Overrun("teker", Budget("istek", 2), samples)
overrun.line()
countOverruns(listOf(overrun))
}
istek: [3, 0, 5, 1, 0, 4, 2, 0] teker: butce=istek<=2 | asan=3/8 -> azinda icinde: 0 azinda: 1 cogunda: 0 hepsinde: 0
Üç tur aştı: üç, beş ve dört değişiklik gelen turlar. İki değişiklik gelen tur sınıra eşit olduğu için aşım sayılmadı. Sekiz turun üçü aşmış olduğundan sonuç azında.
BA1. Model, bir değişikliği bir isteğe eşler ve her isteği aynı ağırlıkta sayar. Gerçekte istekler gövde boyutu, bağlantı kurma ve yeniden deneme bakımından birbirinden çok farklıdır. Model gönderilen istekleri sayar; bir isteğin ağda ne kadar kaldığını, ne kadar veri taşıdığını ve pilden ne aldığını kurmaz.
Toplu Gönderim
Aynı varış akışını biriktirip tek istekle göndermek, istek sayısını değişiklik sayısından ayırır.
// Main.kt — ayni degisiklikler tur basina tek istekle gonderilir
fun main() {
val arrivals = listOf(3, 0, 5, 1, 0, 4, 2, 0)
val samples = requestCount(arrivals, "toplu")
println("istek: $samples")
val overrun = Overrun("toplu", Budget("istek", 2), samples)
overrun.line()
countOverruns(listOf(overrun))
}
istek: [1, 0, 1, 1, 0, 1, 1, 0] toplu: butce=istek<=2 | asan=0/8 -> icinde icinde: 1 azinda: 0 cogunda: 0 hepsinde: 0
Hiçbir tur iki isteği geçmedi: içinde. Aynı on beş değişiklik, beş istekle gitti. Önceki ölçümle bu ölçüm arasındaki fark, bütçeyi kaç turun aştığıdır ve toplu gönderim bu sayıyı üçten sıfıra indirdi.
Platformda toplu gönderim, işi sistemin iş yöneticisine devrederek yapılır: uygulama işi ne zaman çalışması gerektiğini söyleyen koşullarla kaydeder ve sistem birçok uygulamanın işini aynı uyanıklık penceresinde çalıştırabilir.
// Platform kodu: kosullu periyodik esitleme
import android.content.Context
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import java.util.concurrent.TimeUnit
fun scheduleSync(context: Context) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresBatteryNotLow(true)
.build()
val request = PeriodicWorkRequestBuilder<SyncWorker>(1, TimeUnit.HOURS)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork("sync", ExistingPeriodicWorkPolicy.KEEP, request)
}
Bu parça platform kodudur ve burada çıktı, süre ya da pil iddia etmiyoruz. Koşullar işin yalnız uygun ağda ve pil düşük değilken çalışmasını ister; sistem, işi koşulların sağlandığı bir anda başlatır. Model, bu ölçümün kuralını kurar; model aracın kendisi değildir. Sistemin işleri gerçekte nasıl birleştirdiği, modelin göstermediği bilgidir.
Yoklama
Yoklama, değişiklik olsun ya da olmasın sunucuya sormaktır. Değişiklik sık geliyorsa yoklama makul görünebilir, ama değişikliğin gelmediği zamanlarda da aygıtı uyandırır. Bunu ölçmek için tüm turların sessiz geçtiği bir akış kuralım: hiçbir tur değişiklik getirmiyor.
// Main.kt — hic degisiklik gelmeyen akista uc kip ayni butceyle olculur
fun main() {
val quiet = List(8) { 0 }
val overruns = listOf("teker", "toplu", "yoklama").map { mode ->
Overrun(mode, Budget("istek", 0), requestCount(quiet, mode))
}
overruns.forEach { it.line() }
countOverruns(overruns)
}
teker: butce=istek<=0 | asan=0/8 -> icinde toplu: butce=istek<=0 | asan=0/8 -> icinde yoklama: butce=istek<=0 | asan=8/8 -> hepsinde icinde: 2 azinda: 0 cogunda: 0 hepsinde: 1
Hiç değişiklik yokken teker ve toplu kipleri hiç istek göndermedi, yoklama ise sekiz turun sekizinde istek gönderdi: hepsinde. Yoklamanın maliyeti değişiklik sayısına bağlı değildir; zamana bağlıdır. Kullanıcı uygulamayı kullanmasa bile, yoklama her tur kadar uyandırma ödetir.
BA2. Modelde yoklama tur başına tam bir istektir. Gerçekte yoklama aralığı uzatılabilir, sunucu “değişiklik yok” cevabını çok küçük bir yanıtla verebilir ve sistem isteği başka işlerle aynı pencerede çalıştırabilir. Bu hafifletmeler modelde yoktur, bu yüzden yoklamanın hepsinde çıkması gerçek bir uygulamanın en kötü durumuna yakındır.
Neden İstek Sayısını Sayıyoruz
Pil tüketimi doğrudan ölçülmez, çünkü onu ölçmenin güvenilir bir yolu bu depoda yoktur. Onun yerine pili etkileyen davranışlardan birini sayıyoruz: gönderilen istek sayısı. Bu seçimin bir bedeli var. İstek sayısı pilin yerine geçmez; yalnız onunla ilişkili bir vekildir. İki istek bazen tek büyük istekten ucuza gelir, bazen pahalıya. Ders bu yüzden pil hakkında hiçbir sayı vermez ve pili yalnız uyandırma kavramıyla anar.
İstek sayısının iyi bir ölçü olmasının nedeni, doğrudan tasarım kararına bağlanmasıdır. Toplu göndermek ya da yoklamak, geliştiricinin verdiği bir karardır ve istek sayısı bu kararın sonucudur. Ölçüm, kararın etkisini kodun içinden okumayı sağlar.
Sessiz akış ölçümü de bu yüzden ayrı tutulur. Gerçek bir uygulama her zaman yoğun kullanılmaz; gecenin büyük bölümünde aygıt boştadır ve uygulamaya hiçbir değişiklik gelmez. O saatlerde pili tüketen şey kullanıcının yaptığı iş değil, uygulamanın kendi kendine yaptığı iştir. Yoklama tam olarak bu tür bir iştir. Bu yüzden bir eşitleme tasarımı yalnız yoğun akışta değil, sessiz akışta da sınanır.
İş yöneticisine devredilen iş ayrıca bir tasarım sorusu doğurur: işin ne kadar gecikebileceği. Bir not kaydı birkaç dakika gecikebilir, bir mesaj gecikemez. Koşulları gevşetmek sistemin işleri birleştirme şansını artırır, sıkılaştırmak azaltır. Model bu pazarlığı kurmaz; yalnız istek sayısını verir ve gecikmeyi ölçmez.
Eşitleme kipi seçiminde üç soru sorulur. Değişiklik ne kadar sık geliyor, karşı taraf ne kadar gecikmeyi kabul ediyor ve uygulama kapalıyken de eşitleme gerekiyor mu. Sık gelen değişiklikte toplu gönderim kazancı artar, çünkü her toplu istek daha çok değişikliği taşır. Gecikmeyi kabul etmeyen akışta toplu gönderim yerini anlık göndermeye bırakır ve bütçe bu durumda gevşetilir. Uygulama kapalıyken de eşitleme isteniyorsa iş, uygulama süreci olmadan çalışabilen bir iş olarak kaydedilir. Modeldeki üç kip, bu üç sorunun cevabına karşılık gelen en yalın seçimlerdir.
Ölçümü üretimde yorumlarken aygıtın durumu da hesaba girer. Aynı istek, ağ donanımı zaten uyanıkken ucuza, boştayken pahalıya gelir. Bu fark modele girmez: model her isteği bağımsız sayar. Bu yüzden istek sayısı, iki tasarımı karşılaştırmak için sağlam bir ölçüdür ama bir tasarımın gerçek pil maliyetini söylemek için yetersizdir.
Toplu gönderimde pencerenin uzunluğu da bir ayardır. Pencere uzadıkça her toplu istek daha çok değişiklik taşır ve istek sayısı düşer; ama değişikliklerin karşı tarafa ulaşma gecikmesi de uzar. Modelde pencere bir tur kadardır ve değiştirilmez. Gerçek bir uygulamada pencereyi seçmek, istek sayısı ile gecikme arasında bir denge kurmaktır ve bu dengeyi yalnız ürünün gereksinimi belirler.
Hata durumları da istek sayısını etkiler. Başarısız bir istek yeniden denenirse aynı değişiklik için ikinci bir uyandırma ödenir. Yeniden deneme aralığı uzayan bir tasarım bu maliyeti sınırlar. Model her isteği başarılı sayar ve yeniden denemeyi içermez; bu yüzden gerçek bir akışın istek sayısı modelin verdiğinden büyük olabilir.
Son olarak ölçümün hangi kipte alındığı rapora yazılır. Aynı akışta üç kip üç farklı sayı verir ve sayıyı tek başına okuyan biri hangi tasarımı ölçtüğünü bilemez. Kip adını, bütçeyi ve toplam istek sayısını birlikte yazdırmak, bir satırı sonradan yorumlamayı mümkün kılar; dersin çıktılarında bu üçü her satırda yer alır.
Yaygın Yanılgılar
“Yoklama sık değişiklik olan uygulamalarda zararsızdır.” Değişiklik sık geliyorsa yoklamanın boşa giden turları azalır, ama sıfır olmaz. Dersin sessiz akış ölçümünde yoklama, hiç değişiklik yokken bile her turda istek gönderdi.
“Toplu gönderim her zaman daha iyidir.” Toplu gönderim, değişikliklerin gönderilmesini geciktirir. Değişikliğin anında karşı tarafa ulaşması gereken bir akışta bu gecikme kabul edilemeyebilir. Ders yalnız istek sayısını ölçtüğü için gecikmeyi göstermez; bu, toplu gönderimin bedelsiz olduğu anlamına gelmez.
“Aynı aşım, aynı tasarruf demektir.” Sıfır bütçede tek tek gönderim ile toplu gönderim aynı aşımı verdi, oysa biri on beş, diğeri beş istek yolladı. Kural bu farkı görmez; toplam sayı ayrıca yazdırıldığında görünür.
“Platform işi sistem birleştirir, bu yüzden ölçmeye gerek yoktur.” Sistem uygun bir pencerede işleri bir araya getirebilir, ama uygulamanın kaç iş kaydettiği ve bunların ne sıklıkla çalışması istendiği hâlâ uygulamanın kararıdır. Sistemin birleştirme payı, kaydedilen işin kendisini azaltmaz.
Bütçeyi Nasıl Seçmeli
İstek bütçesi, tur başına izin verilen istek sayısıdır. Bütçeyi sıfır koymak, hiçbir turun istek göndermemesini ister; bu, değişiklik gelen her turu da aşım yapar ve yalnız sessiz turları temiz bırakır. Bütçeyi bire koymak, tur başına tek isteği kabul eder; bu toplu gönderimi içinde tutar ve tek tek göndermeyi, bu akışta, çoğunda bırakır. Dersin ilk iki ölçümünde bütçe ikiydi. İkisi arasında bir yerde seçilen değer, uygulamanın ne kadar gecikmeyi kabul ettiğine bağlıdır.
Sınırlayıcı Ölçüm
Şimdiye kadar iki şey gördük: toplu gönderim istek sayısını düşürür, yoklama sessiz akışta bile istek yollar. Kuralın göremediği şeyi görmek için aynı varış akışını üç kiple, bu kez sıfır bütçeyle ölçelim ve toplam istek sayısını da yazdıralım.
// Main.kt — ayni akis uc kiple sifir butceyle olculur ve toplam istek yazilir
fun main() {
val arrivals = listOf(3, 0, 5, 1, 0, 4, 2, 0)
val overruns = listOf("teker", "toplu", "yoklama").map { mode ->
val samples = requestCount(arrivals, mode)
println("$mode toplam: ${samples.sum()}")
Overrun(mode, Budget("istek", 0), samples)
}
overruns.forEach { it.line() }
countOverruns(overruns)
}
teker toplam: 15 toplu toplam: 5 yoklama toplam: 8 teker: butce=istek<=0 | asan=5/8 -> cogunda toplu: butce=istek<=0 | asan=5/8 -> cogunda yoklama: butce=istek<=0 | asan=8/8 -> hepsinde icinde: 0 azinda: 0 cogunda: 2 hepsinde: 1
Sıfır bütçede teker ve toplu aynı aşımı verdi: ikisi de çoğunda. Oysa biri on beş, diğeri beş istek gönderdi. Kural, bir turun bütçeyi aşıp aşmadığına bakar, kaç istek fazla gönderdiğine bakmaz; bu yüzden toplu gönderimin on istekle ölçülen tasarrufunu bu bütçede göremez. Aynı işlev iki bütçede iki aşım verir: bütçe iki iken teker azındaydı, sıfırken çoğunda. Tasarrufu görmek için ya toplam sayıya bakmak ya da bütçeyi toplu gönderimin ulaşabileceği bir sınıra, yani turda bir isteğe çekmek gerekir. Dersin ilk iki ölçümü bu yüzden iki bütçeli bir resim çizdi: bütçe iki iken fark görünür, sıfırken kaybolur.
Yoklama ise bu ölçümde de tek başına hepsinde çıktı, çünkü değişiklik gelmeyen üç turda da istek gönderdi. Sekiz istek toplu gönderimin beş isteğinden fazla, tek tek göndermenin on beş isteğinden azdır; kuralın hepsinde demesi, yoklamanın en çok istek gönderdiği anlamına gelmez, her turda istek gönderdiği anlamına gelir.
BA3. Modeldeki varış akışı sabit ve sekiz turdur. Gerçek kullanımda değişiklikler kümeler hâlinde gelir, uzun sessiz dönemlerle kesilir ve kullanıcıya göre çok değişir. Tek bir akışta kazanılan sonuç, başka bir akışa taşınmadan önce o akışta yeniden ölçülmelidir.
Özet
- Bu dersin aşımı: içinde. Toplu gönderimle aynı değişiklikler tur başına en çok bir isteğe dönüştü ve iki isteklik bütçeyi hiçbir tur aşmadı.
- İstek, aygıtı uyandırdığı için pil harcar; toplu gönderim uyandırma sayısını değişiklik sayısından ayırır.
- Tek tek gönderim aynı akışta bütçeyi sekiz turun üçünde aştı: azında.
- Yoklama hiç değişiklik gelmeyen akışta da her turda istek gönderdi ve sıfır bütçede hepsinde çıktı.
- Kural turun bütçeyi aşıp aşmadığını sayar. Sıfır bütçede on beş ve beş istek aynı aşımı verdi; tasarrufu görmek için toplam sayıya da bakmak gerekir.
Sonraki Adım
Şimdiye kadar ölçtüğümüz her sayı bir kez ölçüldü ve o bir sayı kabul edildi. Oysa ölçüm gürültülüdür: aynı işi iki kez çalıştırınca farklı sayı çıkabilir. Sıradaki ders, Karşılaştırmalı Ölçüm, tekrarlanan ölçümlerde bir gerilemenin nasıl ayırt edildiğini ve ısınma yinelemelerinin neden ölçüme katılmadığını ele alır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.