Android Macrobenchmark ve Baseline Profiles

Hüseyin Serkan Özaydin
7 min readJun 29, 2023

Selamlar, bugün bir android uygulama geliştirken en çok ihtiyacınız olan performansı nasıl ölçebileceğinizden ve ölçtüğünüz performansı nasıl arttırabileceğinizden bahsedeceğim.

Bugünlerde sağda solda her yerde “Uygulamamızın performansını %50 arttırdık”, “Kullanıcı sayımız arttı” veya “Nasıl daha iyi deneyim sunduk” şeklinde yazılar görüyorsunuzdur. Bu yazıları okuduğunuzda bizim aşağıda bahsedeceğimiz konuları görüyor olacaksınız :)

Bir girizgah yaptığımıza göre yavaştan asıl konularımıza geçelim.

Macrobenchmark

Bu konudan bahsetmeden önce şu kelimeye bir açıklık getirelim “benchmark”. Nedir benchmark kelimesi? Benchmark kelimesini şu anlama gelmekte;

Herhangi bir cihazın herhangi bir işi yapma performansını ölçmek

Peki Android Evren’inde Benchmark diyince aklımıza ne gelmeli? 2 kelime gelmeli onlar şunlar;

  • Microbenchmark
  • Macrobenchmark

Bugün ki konumuz “Macrobenchmark” ama “Microbenchmark” konusunada değinmek istiyorum. Benchmarkın ne olduğundan yukarıda bahsettik ve yazı boyunca bu kelimenin anlamını kafamızın bir köşesine yazalım bu sayede bazı şeyleri anlamlandıracağız.

Microbechmark: Yazdığınız kodun performansını ölçmek için kullanılır. Bu bir for döngüsü olur veya bir CRUD işlemi olur farketmez IDE de gördüğünüz herhangi bir kod parçacığının performansını ölçmek için Microbenchmarkı kullanabilirsiniz.

Microbenchmarktan bahsettiğimize göre Macrobenchmarktan bahsetmeye devam edebiliriz. Microbenchmark ile bir kod parçacağının performansını ölçerken Macrobenchmark ile kullanıcıya sunulan bir storynin performansını ölçebilirsiniz. Peki story derken neyden bahsediyorum? Bir örnek ile açıklayayım;

Kullanıcı eline uygulamayı aldı ve giriş yaptı. Bundan sonra ana ekrana gelip ekranı aşağı kaydırıp herhangi bir gönderiye beğeni attı

Yukarıda italik olarak belirttiğim cümle bir storydir aslında bir hikayedir ve bu hikayeleri kullanıcıya developer sunuyor. Hadi diyelim ki kullanıcı ana ekrana gelince profil sayfasına gitti ve ismini değiştirdi. Bu da bir storydir. Neymiş Macrobenchmark kullanıcıya sunduğumuz hikayelerin performansını ölçmek için kullanılırmış.

Şimdi bir örnek yapalım. Sizin için bir demo hazırladım. Bu demoda bir splash screen geliyor ve bir süre sonra bir mesajlaşma ekranı geliyor kullanıcı bu mesajlaşma ekranında istediği mesajı atabiliyor.

Demo uygulama ekranları

Birlikte bir Macrobenchmark Yapalım

Uygulamamızı açtık ve aşağıda bulunan görseldeki gibi “New Module” seçeneğine tıkladık.

New Module seçeneğini seçtikten sonra karşınıza aşağıdaki gibi bir ekran gelecek ve burada “Benchmark” sekmesini seçeceğiz.

Benchmark” seçeneğini seçtikten sonra karşınıza yukarıdaki gibi bir ekran gelecek. Bu ekranda “Macrobenchmark” seçeneğini seçip “Finish” e tıklayalım (Microbenchmark yazmak isterseniz “Microbenchmark” seçeneğini tıklamanız gerekmekte)

Yukarıdaki işlemleri yaptıktan sonra dosya yapınızda “benchmark” adında bir modül oluşacak bunula birlikte yeni bir Build Type app gradle dosyanıza gelecek

Dosya yapısındaki benchmark modülü
buildType

