Performansla% 400 daha hızlı bir performans paneli

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

Geliştirmekte olduğunuz uygulama türü ne olursa olsun, uygulamanın performansını optimize etmek, hızlı yüklenmesini sağlamak ve sorunsuz etkileşimler sunmak, kullanıcı deneyimi ve uygulamanın başarısı için kritik öneme sahiptir. Bunu yapmanın bir yolu, bir uygulama zaman aralığında çalışırken arka planda neler olup bittiğini görmek için profil oluşturma araçlarını kullanarak uygulama etkinliğini incelemektir. Geliştirici Araçları'ndaki Performans paneli, web uygulamalarının performansını analiz ve optimize etmek için ideal bir profil oluşturma aracıdır. Uygulamanız Chrome'da çalışıyorsa, uygulamanız çalıştırılırken tarayıcının ne yaptığına ilişkin ayrıntılı bir görsel genel bakış sunar. Bu etkinliği anlamak, performansı artırmak için uygulayabileceğiniz kalıpları, performans sorunlarını ve performans noktalarını belirlemenize yardımcı olabilir.

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

Profil oluşturma senaryomuzu oluşturma ve yeniden oluşturma

Kısa bir süre önce, Performans panelinin daha iyi performans göstermesini sağlamak için bir hedef belirledik. Özellikle, büyük hacimli performans verilerini daha hızlı yüklemesini istedik. Örneğin, uzun süreli veya karmaşık işlemlerin profilini çıkarırken ya da yüksek ayrıntı düzeyine sahip verileri yakalarken böyle bir durum söz konusudur. Bunun için uygulamanın nasıl performans gösterdiğini ve bu şekilde neden performans gösterdiğini anlamak gerekiyordu. Bu da bir profil oluşturma aracı kullanılarak gerçekleştirildi.

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

Kurulum hazır olduğunda, profili oluşturulacak senaryo yeniden oluşturulmalı ve kaydedilmelidir. 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.

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

İkinci DevTools örneğinde, bundan sonra performans paneli olarak adlandırılacak olan Performans paneli, ilk DevTools örneğini gözlemleyerek bir profil yükleyen senaryoyu yeniden oluşturur.

İkinci Geliştirici Araçları örneğinde canlı kayıt başlatılırken ilk örnekte ise diskteki bir dosyadan bir profil yüklenir. Büyük girişlerin işlenmesinin performansını doğru bir şekilde belirlemek için büyük bir dosya yüklenir. Her iki örneğin de yüklenmesi bittiğinde, genellikle trace adı verilen performans profili oluşturma verileri, bir profil yükleyen perf panelinin ikinci Geliştirici Araçları örneğinde görünür.

Başlangıçtaki durum: iyileştirme fırsatlarını belirleme

Yükleme tamamlandıktan sonra, bir sonraki ekran görüntüsünde ikinci perf paneli örneğimizde aşağıdakiler gözlemlendi. Ana etiketli parçanın altında görünen ana ileti dizisinin etkinliğine odaklanın. Flame grafiğinde beş büyük etkinlik grubu olduğu görülüyor. Bunlar, yüklemenin en çok zaman aldığı görevlerden oluşur. Bu görevlerin toplam süresi yaklaşık 10 saniyeydi. Aşağıdaki ekran görüntüsünde performans paneli, nelerin bulunabileceğini görmek amacıyla bu etkinlik gruplarının her birine odaklanmak için kullanılmıştır.

Geliştirici Araçları'nda, başka bir Geliştirici Araçları örneğinin performans panelindeki bir performans izlemenin yüklenmesini inceleyen 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 bölünür.

İlk etkinlik grubu: gereksiz iş

İlk etkinlik grubunun hâlâ çalışan, ancak aslında gerekli olmayan eski kod olduğu anlaşıldı. Özetle, processThreadEvents etiketli yeşil blokun altındaki her şey boşa çaba harcanırdı. Bu sonuç hemen geldi. İşlev çağrısının kaldırılması yaklaşık 1,5 saniye tasarruf sağladı. Güzel!

İkinci etkinlik grubu

İkinci etkinlik grubunda çözüm ilki kadar basit değildi. buildProfileCalls işlemi yaklaşık 0, 5 saniye sürdü ve bu işlemden kaçınılabilecek bir durum değildi.

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

Daha ayrıntılı araştırma yapmak için merak nedeniyle perf 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ılırken mavi çizgi grafiğin aniden nasıl değiştiğini görebilirsiniz. Bu, olası bir bellek sızıntısına işaret eder.

