Geliştirdiğiniz uygulamanın türü ne olursa olsun, performansını optimize etmek, hızlı yüklenmesini ve sorunsuz etkileşimler sunmasını sağlamak kullanıcı deneyimi ve uygulamanın başarısı için kritik öneme sahiptir. Bunu yapmanın bir yolu, bir zaman aralığında çalışırken arka planda neler olduğunu görmek için profilleme araçlarını kullanarak bir uygulamanın etkinliğini incelemektir. Geliştirici Araçları'ndaki Performans paneli, web uygulamalarının performansını analiz etmek ve optimize etmek için harika bir profilleme aracıdır. Uygulamanız Chrome'da çalışıyorsa uygulama yürütülürken tarayıcının ne yaptığına dair ayrıntılı bir görsel genel bakış sunar. Bu etkinliği anlamak, performansı artırmak için harekete geçebileceğiniz kalıpları, darboğazları ve performans açısından önemli noktaları belirlemenize yardımcı olabilir.
Aşağıdaki örnekte, Performans panelinin kullanımı adım adım açıklanmaktadır.
Profil oluşturma senaryomuzu ayarlama ve yeniden oluşturma
Yakın zamanda, Performans panelini daha iyi performans gösterecek şekilde geliştirme hedefi belirledik. Özellikle büyük hacimli performans verilerinin daha hızlı yüklenmesini istiyorduk. Örneğin, uzun süren veya karmaşık süreçlerin profili oluşturulurken ya da ayrıntılı veriler yakalanırken bu durum geçerlidir. Bunu başarmak için öncelikle uygulamanın nasıl çalıştığını ve neden o şekilde çalıştığını anlamak gerekiyordu. Bu da bir profil oluşturma aracı kullanılarak sağlandı.
Geliştirici Araçları'nın kendisi de bir web uygulamasıdır. Bu nedenle, Performans paneli kullanılarak profillendirilebilir. Bu panelin kendisini profillemek için Geliştirici Araçları'nı açabilir ve ardından ona bağlı başka bir Geliştirici Araçları örneği açabilirsiniz. Google'da bu kurulum Geliştirici Araçları'nda Geliştirici Araçları olarak bilinir.
Kurulum tamamlandıktan sonra, profili oluşturulacak senaryo yeniden oluşturulup kaydedilmelidir. Kafaları karıştırmamak için orijinal Geliştirici Araçları penceresi "birinci Geliştirici Araçları örneği", birinci örneği inceleyen pencere ise "ikinci Geliştirici Araçları örneği" olarak adlandırılacaktır.

İkinci Geliştirici Araçları örneğinde, Performance (Performans) paneli (bundan sonra perf paneli olarak adlandırılacak), bir profili yükleyen senaryoyu yeniden oluşturmak için ilk Geliştirici Araçları örneğini gözlemler.
İkinci DevTools örneğinde canlı kayıt başlatılırken birinci örnekte diskteki bir dosyadan profil yüklenir. Büyük girişlerin işlenmesiyle ilgili performansı doğru şekilde profillendirmek için büyük bir dosya yüklenir. Her iki örnek de yüklenmeyi tamamladığında, genellikle iz olarak adlandırılan performans profili oluşturma verileri, bir profili yükleyen perf panelinin ikinci Geliştirici Araçları örneğinde görülür.
İlk durum: İyileştirme fırsatlarını belirleme
Yükleme işlemi tamamlandıktan sonra, ikinci performans paneli örneğimizde aşağıdaki durumlar gözlemlendi. Main etiketli izde görünen ana iş parçacığının etkinliğine odaklanın. Alev grafiğinde beş büyük etkinlik grubu olduğu görülmektedir. Bunlar, yüklemenin en uzun sürdüğü görevlerden oluşur. Bu görevlerin toplam süresi yaklaşık 10 saniyeydi. Aşağıdaki ekran görüntüsünde, neler bulunabileceğini görmek için bu etkinlik gruplarının her birine odaklanmak üzere performans paneli kullanılmaktadır.

İlk etkinlik grubu: gereksiz çalışma
İlk etkinlik grubunun, hâlâ çalışan ancak gerçekten gerekli olmayan eski kod olduğu anlaşıldı. Temel olarak, processThreadEvents
etiketli yeşil blokun altındaki her şey boşuna harcanmış çabaydı. Bu, hızlı bir kazanımdı. Bu işlev çağrısının kaldırılmasıyla yaklaşık 1,5 saniye tasarruf edildi. Güzel!
İkinci etkinlik grubu
İkinci etkinlik grubunda çözüm, ilk gruptaki kadar basit değildi. buildProfileCalls
yaklaşık 0, 5 saniye sürdü ve bu görevden kaçınılamazdı.

