Performansla% 400 daha hızlı bir performans paneli

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

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 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ı artırmak için harekete geçebileceğiniz kalıpları, darboğazları ve performans sorunlarını belirlemenize yardımcı olabilir.

Aşağıdaki örnekte, Performans panelini kullanma konusunda size yol gösterilir.

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. Bunu başarmak için öncelikle uygulamanın nasıl performans gösterdiği ve neden bu şekilde performans gösterdiğinin anlaşılması gerekiyordu. Bu da bir profil oluşturma aracı kullanılarak sağlandı.

Bildiğiniz gibi Geliştirici Araçları 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 Geliştirici Araçları penceresi, "ilk Geliştirici Araçları örneği", ilk örneği inceleyen pencere ise "ikinci Geliştirici Araçları örneği" olarak adlandırılır.

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.

Başlangıç durumu: İ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 çok zaman aldığı görevlerden oluşur. Bu görevlerin toplam süresi yaklaşık 10 saniye idi. Aşağıdaki ekran görüntüsünde, performans paneli kullanılarak bu etkinlik gruplarının her birine odaklanıp neler bulunabileceğini görebilirsiniz.

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 saniyelik bir zaman tasarrufu sağlandı. 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.

Merak ettiğimizden, daha ayrıntılı bir araştırma yapmak için performans panelindeki 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 şüphenin devamı için araştırma yaparken Bellek panelini (Geliştirici Araçları'nda bulunan ve performans panelindeki Bellek çekmecesinden farklı başka bir panel) kullandı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 kullandığı gözlemlendi. Çağrı noktalarını kontrol ettiğimizde, büyük hacimlerde oluşturulmuş nesnelere gereksiz yere Set türü özellikler atadığımızı tespit ettik. Bu maliyetler artıyor ve çok fazla bellek tüketiliyordu. Bu da uygulamanın büyük girişlerde kilitlenmesi yaygın bir durumdur.

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 olması 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 göze çarpan bir nokta vardı. Girişteki (kodda "etkinlik" olarak bilinir) her bir veri girişi için zaman çizelgesi girişlerinin dikey konumunu izleyen haritaya bir öğe eklenmiştir. 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:

InsertEventAtLevel işlevine optimizasyonlar 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ırmaya devam ediyoruz.

Beşinci etkinlik grubu: Mümkün olduğunda karmaşık çağrı 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" oluşturulacak verileri işleyen kısımdır (panelin üst kısmındaki zaman çizelgesi etkinliğine genel bakış). 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 doğrudan veya dolaylı olarak mini haritayı hesaplayan işlevi çağırması sonucunda çağrıldığı tespit edildi. Bunun nedeni, programın çağrı grafiğinin zamanla karmaşıklığının zamanla değişmesi ve farkında olmadan bu koda daha fazla bağımlılığın eklenmesidir. Bu sorun için hızlı bir çözüm yoktur. Bu sorunun çözümü, söz konusu kod tabanının mimarisine bağlıdır. Bizim örneğimizde, ç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 denetim 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 tarihten 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). Yine de 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üzeltmeleri (ve başka birkaç küçük düzeltmeyi) uyguladıktan sonra, profil yükleme zaman çizelgesindeki değişiklik aşağıdaki gibi göründü:

Ö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ü. İşlem şu anda 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 çok büyük olduğu için bizim için ilginçti. 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 Geliştirici Araçları'ndaki Performans paneli, tarayıcıdaki yerel web profili oluşturma aracı olduğundan ve en son web platformu özelliklerini yansıtacak şekilde etkin bir şekilde korunduğundan 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şilerinden 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 gerilemelerini kolayca ortaya çıkarır ve kodunuzun neden bu şekilde çalıştığını anlamanızı zorlaştırır. Bu da iyileştirme yapmayı zorlaştırır.

3. Gereksiz işleri belirleme

Eskiyen kod tabanlarının artık ihtiyaç duyulmayan kod içermesi 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 söz konusu kategoriyi ele almadık, ancak önemleri hafife alınamaz.

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

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ı elde edebilirsiniz.