Performansla% 400 daha hızlı bir performans paneli

Andrés Olivares
Andrés Olivares
Nancy Li
Nancy Li

Geliştirdiğiniz uygulama türü ne olursa olsun, performansını optimize etmek, hızlı yüklenmesini ve sorunsuz etkileşimler sunmasını sağlamak hem kullanıcı deneyimi hem de uygulamanın başarısı için çok önemlidir. Bunu yapmanın bir yolu, bir zaman aralığında çalışırken uygulamanın altında neler olduğunu görmek için profilleme araçlarını kullanarak 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 mükemmel bir profil oluşturma aracıdır. Uygulamanız Chrome'da çalışıyorsa uygulamanız 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ı iyileştirmek için harekete geçebileceğiniz kalıpları, darboğazları ve performans sorunlarını belirlemenize yardımcı olabilir.

Aşağıdaki örnekte, Performans panelinin kullanımı açıklanmaktadır.

Profil oluşturma senaryomuzu ayarlama ve yeniden oluşturma

Yakın zamanda Performans panelini daha performanslı hale getirmeyi hedefledik. Özellikle büyük hacimli performans verilerini daha hızlı yüklemesini istedik. Örneğin, uzun süren veya karmaşık süreçlerin profilini çıkarırken ya da yüksek ayrıntı düzeyinde verileri yakalarken bu durum söz konusudur. Bu hedefe ulaşmak için öncelikle uygulamanın nasıl ve neden bu şekilde performans gösterdiğini anlamak gerekiyordu. Bu da bir profil oluşturma aracı kullanılarak gerçekleştirildi.

Bildiğiniz gibi DevTools da bir web uygulamasıdır. Bu nedenle, Performans paneli kullanılarak profillenebilir. Bu panelin profilini oluşturmak için Geliştirici Araçları'nı ve ardından buna bağlı başka bir Geliştirici Araçları örneğini açabilirsiniz. Google'da bu kurulum DevTools üzerinde DevTools olarak bilinir.

Kurulum hazır olduğunda, profil oluşturulacak senaryonun yeniden oluşturulması ve kaydedilmesi gerekir. Karışıklığı önlemek için orijinal DevTools penceresine "ilk DevTools örneği", ilk örneği denetleyen pencereye ise "ikinci DevTools örneği" adı verilecektir.

Geliştirici Araçları'ndaki öğeleri inceleyen bir Geliştirici Araçları örneğinin ekran görüntüsü.
Geliştirici Araçları'nda Geliştirici Araçları: Geliştirici Araçları'nı Geliştirici Araçları ile inceleme.

İkinci DevTools örneğinde, Performans paneli (bundan sonra perf paneli olarak anılacaktır) bir profil yükleyen senaryoyu yeniden oluşturmak için ilk DevTools örneğini gözlemler.

İkinci DevTools örneğinde canlı kayıt başlatılırken ilk örnekte diskteki bir dosyadan profil yüklenir. Büyük girişlerin işlenme performansını doğru şekilde profillemek için büyük bir dosya yüklenir. Her iki örnek de yüklendiğinde, performans profilleme verileri (genellikle iz olarak adlandırılır) bir profil yükleyen performans panelinin ikinci DevTools örneğinde gösterilir.

İlk durum: İyileştirme fırsatlarını belirleme

Yükleme işlemi tamamlandıktan sonra, aşağıdaki ekran görüntüsünde ikinci performans paneli örneğimizde aşağıdaki durum gözlemlendi. Ana etiketli kanalın altında görünen ana mesaj dizisinin etkinliğine odaklanın. Alev grafiğinde beş büyük etkinlik grubu olduğu görülebilir. Bunlar, yüklemenin en uzun sürdüğü görevlerden oluşur. Bu görevlerin toplam süresi yaklaşık 10 saniye idi. Aşağıdaki ekran görüntüsünde, bu etkinlik gruplarının her birine odaklanarak neler bulunabileceğini görmek için performans paneli kullanılmaktadır.

Başka bir DevTools örneğinin performans panelinde bir performans izlemenin yüklenmesini inceleyen DevTools'daki performans panelinin ekran görüntüsü. Profilin yüklenmesi yaklaşık 10 saniye sürer. Bu süre çoğunlukla beş ana etkinlik grubuna ayrılır.

İlk etkinlik grubu: Gereksiz çalışma

İlk etkinlik grubunun, hâlâ çalışan ancak aslında gerekli olmayan eski kod olduğu anlaşıldı. Temel olarak, processThreadEvents etiketli yeşil bloğun altındaki her şey boşa çabaydı. Bu, hızlı bir kazançtı. Bu işlev çağrısının kaldırılmasıyla yaklaşık 1,5 saniye kazanıldı. 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ı.

