Yazılımların Performansı ve Performans İyileştirme

Java’nın performansı ile ilgili olarak daha önce bu yazıyı yayınlamıştım. Şimdi bu konuda biraz daha temel tanımlar ve metot üzerinden ilerleyelim. Sonrasında Java’ya özel performans konularını ele alırız.

Aslında performans derken birden fazla şeyi kastederiz, dolayısıyla performans kelimesi aslında “hız” etrafında kümelenmiş pek çok farklı kalite kriterinin şemsiye ismidir. Bakın o şemsiye altında neler olabilir:

  • Son kullanıcı cevap zamanı (responsiveness): Son kullanıcının sistemi kullanırken yaptığı hareketlere yönelik sistemin cevap verme zamanıdır. Son kullanıcıyı en fazla ilgilendiren ve uzun olduğunda canını sıkan ve çalışma ritmini bozan faktördür. “Sistem yavaş” dendiğinde çoğu zaman bu kastedilir ama çok farklı sebepleri olabilir. Performans iyileştirme çalışmalarının en öncelikli hedefi, son kullanıcı cevap zamanını makul seviyelere indirmek ve ortalama olarak kabul edilebilir bir son kullanıcı performans sağlamaktır.
  • Ölçeklenirlik (scalability): Uygulamanın davranışının artan yük altında nasıl değiştiğini ifade eder. Ölçeklenirliği en temelde etkileyen iki faktör, artan canlı kullanıcı sayısı ve büyüyen veri miktarıdır. Ölçeklenirliği kötü olan sistemlerin son kullanıcı cevap zamanı da kötüdür. Ama bu durum her zaman ortaya çıkmaz, bazen belli zamanlarda daha fazla görünür olur. Bu durum tamamen sistem yükünün devamlı değişir yapıda olmasından kaynaklanır. Performans iyileştirme çalışmalarında ölçeklenirlik, son kullanıcı cevap zamanını makul sınırlarda tutacak şekilde, ikinci öncelik olarak ele alınır.
  • Bellek kullanımı (memory footprint): Uygulamanın bellek kullanımıdır. Genelde bellek kaçağı (memory leak) olarak ortaya çıkar ve kurumsal uygulamaların ölçeklenirliğini en fazla etkileyen faktördür. Bellek kaçakları, Java ile geliştirilmiş uygulamalarda çöp toplama (garbage collection) mekanizmasının sık ve uzun sürelerle devreye girmesine sebep olduğundan ve bu faaliyet ciddi kaynak tükettiğinden, nihayetinde son kullanıcı cevap zamanını menfi yönde etkiler. Bellek kullanımı iyileştirilmediği taktirde, nihayetinde ölçeklenirliği tamamen ortadan kaldırır ve sistemleri, sıklıkla tekrar başlatılması gerekli hale getirir. Performans iyileştirme çalışmalarınında bellek kullanımına el atmadan ya da çöp toplama faaliyetini optimize etmeden sağlıklı bir ölçeklenirlik elde etmek zordur.
  • Üretilen iş (throughput): Belli bir zamanda uygulamanın ürettiği iş miktarıdır. Özellikle toplu yapılan basım, faturalandırma, görüntü işleme, veri aktarımı vb. işlerde çok daha önemli hale gelir, çünkü aslolan bir raporun basılmasının ya da bir abonenin faturasının oluşturulmasından ziyade, belli bir sürede bu işlerden ne kadar çok yapıldığıdır. Bundan dolayı çoğu zaman bu kriter, son kullanıcı cevap zamanı ile çelişen tabiata sahip olur. Örneğin bir abonenin faturasını 10 saniyede üreten bir sistem, bu performans ile son kullanıcısını memnun etmeyebilir. Ama aynı sistem arka tarafta batch olarak, paralel işlem yaparak, saatte 1,000,000 tane fatura üretiyordur ve bu durum son derece kabul edilebilir bir iş üretimi olabilir. Performans iyileştirme çalışmalarınında üretilen işi arttırıcı çalışmalar, uygulamanın mahiyetine göre değişecek şekilde gerekebilir.
  • Başlama zamanı (startup time): Uygulama ilk başlatılırken, kullanıcı isteklerine cevap vermek için hazır oluncaya kadar geçen zamandır. Bu kriter çoğunlukla istemci (client) tarafındaki uygulamalar için önemlidir. Örneğin kendi kişisel makinanızdaki bir kelime işlemcinin iconuna tıkladıktan ne kadar süre sonra kullanıma hazır hale geldiği, son kullanıcılar olara gittikçe daha fazla dikaktimizi çekmektedir. Bundan dolayı örneğin yılardır kullandığımız mekanik hard diskleri, çok daha hızlı olan SSDlerle değiştiriyoruz.  Fakat sunucu (server) tarafındaki uygulamaların örneğin 15-20 dakikada ayağa kalkıyor olması her zaman problem olarak görülmeyebilir. Bundan dolayı bu kriter performans iyileştirme çalışmalarınında genellikle çok da öncelikli olarak ele alınmaz.