Geliştirici Araçları'nda, performans panelinin bellek tüketimini değerlendiren bellek profil aracını gösteren ekran görüntüsü. İnceleyici, buildProfileCalls işlevinin bellek sızıntısından sorumlu olduğunu önerir.

Bu şüpheyle ilgili araştırma yapmak için Bellek panelini (DevTools'daki başka bir panel ve perf panelindeki Bellek çekmecesinden farklı) kullandık. Bellek panelinde, CPU profilini yükleyen perf panelinin yığın anlık görüntüsünü kaydeden "Ayırma örneklemesi" profil oluşturma türü seçildi.

Bellek profili oluşturucunun ilk durumunun ekran görüntüsü. "Ayırma örnekleme" seçeneği, kırmızı bir kutuyla vurgulanmıştır ve bu seçeneğin JavaScript bellek profili oluşturmak için en iyi seçenek olduğunu belirtir.

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

Belleği yoğun olarak kullanan Set tabanlı işlemin seçili olduğu bellek profil oluşturucusunun 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şturulan nesnelere gereksiz yere Set türünde özellikler atadığımız tespit edildi. Bu maliyetler artmakta ve uygulamanın büyük girişlerde kilitlenmesi yaygın görülen bir noktaya kadar büyük oranda bellek harcanıyordu.

Kümeler, benzersiz öğeler depolamak ve veri kümelerinin tekilleştirilmesi ve daha etkili aramalar sağlamak gibi yöntemlerle içeriklerinin benzersizliğini kullanan işlemler sunmak için yararlıdır. Ancak depolanan verilerin kaynaktan benzersiz olacağı garanti edildiği için bu özellikler gerekli değildi. Bu nedenle, en başta setler gerekli değildi. Bellek ayırmayı iyileştirmek için Set olan özellik türü, düz 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 bellek tahsisinin azaldığı gözlemlendi. Bu değişiklik sayesinde hızda önemli düzeyde iyileşme sağlanamasa da uygulamanın faydası, uygulamanın daha seyrek kilitlenmesiydi.

Hafıza profil aracını gösteren ekran görüntüsü. Daha önce yoğun bellek kullanan Set tabanlı işlem, bellek maliyetini önemli ölçüde azaltan bir düz dizi kullanacak şekilde değiştirildi.

Üçüncü etkinlik grubu: veri yapısında ters ibrazların değerlendirilmesi

Üçüncü bölüm kendine özgüdür: flame grafiğinde, bu bölümün derin işlev çağrılarını ve bu örnekte derin yinelemeleri ifade eden dar ama uzun sütunlardan oluştuğunu görebilirsiniz. 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şılmaktadır: appendEventAtLevel, bu da bunun bir performans sorunu olabileceğini gösteriyordu

appendEventAtLevel işlevinin uygulanmasında bir şey öne çıktı. Girişteki (kodda "etkinlik" olarak bilinir) her bir veri girişi için, zaman çizelgesi girişlerinin dikey konumunu izleyen bir haritaya bir öğe eklenmiştir. Depolanan öğe miktarı çok fazla olduğundan bu durum sorunluydu. Haritalar, temel tabanlı aramalar için hızlı olsa da bu avantaj ücretsiz değildir. Örneğin, bir harita büyüdükçe, yeniden karma oluşturma işlemi nedeniyle haritaya veri eklemek pahalı hale gelebilir. Bu maliyet, haritaya arka arkaya büyük miktarlarda öğe eklendiğinde belirgin hale gelir.

/**
 * 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 bir öğe eklememizi gerektirmeyen başka bir yaklaşım denedik. Önemli düzeyde iyileştirme yapıldı. Bu durum, performans sorununun gerçekten de tüm verilerin haritaya eklenmesinden kaynaklanan ek yük ile ilgili olduğunu kanıtladı. Etkinlik grubunun küçülme süresi yaklaşık 1,4 saniyeden yaklaşık 200 milisaniyeye çıkar.

Önce:

addEventAtLevel işlevinde optimizasyonlar yapılmadan önceki performans panelinin ekran görüntüsü. İşlevin çalışması için toplam süre 1.372,51 milisaniyeydi.

Sonra:

additionalEventAtLevel işlevinde optimizasyonlar yapıldıktan sonraki performans panelinin ekran görüntüsü. İşlevin çalışması için toplam süre 207,2 milisaniyeydi.

Dördüncü etkinlik grubu: Yinelenen çalışmaları önlemek için kritik olmayan işlerin ve önbellek verilerinin ertelenmesi

Bu pencereyi yakınlaştırdığınızda, neredeyse aynı iki işlev çağrısı bloğu olduğu görülebilir. Adlandırılan işlevlerin adına bakarak, bu blokların ağaç oluşturan koddan (örneğin, refreshTree veya buildChildren gibi adlarla) oluştuğu çıkarımında bulunabilirsiniz. Aslında, ilgili kod panelin alt çekmecesinde ağaç görünümlerini oluşturan koddur. Buradaki ilginç olan, bu ağaç görünümlerinin yüklendikten 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ü ("Aşağıdan yukarıya", "Çağrı Ağacı" ve "Etkinlik Günlüğü" sekmeleri) seçmesi gerekir. Ayrıca, ekran görüntüsünden de anlayabileceğiniz gibi ağaç oluşturma işlemi iki kez yürütülmüştür.

İhtiyaç duyulmasa bile gerçekleştirilen çeşitli tekrar eden görevleri gösteren performans panelinin ekran görüntüsü. Bu görevler, zamanında değil, istek üzerine yürütülecek ş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 çıktılarına ihtiyaç duymazlar. Bu nedenle, görev, profilin yüklenmesi açısından 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ğaç hesaplamasını, kullanıcının ağaç görünümünü manuel olarak açtığı zamana erteledik. Ancak bu durumda bu ağaçların yapılması için gereken ücreti ödemeye değer. Bu aracın iki kez çalıştırıldığı toplam süre yaklaşık 3,4 saniyeydi.Bu nedenle, aracın ertelenmesi, yükleme süresinde önemli bir fark yarattı. Bu tür görevlerin önbelleğe alınmasıyla ilgili çalışmalarımız devam ediyor.

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

Bu grup ayrıntılı olarak incelendiğinde, belirli bir çağrı zincirinin tekrar tekrar çağrıldığı açıkça anlaşılmıştır. Aynı kalıp, alev grafiğinin farklı yerlerinde 6 kez göründü ve bu aralığın toplam süresi yaklaşık 2,4 saniye oldu.

Her biri derin çağrı yığınlarına sahip olan aynı iz mini haritasını oluşturmak için altı ayrı işlev çağrısını gösteren performans panelinin ekran görüntüsü.

Birden çok kez çağrılan ilgili kod, "mini harita"da (panelin üst kısmındaki zaman çizelgesi etkinliğine genel bakış) oluşturulacak verileri işleyen kısımdır. Neden birden fazla kez tekrarlandığını anlayamadım ancak en azından 6 kez olması gerekmiyordu. Aslında, başka bir profil yüklenmezse kod çıkışı güncel kalmalıdır. Teoride, kod yalnızca bir kez çalıştırılmalıdır.

Yapılan incelemede, yükleme hattındaki birden fazla parçanın doğrudan veya dolaylı olarak mini haritayı hesaplayan işlevi çağırması sonucunda, ilgili kodun çağrıldığı tespit edildi. Bunun nedeni, programın çağrı grafiğinin karmaşıklığının zamanla gelişmiş ve farkında olmadan bu koda daha fazla bağımlılık eklenmesidir. Bu sorunun hızlı bir çözümü yoktur. Çözümün yolu, ilgili kod tabanının mimarisine bağlıdır. Örneğimizde, çağrı hiyerarşisi karmaşıklığını biraz azaltmamız ve giriş verileri değişmeden kaldığı takdirde kodun yürütülmesini önlemek için bir denetim eklememiz gerekti. Bunu uyguladıktan sonra, zaman çizelgesini şu şekilde özetledik:

Aynı iz mini haritasını oluşturmak için altı ayrı işlev çağrısını gösteren performans panelinin ekran görüntüsü, yalnızca iki kata indi.

Mini harita oluşturma işleminin bir kez değil, iki kez gerçekleştirildiğini unutmayın. Bunun nedeni, her profil için çizilen iki mini harita olmasıdır: Biri panelin üst kısmındaki genel bakış için, diğeri ise o anda görünür olan profili geçmişten seçen açılır menü içindir (bu menüdeki her öğe, seçtiği profile ilişkin bir genel bakış içerir). Bununla birlikte, bu ikisi tamamen aynı içeriğe sahip olduğundan biri diğeri için yeniden kullanılabilmelidir.

Bu mini haritaların her ikisi de tuval üzerine çizilmiş resimler olduğundan, fazladan zaman kazanmak için drawImage tuval yardımcı programını kullanmak ve ardından kodu yalnızca bir kez çalıştırmak gerekti. Bu çabanın bir sonucu olarak, grubun süresi 2,4 saniyeden 140 milisaniyeye düşürüldü.

Sonuç

Tüm bu düzeltmelerin (ve başka birkaç küçük düzeltme) uygulanmasının ardından, profil yükleme zaman çizelgesindeki değişiklik aşağıdaki gibi oldu:

Önce:

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

Sonra:

Optimizasyonlardan sonra iz yüklemesini gösteren performans panelinin ekran görüntüsü. İşlem şu anda yaklaşık iki saniye sürmektedir.

İyileştirmelerden sonraki yükleme süresi 2 saniyeydi. İşlemlerin çoğu hızlı düzeltmelerden oluştuğu için nispeten az çabayla yaklaşık%80 iyileştirme gerçekleştirildi. Elbette başlangıçta nenin yapılması gerektiğini doğru şekilde tanımlamak çok önemliydi ve performans paneli bunun için doğru araçtı.

Bu sayıların, çalışmanın konusu olarak kullanılan bir profile özgü olduğunu vurgulamak da önemlidir. Profil, oldukça 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şme, performans panelinde yüklenen her profil için geçerlidir.

Çalmalar

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 faydalanın

Profil oluşturma araçları, uygulamanız çalışırken uygulamanızda neler olduğunu anlamak, özellikle de performansı artırma fırsatlarını belirlemek açısından çok yararlıdır. Chrome Geliştirici Araçları'ndaki Performans paneli, tarayıcının yerel web profili oluşturma aracı olduğundan ve en son web platformu özellikleriyle güncel kalmak üzere etkin bir şekilde korunduğundan web uygulamaları için mükemmel bir seçenektir. Ayrıca, artık önemli ölçüde daha hızlı! 😉

Temsili iş yükleri olarak kullanılabilecek örnekler kullanın ve neler bulabileceğinizi görün.

2. Karmaşık çağrı hiyerarşilerinden kaçınma

Mümkün olduğu durumlarda çağrı grafiğinizi çok karmaşık hale getirmekten kaçının. Karmaşık çağrı hiyerarşileri ile performans regresyonlarını eklemek kolaydır ve kodunuzun neden bu şekilde çalıştığını anlamak zordur. Bu da geliştirmelerin yapılmasını zorlaştırır.

3. Gereksiz çalışmaları belirleme

Eskiyen kod tabanlarının artık gerekli olmayan kodları içermesi yaygın bir durumdur. Örneğimizde, eski ve gereksiz kodlar toplam yükleme süresinin önemli bir kısmını alıyordu. Bunu kaldırmak, en kolay hedefti.

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

Performansı optimize etmek için veri yapılarını kullanın. Ayrıca, hangilerini kullanacağınıza karar verirken her bir veri yapısı türünün getirdiği maliyetleri ve dengeleri de anlayın. Bu durum yalnızca veri yapısının mekandaki karmaşıklığı değil, aynı zamanda uygulanabilir işlemlerin zaman karmaşıklığı da değildir.

5. Karmaşık veya tekrarlanan işlemlerde yinelenen çalışmaları önlemek için sonuçları önbelleğe alın

İşlemin yürütülmesi maliyetliyse sonuçları bir dahaki sefere ihtiyaç duyulması için depolamak mantıklıdır. İşlem birçok kez yapılıyorsa, tek tek işlem çok maliyetli olmasa bile bunu yapmak da 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 işlemi kritik yolu genişletiyorsa, çıktısı gerçekten gerekli olduğunda geç çağrısı yaparak görevi ertelemeyi düşünebilirsiniz.

7. Büyük girişler için verimli algoritmalar kullanın

Büyük girişler için optimum zaman karmaşıklığı algoritmaları kritik hale gelir. Bu örnekte bu kategoriyi ele almadık. Ancak, önemi neredeyse hafife alınabilir.

8. Bonus: Satış kanallarınızı karşılaştırın

Gelişen kodunuzun hızlı kalmasını sağlamak için davranışı izlemek ve standartlarla karşılaştırmak akıllıca olur. Bu sayede, regresyonları proaktif olarak tespit edip genel güvenilirliği artırır ve uzun vadeli başarı elde edersiniz.