Geliştirici Araçları'ndaki performans panelinin başka bir performans paneli örneğini inceleyen ekran görüntüsü. buildProfileCalls işleviyle ilişkili bir görev yaklaşık 0,5 saniye sürer.

Daha ayrıntılı incelemek için performans panelinde Bellek seçeneğini etkinleştirdik ve buildProfileCalls etkinliğinin de çok fazla bellek kullandığını gördük. Burada, buildProfileCalls çalıştırıldığı sırada mavi çizgi grafiğin aniden nasıl sıçradığını görebilirsiniz. Bu durum, olası bir bellek sızıntısı olduğunu gösterir.

Geliştirici Araçları'ndaki bellek profilleyicinin, performans panelinin bellek tüketimini değerlendirdiği ekran görüntüsü. Denetleyici, buildProfileCalls işlevinin bellek sızıntısından sorumlu olduğunu belirtir.

Bu şüpheyi gidermek için DevTools'taki Bellek panelini (performans panelindeki Bellek çekmecesinden farklı bir panel) kullanarak inceleme yaptık. Bellek panelinde, CPU profilini yükleyen performans paneli için yığın anlık görüntüsünü kaydeden "Ayrıntılandırma örnekleme" profilleme türü seçildi.

Bellek profilleyicinin ilk durumunun ekran görüntüsü. "Tahsis örnekleme" seçeneği kırmızı bir kutuyla vurgulanır ve bu seçeneğin JavaScript bellek profilleme için en iyi seçenek olduğunu gösterir.

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

Bellek yoğun bir küme tabanlı işlemin seçili olduğu bellek profilleyicinin ekran görüntüsü.

Bu yığın anlık görüntüsünde, Set sınıfının çok fazla bellek tükettiği gözlemlendi. Çağrı noktaları kontrol edildiğinde, büyük miktarlarda oluşturulan nesnelere gereksiz yere Set türündeki özellikler atadığımız tespit edildi. Bu maliyet artıyordu ve çok fazla bellek tüketiliyordu. Uygulamanın büyük girişlerde kilitlenmesi sık karşılaşılan bir durum haline gelmişti.

Kümeler, benzersiz öğeleri depolamak için kullanışlıdır ve içeriklerinin benzersizliğini kullanan işlemler (ör. veri kümelerini tekilleştirme ve daha verimli aramalar sağlama) sunar. Ancak depolanan verilerin kaynaktan benzersiz olduğu garanti edildiğinden bu özellikler gerekli değildi. Bu nedenle, başlangıçta setlere gerek yoktu. Bellek ayırmayı iyileştirmek için mülk türü Set yerine düz bir dizi olarak değiştirildi. Bu değişiklik uygulandıktan sonra başka bir yığın anlık görüntüsü alındı ve azaltılmış bellek ayırma işlemi gözlemlendi. Bu değişiklikle önemli bir hız artışı elde edemesek de ikincil bir avantaj olarak uygulamanın daha seyrek kilitlenmesini sağladık.

Bellek profilleyicinin ekran görüntüsü. Daha önce bellek yoğun olan Set tabanlı işlem, bellek maliyetini önemli ölçüde azaltan basit bir dizi kullanacak şekilde değiştirildi.

Üçüncü etkinlik grubu: Veri yapısıyla ilgili avantaj ve dezavantajları değerlendirme

Üçüncü bölüm tuhaf: Alev grafiğinde, bu bölümün dar ancak yüksek sütunlardan oluştuğunu görebilirsiniz. Bu sütunlar, derin işlev çağrılarını ve bu durumda derin yinelemeleri gösterir. Bu bölüm toplamda yaklaşık 1, 4 saniye sürdü. Bu bölümün alt kısmı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 bir darboğaz olabileceğini gösteriyordu.

appendEventAtLevel işlevinin uygulanmasında bir şey dikkatimizi çekti. Girişteki her bir veri girişi için (kodda "etkinlik" olarak bilinir) zaman çizelgesi girişlerinin dikey konumunu izleyen bir haritaya bir öğe eklendi. Depolanan öğelerin miktarı çok fazla olduğu için bu durum sorunluydu. Haritalar, anahtar tabanlı aramalar için hızlıdır ancak bu avantaj ücretsiz değildir. Harita büyüdükçe, örneğin yeniden karıştırma nedeniyle haritaya veri eklemek pahalı hale gelebilir. 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 önemli bir gelişmeydi ve darboğazın gerçekten de tüm verilerin haritaya eklenmesiyle ortaya çıkan ek maliyetle ilgili olduğunu doğruladı. Etkinlik grubunun sürdüğü süre yaklaşık 1,4 saniyeden yaklaşık 200 milisaniyeye düştü.

Önce:

appendEventAtLevel işlevinde optimizasyon yapılmadan önceki performans panelinin ekran görüntüsü. İşlevin çalışması için geçen toplam süre 1.372,51 milisaniyedir.

Sonra:

appendEventAtLevel işlevinde optimizasyonlar yapıldıktan sonra performans panelinin ekran görüntüsü. İşlevin çalışması için geçen toplam süre 207,2 milisaniyedir.

Dördüncü etkinlik grubu: Yinelenen çalışmaları önlemek için kritik olmayan çalışmaları erteleme ve verileri önbelleğe alma

Bu pencereyi yakınlaştırdığınızda neredeyse aynı iki işlev çağrısı bloğu olduğunu görebilirsiniz. Çağrılan işlevlerin adına bakarak bu blokların ağaç oluşturan koddan (örneğin, refreshTree veya buildChildren gibi adlara sahip) 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üklendikten hemen sonra gösterilmemesidir. Bunun yerine, kullanıcının ağaçların gösterilmesi için bir ağaç görünümü seçmesi gerekir ("Aşağıdan yukarıya", "Çağrı ağacı" ve "Olay günlüğü" sekmeleri). 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.

Gerekmediği halde yürütülen çeşitli tekrarlanan görevleri gösteren performans panelinin ekran görüntüsü. Bu görevler, önceden yerine getirilmek yerine gerektiğinde yerine getirilecek şekilde ertelenebilir.

Bu resimde tespit ettiğimiz iki sorun var:

  1. Kritik olmayan bir görev, yükleme süresinin performansını engelliyordu. Kullanıcıların her zaman bu işlevin çıktısına ihtiyacı yoktur. Bu nedenle, görev profilin yüklenmesi için kritik değildir.
  2. Bu görevlerin sonucu önbelleğe alınmadı. Bu nedenle, veriler değişmemesine rağmen ağaçlar iki kez hesaplandı.

Ağ hesaplamasını, kullanıcı ağaç görünümünü manuel olarak açtığında ertelemeye başladık. Ancak bu durumda bu ağaçları oluşturmanın bedelini ödemeye değer. Bu işlemi iki kez çalıştırmanın toplam süresi yaklaşık 3,4 saniyeydi.Bu nedenle, işlemi ertelemek yükleme süresinde önemli bir fark yarattı. Bu tür görevlerin önbelleğe alınmasını da araştırıyoruz.

Beşinci etkinlik grubu: Mümkün olduğunda karmaşık arama hiyerarşilerinden kaçının

Bu gruba yakından baktığımızda, belirli bir çağrı 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.

Aynı izleme mini haritasını oluşturmak için altı ayrı işlev çağrısını gösteren performans panelinin ekran görüntüsü. Her işlev çağrısının derin çağrı yığınları vardır.

Birden çok kez çağrılan ilgili kod, "mini haritada" (panelin üst kısmındaki zaman çizelgesi etkinliğine genel bakış) oluşturulacak verileri işleyen kısımdır. Bu durumun neden birden fazla kez yaşandığı net değildi ancak 6 kez yaşanması kesinlikle gerekmiyordu. Aslında, başka bir profil yüklenmezse kodun çıkışı güncel kalır. Teorik olarak, kod yalnızca bir kez çalışmalıdır.

İnceleme sonucunda, ilgili kodun, yükleme ardışık düzenindeki birden fazla parçanın mini haritayı hesaplayan işlevi doğrudan veya dolaylı olarak çağırması sonucunda çağrıldığı tespit edildi. Bunun nedeni, programın çağrı grafiğinin karmaşıklığının zaman içinde gelişmesi ve bu koda bilmeden daha fazla bağımlılık eklenmiş olmasıdır. 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şisinin karmaşıklığını biraz azaltmamız ve giriş verileri değişmeden kalırsa kodun yürütülmesini engelleyecek bir kontrol eklememiz gerekiyordu. Bunu uyguladıktan sonra zaman çizelgesinin görünümü şu şekilde oldu:

Aynı izleme mini haritasını oluşturmak için altı ayrı işlev çağrısını yalnızca iki kez azaltılmış şekilde gösteren performans panelinin ekran görüntüsü.

Küçük harita oluşturma işleminin bir kez değil, iki kez gerçekleştiğini unutmayın. Bunun nedeni, her profil için iki küçük harita çizilmesidir: biri panelin üst kısmındaki genel bakış için, diğeri ise geçmişten seçilen profili gösteren açılır menü için (bu menüdeki her öğe, seçtiği profile genel bir bakış içerir). Bununla birlikte, bu iki öğenin içeriği tamamen aynı olduğundan biri diğeri için yeniden kullanılabilir.