Performans iyileştirme çalışmalarında yöntemsiz, gelişi güzel gitmek çoğu zaman sıklıkla karşılaştığımız bir durumdur. Bundan dolayı attığımız taşın ürküttüğümüz kurbagaya deyip deymemesi durumu söz konusudur. Örneğin ortalama olarak 10 saniyede cevap olarak dönen bir arama arayüzünü hızlandırmak için zaman harcayıp sonunda 8 saniyeye indirmiş olmak hiç bir zaman kullanıcı tarafında taktir edilecek bir durum olarak görünmeyecektir. Bu kadar uğreşmak yerine 10 saniye boyunca işlemin durumunu gösteren bir progress bar çıkarmak, son kullanıcıyı, aramanın 8 saniyeye indiği duruma göre daha fazla memnun edebilecektir. Çünkü aslolan son kullanıcının algısıdır. Bundan dolayı son kullanıcı cevap zamanı iyileştirmelerinde, nereye bakmanız, neyi ne kadar iyileştirmenizin gerektiği son derece önemlidir.

Performans iyileştirme çalışmalarında yapılan bir başka yanlış da var olan durumu tespit etmemek ya da edememektir.  Bu durum çoğu zaman teknik bir problem olarak ortaya çıkar. Yani örneğin müşterinin çektiği performans sıkıntısının aynısını üretmekte zorlanırsınız, gerçek ortamda da o sıkıntıyı ölçmeniz zordur. Kendi test ortamınızda ölçüp, iyileştirmeyi yapıp sonra yine aynı şartlarda performansı ölçmeniz gereklidir ki ne kadar yol katettiğinizi anlayabilesiniz. Dolayısıyla ölçüm yapmak, çok sayıda ve aynı şartlarda ölçüm yapmak, olabildiğince ortalamalar üzerinden gitmek, performans iyileştirme çalışmalarının dikkat edilmesi gereken önemli noktalarındandır.

Yöntemden bahsetmişken, basit adımlardan oluşan bir performans iyileştirme sürecinden bahsedebiliriz. Performansı sıkıntılı olan bir yazılım sisteminde iyileştirmeler yapmak için aşağıdaki adımları takip edebiliriz:

  1. Performans problemlerini tanımlamak ve listelemek,
  2. Her bir problemin performansını ölçmek ve iyileştirme hedefini belirlemek,
  3. Problemleri önceliklendirmek,
  4. En yüksek öncelikli problemi ele almak,
  5. Problemin iyileştirme gerektiren noktalarını belirleyip üzerinde çalışmak,
  6. İyileştirilmiş performansı ölçmek,
  7. 5. ve 6. maddeyi, performans hedefini yakalayana kadar tekrar etmek,
  8. 4. maddeden devam etmek.

Bu şekilde bir süreç ile, nereden nereye geldiğimizi belirleyebileceğimiz gibi, değer katan iyileştirmelerde bulunma ihtimalimiz de artmış olur.