Build Type bloğuna gelen “benchmark” bloğundan biraz bahsetmek istiyorum. Biz Macrobenchmark ile kullanıcıya sunduğumuz kullanıcı deneyimini ölçmek istiyoruz ve kullanıcıya sunduğumuz ortamın aynısını oluşturmamız gerekiyor. Yani Macrobenchmarkı çalıştırırken “debug” aktif olmamalı. Çünkü “debug” uygulamanın runtime performansını etkiliyor. Bununla birlikte Macrobenchmarkı gerçek bir cihazda çalıştırmamız gerekiyor. Bununda sebebi kullanıcı bir emülatör üstünde uygulamayı kullanmaz gerçek bir cihazda kullanır. Bunlardan bahsettikten sonra dosya yapımızda bulunan “benchmark” modülüne daha yakından bakabiliriz.

Benchmark modülüne bakınca bizim için oluşturulmuş bir bechmarkı göreceksiniz. Bu benchmark uygulamanın açılma hızını ölçüyor. Peki biraz daha derine inelim ve bu sınıfta bulunan parametrelerin anlamı neymiş öğrenelim.

Macrobenchmarklar bir test dosyasıdır bundan dolayı @get:Rule veya @RunWith gibi annotationları görebilirsiniz. Burada “startup” adında bir test görüyorsunuz ve bu test fonksiyonunda parametreler var. Bunlara teker teker bakalım.

packageName: Projemizin paket ismini buraya yazıyoruz

metrics: Yaptığımız işlemin neyini ölçek istiyoruz açılma hızımı, frame oluşma hızımı gibi metrikleri burada belirtiyoruz. Biz açılma hızını ölçmek istediğimiz için “StartupTimingMetric()” yazıyoruz bu parametreye. Diğer parametler için aşağıdaki bağlantıya tıklayabilirsiniz.

iterations: Yaptığımız testin kaçkez çalışmasını istiyorsak buraya o değeri yazıyoruz. Sonuç olarak uygulama açılırken çok farklı değişkenler araya girebiliyor. Mesela ilk açılışta arkada istemediğimiz bir servis çalışıyor olabilir bu da uygulamanın açılma hızını etkileyebilir. Bunun için testi birkaç kez çalıştırmak daha iyi oluyor.

startupMode: İlk ekranın nasıl açılacağını burada belirtiyoruz. Biz gerçek uygulamanın açılma hızını ölçmek istediğimiz için COLD diyoruz. Diğer seçenekler için aşağıdaki bağlantıya tıklayabilirsiniz.

En son olarak en aşağıdaki blok yani “MacrobenchmarkScope” burada performansını ölçmek istediğimiz storyi yazıyoruz. Mesela biz uygulamanın açılma hızını ölçmek istediğimiz için “pressHome” ve “startActivityAndWait” fonksiyonlarını yazıyoruz. Bu arada burada “UI Automator” kullanıyoruz. UI Automator’ü end to end testler için kullanırız genellikle. Daha fazla bilgi için aşağıdaki bağlantıya tıklayabilirsiniz.

Testimizi yazdık şimdi bu testi gerçek bir cihazda deneyelim.

İlgili işlemi 5 kez yaptık ve sonucunu aşağıda görüyoruz.

Uygulama min 327 ms ortalama 356 ms ve en fazla 376 ms de açılmış (Tavsiyem her zaman ortalama zamanı baz almanız). Android Studio bu sonucu bir JSON dosyası olarakta veriyor ve bu JSON dosyasının yolunu size loglardan veriyor. Siz bu JSON dosyasına göre her geliştirme sonucunda uygulamanın performansında bir düşüş oldu mu olmadı mı diye kontrol edebilirsiniz. Tabi bu kontroller için bir CI sistemi kurmak gerekiyor.

Uygulamamızın performansını ölçtük peki uygulamanın performansını arttırmak istiyorsak ne yapmalıyız? İşte tam burada yeni bir kelime hayatımıza giriyor…

Baseline Profiles

Baseline Profiles uygulamamızın performansını arttırmamıza yarıyor. Peki nasıl?

Bunu anlatmak için size bir zaman çizgisi oluşturmam gerekiyor ve Android dünyasında herşey toz bulutuyken neler vardı onlardan bahsetmem gerekiyor. Hadi başlayalım

Java dünyamıza geldiği zaman 2 kavramı hayatımıza soktu JVM ve JIT

Biz java kodu yazdığımıza zaman derleyici yazdığımız java kodunu class dosyasına ve oluşan class dosyaları ise JIT sayesinde byte dosyalarına dönüşüyordu ve bu byte dosyaları JVM üstünde çalışıyordu.

Android dünyamıza Java ile girdiği zaman yukarıdaki sistemi kullanmak istedi ama bir problem çıktı. JVM + JIT ikilisi mobil gibi az kaynağa sahip olan cihazlar için geliştirilmemişti.