Bu mini haritalar bir kanvas üzerine çizilmiş resimler olduğundan, drawImage kanvas yardımcı programını kullanmak ve ardından biraz zaman kazanmak için kodu yalnızca bir kez çalıştırmak gerekiyordu. 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 daha) uygulandıktan sonra, profil yükleme zaman çizelgesinde yapılan değişiklik aşağıdaki gibi görünüyordu:

Önce:

Optimizasyonlardan önce yükleme izlemeyi gösteren performans panelinin ekran görüntüsü. İşlem yaklaşık on saniye sürdü.

Sonra:

Optimizasyonlardan sonra izleme yüklemesini gösteren performans panelinin ekran görüntüsü. Bu işlem artık yaklaşık iki saniye sürüyor.

İyileştirmelerden sonra yüklenme süresi 2 saniye oldu. Yani yapılanların çoğu hızlı düzeltmelerden oluştuğu için nispeten az çabayla yaklaşık%80 oranında bir iyileşme elde edildi. Elbette, başlangıçta ne yapılacağını doğru şekilde belirlemek önemliydi ve performans paneli bunun için doğru araçtı.

Bu sayıların, çalışma konusu olarak kullanılan bir profile özel olduğunu da belirtmek önemlidir. Profil, özellikle büyük olduğu için bizim için ilgi çekiciydi. Bununla birlikte, işleme ardışık düzeni her profil için aynı olduğundan, elde edilen önemli iyileştirme, performans paneline yüklenen her profil için geçerlidir.

Çıkarımlar

Uygulamanızın performans optimizasyonu açısından bu sonuçlardan çıkarılabilecek bazı dersler vardır:

1. Çalışma zamanı performans kalıplarını belirlemek için profil oluşturma araçlarını kullanın

Profil oluşturma araçları, uygulamanız çalışırken neler olduğunu anlamak ve özellikle performansı artırma fırsatlarını belirlemek için son derece yararlıdır. Chrome DevTools'daki Performans paneli, tarayıcıdaki yerel web profilleme aracı olduğu ve en son web platformu özellikleriyle güncel kalacak şekilde etkin bir şekilde yönetildiği için web uygulamaları için mükemmel bir seçenektir. Ayrıca, artık çok daha hızlı. 😉

Temsil edici iş yükleri olarak kullanılabilecek örnekleri kullanın ve neler bulabileceğinize bakın.

2. Karmaşık çağrı hiyerarşileri oluşturmaktan kaçının

Mümkün olduğunda çağrı grafiğinizi çok karmaşık hale getirmekten kaçının. Karmaşık çağrı hiyerarşileri, performans gerilemelerine yol açabilir ve kodunuzun neden bu şekilde çalıştığını anlamanızı zorlaştırarak iyileştirme yapmanızı engelleyebilir.

3. Gereksiz işleri belirleme

Eski kod tabanlarında artık ihtiyaç duyulmayan kodların bulunması yaygın bir durumdur. Bizim durumumuzda, eski ve gereksiz kod toplam yükleme süresinin önemli bir bölümünü alıyordu. Bu sorunun çözümü kolaydı.

4. Veri yapılarını uygun şekilde kullanın

Performansı optimize etmek için veri yapılarını kullanın ancak hangilerini kullanacağınıza karar verirken her veri yapısı türünün getirdiği maliyetleri ve avantajları da anlayın. Bu, yalnızca veri yapısının alan karmaşıklığı değil, aynı zamanda geçerli işlemlerin zaman karmaşıklığıdır.

5. Karmaşık veya tekrar eden işlemler için yinelenen çalışmalardan kaçınmak amacıyla sonuçları önbelleğe alma

İşlemin yürütülmesi maliyetliyse sonuçlarının bir sonraki sefere ihtiyaç duyulduğunda saklanması mantıklı olur. İşlem çok kez yapılıyorsa (her işlem özellikle maliyetli olmasa bile) bunu yapmak da mantıklıdır.

6. Kritik olmayan işleri erteleme

Bir görevin sonucuna hemen ihtiyaç yoksa ve görev yürütme kritik yolu uzatıyorsa sonucuna gerçekten ihtiyaç duyulduğunda tembel olarak çağırarak görevi erteleyin.

7. Büyük girişlerde verimli algoritmalar kullanın

Büyük girişler için optimum zaman karmaşıklığı algoritmaları çok önemlidir. Bu örnekte bu kategoriye bakmadık ancak bu kategorinin önemini vurgulamak isteriz.

8. Bonus: Ardışık düzenlerinizi karşılaştırma

Gelişmekte olan kodunuzun hızlı kalmasını sağlamak için davranışı izlemek ve standartlarla karşılaştırmak akıllıca bir harekettir. Bu sayede, gerileme noktalarını proaktif olarak tespit edebilir ve genel güvenilirliği artırarak uzun vadeli başarı için kendinizi hazırlayabilirsiniz.