Daha ayrıntılı incelemek için merakımızı gidermek amacıyla performans panelinde Bellek seçeneğini etkinleştirdik ve buildProfileCalls
etkinliğinin de çok fazla bellek kullandığını gördük. Burada, mavi çizgi grafiğin buildProfileCalls
çalıştırıldığı sırada aniden değiştiğini görebilirsiniz. Bu durum, olası bir bellek sızıntısına işaret eder.

Bu şüpheyi araştırmak için Bellek panelini (DevTools'daki başka bir panel, performans panelindeki Bellek çekmecesinden farklı) kullandık. Bellek panelinde, CPU profilini yükleyen performans paneli için yığın anlık görüntüsünü kaydeden "Allocation sampling" (Ayırma örnekleme) profilleme türü seçildi.

Aşağıdaki ekran görüntüsünde toplanan yığın anlık görüntüsü gösterilmektedir.

Bu yığın anlık görüntüsünden, Set
sınıfının çok fazla bellek tükettiği görülmektedir. Çağrı noktaları kontrol edildiğinde, büyük hacimlerde oluşturulan nesnelere gereksiz yere Set
türünde özellikler atadığımız tespit edildi. Bu maliyet artıyordu ve çok fazla bellek tüketiliyordu. Bu durum, uygulamanın büyük girişlerde kilitlenmesine neden oluyordu.
Kümeler, benzersiz öğeleri depolamak için kullanışlıdır ve içeriklerinin benzersizliğini kullanan işlemler (ör. veri kümelerinin yinelenen öğelerini kaldırma ve daha verimli aramalar sağlama) sunar. Ancak, depolanan verilerin kaynaktan benzersiz olduğu garanti edildiğinden bu özellikler gerekli değildi. Bu nedenle, ilk etapta setlere gerek yoktu. Bellek ayırmayı iyileştirmek için özellik türü Set
olan bir türden düz diziye değiştirildi. Bu değişiklik uygulandıktan sonra başka bir yığın anlık görüntüsü alındı ve bellek ayırma işleminin azaldığı görüldü. Bu değişiklikle önemli bir hız artışı elde edilmese de uygulamanın daha az sıklıkta kilitlenmesi ikincil bir avantaj oldu.

Üçüncü etkinlik grubu: Veri yapısı ile ilgili avantaj ve dezavantajları değerlendirme
Üçüncü bölüm ise farklıdır: Alev grafiğinde, dar ancak uzun sütunlardan oluştuğu görülür. Bu sütunlar, derin işlev çağrılarını ve bu durumda derin özyinelemeleri gösterir. Bu bölüm toplamda yaklaşık 1, 4 saniye sürdü. Bu bölümün en altına bakıldığında, bu sütunların genişliğinin bir işlevin süresine göre belirlendiği anlaşılıyordu: appendEventAtLevel
. Bu da söz konusu işlevin darboğaz olabileceğini gösteriyordu.
appendEventAtLevel
işlevinin uygulanmasında bir nokta dikkat çekiyordu. Girişteki her bir veri girişi için (kodda "etkinlik" olarak bilinir) zaman çizelgesi girişlerinin dikey konumunu izleyen bir haritaya öğe ekleniyordu. Bu durum, depolanan öğe sayısı çok fazla olduğundan sorun yaratıyordu. Haritalar, anahtar tabanlı aramalar için hızlıdır ancak bu avantaj ücretsiz değildir. Bir harita büyüdükçe, örneğin yeniden karma oluşturma nedeniyle haritaya veri eklemek pahalı olabilir. Bu maliyet, haritaya art arda çok sayıda öğe eklendiğinde fark edilir.
/**
* Adds an event to the flame chart data at a defined vertical level.
*/
function appendEventAtLevel (event, level) {
// ...
const index = data.length;
data.push(event);
this.indexForEventMap.set(event, index);
// ...
}
Alev grafiğindeki her giriş için haritaya öğe eklememizi gerektirmeyen başka bir yaklaşım denedik. Bu iyileştirme, darboğazın gerçekten de tüm verilerin haritaya eklenmesiyle oluşan ek yükle ilgili olduğunu doğrulayacak şekilde önemliydi. Etkinlik grubunun süresi yaklaşık 1,4 saniyeden yaklaşık 200 milisaniyeye düştü.
Önce:

Sonra:

Dördüncü etkinlik grubu: Yinelenen çalışmaları önlemek için kritik olmayan işleri erteleme ve verileri önbelleğe alma
Bu pencereye yakınlaştırdığımızda, neredeyse aynı olan iki işlev çağrısı bloğu olduğunu görebiliriz. Çağrılan işlevlerin adına bakarak bu blokların ağaçlar oluşturan kodlardan (ör. refreshTree
veya buildChildren
gibi adlarla) oluştuğunu anlayabilirsiniz. Aslında, ilgili kod, panelin alt çekmecesinde ağaç görünümlerini oluşturan koddur. İlginç olan, bu ağaç görünümlerinin yüklemeden hemen sonra gösterilmemesidir. Bunun yerine, ağaçların gösterilmesi için kullanıcının bir ağaç görünümü (çekmecedeki "Aşağıdan yukarıya", "Çağrı Ağacı" ve "Olay Günlüğü" sekmeleri) seçmesi gerekir. Ayrıca, ekran görüntüsünden de görebileceğiniz gibi, ağaç oluşturma işlemi iki kez yürütülmüştür.

Bu resimle ilgili olarak tespit ettiğimiz iki sorun var:
- Kritik olmayan bir görev, yükleme süresinin performansını engelliyordu. Kullanıcıların her zaman bu çıktılara ihtiyacı yoktur. Bu nedenle, görev profil yükleme açısından kritik değildir.
- Bu görevlerin sonucu önbelleğe alınmadı. Bu nedenle, veriler değişmemesine rağmen ağaçlar iki kez hesaplandı.
Ağaç hesaplamasını, kullanıcının ağaç görünümünü manuel olarak açtığı zamana erteleyerek başladık. Bu ağaçları oluşturmanın maliyetini karşılamak ancak bu şekilde değerlidir. Bu işlevi iki kez çalıştırmanın toplam süresi yaklaşık 3,4 saniye olduğundan ertelemek, yükleme süresinde önemli bir fark yarattı. Bu tür görevlerin de önbelleğe alınmasıyla ilgili araştırmalarımız devam ediyor.
Beşinci etkinlik grubu: Mümkün olduğunda karmaşık görüşme hiyerarşilerinden kaçının
Bu gruba yakından baktığımızda belirli bir arama zincirinin tekrar tekrar çağrıldığı açıkça görülüyordu. Aynı desen, alev grafiğinde farklı yerlerde 6 kez göründü ve bu pencerenin toplam süresi yaklaşık 2,4 saniyeydi.

Birden çok kez çağrılan ilgili kod, verileri işleyerek "mini haritada" (panelin üst kısmındaki zaman çizelgesi etkinliğine genel bakış) oluşturulmasını sağlayan kısımdır. Bu durumun neden birden fazla kez gerçekleştiği net değildi ancak kesinlikle 6 kez gerçekleşmesi gerekmiyordu. Aslında, başka bir profil yüklenmediği sürece kodun çıkışı güncel kalmalıdır. Teorik olarak kod yalnızca bir kez çalıştırılmalıdır.
Yapılan inceleme sonucunda, yükleme işlem hattındaki birden fazla bölümün, minimapi hesaplayan işlevi doğrudan veya dolaylı olarak çağırması nedeniyle ilgili kodun çağrıldığı tespit edildi. Bunun nedeni, programın çağrı grafiğinin karmaşıklığının zaman içinde artması ve bu koda farkında olmadan daha fazla bağımlılık eklenmesidir. Bu sorunun hızlı bir çözümü yoktur. Bu sorunu çözme yöntemi, söz konusu kod tabanının mimarisine bağlıdır. Bizim durumumuzda, çağrı hiyerarşisi karmaşıklığını biraz azaltmamız ve giriş verileri değişmeden kalırsa kodun yürütülmesini önlemek için bir kontrol eklememiz gerekti. Bu özelliği uyguladıktan sonra zaman çizelgesiyle ilgili şu görünümü elde ettik:

Minimap oluşturma işleminin bir kez değil, iki kez gerçekleştiğini unutmayın. Bunun nedeni, her profil için iki mini harita çizilmesidir: biri panelin üst kısmındaki genel bakış için, diğeri ise geçmişten şu anda görünür olan profili seçen açılır menü için (bu menüdeki her öğe, seçtiği profilin genel bakışını içerir). Ancak bu ikisi tamamen aynı içeriğe sahip olduğundan biri diğerinde yeniden kullanılabilir.
Bu mini haritaların her ikisi de tuval üzerine çizilmiş resimler olduğundan, drawImage
tuval yardımcı programını kullanmak ve ardından kodu yalnızca bir kez çalıştırarak biraz daha zaman kazanmak yeterli oldu. Bu çalışma sonucunda grubun süresi 2, 4 saniyeden 140 milisaniyeye düşürüldü.
Sonuç
Tüm bu düzeltmeler (ve birkaç küçük düzeltme) uygulandıktan sonra profil yükleme zaman çizelgesindeki değişiklik aşağıdaki gibi görünüyordu:
Önce:

Sonra:

İyileştirmelerden sonra yükleme süresi 2 saniye oldu. Yapılanların çoğu hızlı düzeltmelerden oluştuğu için nispeten az çabayla yaklaşık%80'lik bir iyileşme sağlandı. Elbette, başlangıçta ne yapılması gerektiğini doğru bir şekilde belirlemek çok önemliydi ve bu amaç için performans paneli doğru araçtı.
Bu sayıların, çalışma konusu olarak kullanılan bir profile özgü olduğunu da belirtmek önemlidir. Bu profil, özellikle büyük olduğu için ilgimizi çekti. Bununla birlikte, işleme hattı her profil için aynı olduğundan elde edilen önemli iyileştirme, performans panelinde yüklenen her profil için geçerlidir.
Çıkarımlar
Uygulamanızın performans optimizasyonu açısından bu sonuçlardan çıkarılacak bazı dersler vardır:
1. Çalışma zamanı performans kalıplarını belirlemek için profil oluşturma araçlarından yararlanma
Profillendirme araçları, uygulamanız çalışırken neler olduğunu anlamak ve özellikle performansı artırma fırsatlarını belirlemek için inanılmaz derecede yararlıdır. Chrome Geliştirici Araçları'ndaki Performans paneli, tarayıcıdaki yerel web profili oluşturma aracı olduğundan ve en yeni web platformu özellikleriyle güncel kalması için aktif olarak bakımı yapıldığından web uygulamaları için harika bir seçenektir. Ayrıca, artık çok daha hızlı! 😉
Temsili iş yükleri olarak kullanılabilecek örnekleri kullanın ve neler bulabileceğinize bakın.
2. Karmaşık görüşme hiyerarşilerinden kaçının
Mümkün olduğunda, çağrı grafiğinizi çok karmaşık hale getirmeyin. Karmaşık çağrı hiyerarşilerinde performans gerilemelerine neden olmak kolaydır ve kodunuzun neden bu şekilde çalıştığını anlamak zordur. Bu da iyileştirmeler yapmayı zorlaştırır.
3. Gereksiz işleri belirleme
Eskimiş kod tabanlarında artık ihtiyaç duyulmayan kodlar bulunması yaygın bir durumdur. Bizim durumumuzda, eski ve gereksiz kodlar toplam yükleme süresinin önemli bir bölümünü alıyordu. Bu özelliği kaldırmak en kolay çözümdü.
4. Veri yapılarını uygun şekilde kullanın
Performansı optimize etmek için veri yapılarını kullanın ancak hangi veri yapılarını kullanacağınıza karar verirken her bir veri yapısı türünün getirdiği maliyetleri ve ödünleri de göz önünde bulundurun. Bu, yalnızca veri yapısının kendisinin alan karmaşıklığı değil, aynı zamanda geçerli işlemlerin zaman karmaşıklığıdır.
5. Karmaşık veya tekrarlayan işlemler için yinelenen çalışmalardan kaçınmak üzere sonuçları önbelleğe alma
İşlemin yürütülmesi maliyetli ise sonuçlarını bir sonraki ihtiyaç için saklamak mantıklıdır. Bu işlem her seferinde çok maliyetli olmasa bile birçok kez yapılıyorsa bu yöntemi kullanmak mantıklıdır.
6. Kritik olmayan işleri erteleyin
Bir görevin çıktısına hemen ihtiyaç duyulmuyorsa ve görev yürütme, kritik yolu uzatıyorsa çıktısı gerçekten gerektiğinde tembelce çağırarak görevi ertelemeyi düşünebilirsiniz.
7. Büyük girişlerde verimli algoritmalar kullanma
Büyük girişler için optimum zaman karmaşıklığı algoritmaları çok önemlidir. Bu örnekte bu kategoriye bakmadık ancak önemini göz ardı etmek mümkün değildir.
8. Bonus: Ardışık düzenlerinizi karşılaştırmalı olarak değerlendirin
Gelişen kodunuzun hızlı kalmasını sağlamak için davranışı izlemeniz ve standartlarla karşılaştırmanız önerilir. Bu sayede, gerilemeleri proaktif bir şekilde belirleyip genel güvenilirliği artırarak uzun vadeli başarıya ulaşabilirsiniz.