Bu problemden ötürü hayatımıza yeni bir kavram girdi. DVM

Artık JIT class dosyalarını dex dosyasına çevirip DVM üstünde çalıştırıyordu. Herşey güzel gidiyordu ama burada da şöyle bir problem çıktı ortaya. O problem;

Uygulama her açıldığı zaman proje tekrar derlenip dex dosyaları her seferinde tekrar oluşturuluyordu buda kullanıcı deneyimini kötü etkiliyordu.

Bu problem ciddi bir problemdi ve bunun için hayatımıza 2 yeni kavram girdi. ART ve AOT

Artık java dosyaları dex dosyasına uygulama açıldığı zaman dönüşmüyordu. Artık uygulama cihaza ilk yüklendiği zaman class dosyaları AOT ile dex dosyasına dönüştürülüyordu ve dex dosyası ART üstünde çalışıyordu. Bu sayede uygulamaların açılma hızı artmıştı. Herşey çok güzel gibi gözüküyor değil mi? Uygulamanın açılma hızı arttı. Ama bu sefer başka sorunlar baş göstermeye başladı. Onlar;

  • Uygulama yüklendiği zaman dex dosyaları cihazda tutulmaya başlandığı için cihazlarda hafıza problemleri çıkmaya başladı
  • Developer her seferinde geliştirme yapmak için uygulamayı cihaza yükledikçe dex dosyalarının oluşması için zaman kaybetmeye başladı.

(Hatırlarsanız bir ara geliştirme yaparken yaptığımız geliştirmeleri cihazda göremiyorduk :) onun sebebi buydu )

Google en son Android 8 ile birlikte şuna karar verdi;
Yav biz oluşması uzun zaman alan dex dosyalarını uygulama yüklendiği zaman oluşturup cihaza koyalım ve hızlı oluşan dex dosyalarını ise uygulama açılınca oluşturalım. Buna da Baseline Profiles diyelim

Bu fikir ile birlikte hayatımıza JIT geri döndü ama AOT ile birlikte çalışma şartı ile :)

Aşağıdaki yazı ile bu hikayeyi daha detaylı okuyabilirsiniz.

İşte hangi dex dosyasının uygulama açılırken oluşacağına hangisinin ise uygulama yüklenirken oluşacağına Baseline Profiles karar verdi. İşte bu Baseline Profiles default olarak sizin kontrol edemeyeceğiniz şekilde oluşuyordu. Artık kendi projemize özel olarak Baseline Profiles dosyaları oluşturabiliyoruz. Gelin aşağıda birlikte yukarıda Macrobenchmark testini yaptığımız demomuz için Baseline Profiles dosyası oluşturup performansı arttıralım :)

Haydi Demoya

Aşağıdaki sınıfı oluşturdum ve uygulamayı açtığımız storynin baseline dosyasını oluştur dedim(Macrobenchmarkta yazdığımız işlemlerin aynısını buraya yazıyoruz)

Bana aşağıdaki dosya yolunda bulunan bir txt dosyası oluşturdu ve bu dosyayı app modülünün altına “baseline-prof.txt” olarak attım.

Şimdi test etme vakti. Aşağıdaki gibi bir sınıf oluşturdum (Macrobenchmark)

Burada “startup” fonksiyonunu 2 farklı compilationMode ile çalıştıracağım. Bu Complication Modeler neler bakalım
None: Herhangi birşey yapma olduğu gibi uygulamayı aç
Partial: Eğer bir baseline profiles dosyası var ise onunla uygulamayı aç

Yukarıdaki testi çalıştırıyorum ve sonuçlarına bakıyorum

Uygulamamızın hızı baseline profiles yokken ortalama 421 ms iken baseline profiles ile 257 ms ye kadar düşmüş. Neredeyse uygulamanın açılma hızı yarı yarıya artmış.

Duyduğuma göre Jetpack Compose ile geliştirilmiş uygulamalarda performans artışı daha fazla imiş.

Evet arkadaşlar bu yazımızda uygulamızın hızını ölçtük ve uygulamamızın hızını arttırdık. Artık bunları uygulayarak yazımın başında bahsettiğim gibi “Uygulamamızın hızını nasıl %50 arttırdık?” gibi yazılar paylaşabilirsiniz :)

Aşağıda yukarıdaki konularla alakalı bir video paylaşıyorum (Android developerların aşağıdaki adamı takip etmesini öneririm)

--

--