Yukarıda tarif edilen süreci kullanarak hedef performans ölçütlerine ulaşmak için temelde üç farklı çözüm şekli kullanılır:

  • Kod değişikliği: Performans olarak sıkıntılı kod parçacıklarını daha etkin çalışanlarla değiştirmektir. Kodda yapılacak değişikliklerle çözüme ulaşmak çoğu zaman en hızlı bulunan ve en hızlı iyileştirilendir. Bu tür kod değişiklikleri hem algoritmik hem de dil tabanlı olabilir. Fakat bu tür değişikliklerin etkisi çoğu zaman azdır.
  • Mimari değişiklik: Yazılım sisteminin alt parçalarınında yapıalcak organizasyonel değişıkliklerdir. Yaygın kod değişiklikleri çoğu zaman kendini mimari değişikliğe çevirir. Mimari değişikliklerin hem maliyeti yüksektir hem de sağladığı iyileştirme oranı çok daha iyidir. Cache kullanmayan bir sistemin cache kullanmaya başlaması ya da veri tabanında örneğin partition kurgulamak vb. yazılım mimarisi değişiklikleri çok sık yapılan iyileştirme çalışmalarındandır. Mimari değişıklikler her zaman kod değişıkliği olarak ortaya çıkmaz, örneğin daha hızlı CPUlara ya da yüksek RAMe sahip olacak şekilde donanım altyapısını iyileştirmek, ağ altyapısını düzenlemek ya da istemcileri güclendirmek de bu tür mimari iyileştirmelerdendir.
  • JVM tuning: Java’nın çalışma zamanı mekanizması olan JVM’in davranışının gözlemlenmesi ve sistemin ihtiyaçlarını karşılayacak şekilde düzenlenmesidir. JVM tuning çalışması, genel olarak JVM’in ayağa kalkarken aldığı parametrelerin düzenlenmesidir. Bu parametreler bellek, çöp toplayıcı, JIT vb. konularla ilgilidir olup genelde her yeni Java SE sürümüyle birlikte ciddi yeniliklerle gelmektedir. Bu amaçla çalışµa zamanında JVM(ler) gözlemlenir, çoğu zaman ayarla-gözlemle-değiştir şeklindeki bir çevrimle parametreler optimum noktaya getirilir. JVM tuningin özellikle gerçek ortam üzerinde, canlıya geçiş öncesinde yapılıp, belli aralıklarla tekrarlanması gereklidir.

Performans ölçümleri dolayısısıyla da gerektikçe performans iyileştirme çalışmaları, bir projenin, yazılım ürününün hayatında farklı evrelerde yapılabilir. Bu evreleri şöyle özetleyebiliriz:

  • Mimariyi kurgularken: Performans ile ilgili en temel çalışmaların yapılması gereken evredir. Elimzide var olan performans ihtiyaçlarını karşılayacak şekilde bir çekirdek mimari kurup, gerekli ölçümleri yapmak ve icap eden değişikliklerden sonra bu çekirdeği nihai mimari olarak kabul etmek, performans konusunda sıkıntı çekmemek için yapılması gereken ilk çalışmadır. Sıkça ıskalananması ile bilinir 🙂
  • Kod geliştiriken: Kritik performans ihtiyaçlarını karşılamada şüphelenilen kod parçalarının daha geliştirilirken ölçülmesi ve iyileştirilmesidir. Dolayısıyla da kodun henüz check in yapılmadan performansının iyileştirilmesi, ilk madde gibi genelde ıskaladığımız ama performans amacıyla yapılacak değişikliğin maliyetini en az seviyede tutan çalışmadır.
  • Test süresince: Fonksiyonel ve performans testleri süresince genelde kod değişikliği ve mimari değişiklikler yerine getirilir. Uygulamanın ölçeklenirliğin belirlenmesi için fonksiyonel testler dışında yük (load) testlerinin de yapılması gereklidir. Test çalışmalarında JVM tuning ile ilgili bazı bulgular da elde edilir ama bu kısma henüz fazla önem verilmez. Dolayısıyla temelde yük testleri söz konusudur. Yük testleri, test otomatizasyon araçları yardımıyla, uygulamaya uygun yük sağlayarak yapılır ve temelde uygulamanın ölçeklenirliğini test etmek için kullanılır.
  • Canlı öncesi: Canlıya geçiş öncesinde, uygulamanın gerçek ortama taşınmasıyla ortaya çıkabilecek yeni performans problemleri ile canlı ortamın JVM mimarisinin ayarlanması ve JVM tuninginin yapılması söz konusudur. Bu amaçla aşağıdaki yoğun yük testler söz yapılır.
  • Canlı sonrası: Canlıya geçiş sonrasında uygulamanın gözlemlenmesi (monitor) ve ortaya çıkan performans problemlerinin giderilmesidir. Bu çalışma daha çok, canlı öncesi oluşturulan JVM mimarisinde iyileştirilmeler yapılmasını gerektirecektir.

Performans problemlerimiz sıkıkla, ıskaladığımız mimari çalışmaların bir bedeli olarak ortaya çıkar ve bu topraklarda genelde donanım altyapısına bol para akıtarak çözülmeye çalışılır. Halbuki bir önceki yazıda verdiğim Facebook örneğindeki gibi sağlıklı mimari çalışmalar ve erken tanılarla bu problem devasa boyutlara çıkmadan önlenebilir.

Performans problemsiz projeler dilerim 😉

Toplam görüntülenme sayısı: 2162