Osvědčené postupy pro zlepšení výkonu spouštění vaší WinUI aplikace

Vytvářejte aplikace WinUI pomocí sady Windows App SDK, které se spouštějí rychle díky snížení činností při spuštění, zjednodušení prvního vykreslení a načítání nekritických funkcí až poté, co je okno interaktivní.

Osvědčené postupy pro výkon spouštění vaší aplikace

Uživatelé částečně vnímají, jestli je vaše aplikace rychlá nebo pomalá na základě toho, jak dlouho trvá spuštění. Pro účely tohoto tématu začíná čas spuštění aplikace, když uživatel spustí aplikaci a skončí, když uživatel může s aplikací pracovat smysluplným způsobem. Tento článek obsahuje návrhy, jak z aplikace WinUI dosáhnout lepšího výkonu při spuštění.

Měření doby spuštění aplikace

Než skutečně změříte čas spuštění aplikace, nezapomeňte aplikaci spustit několikrát. Tím získáte směrný plán pro měření a zajistíte, že budete měřit co možná nejkratší dobu spuštění.

Proveďte měření, která jsou reprezentativní pro koncového uživatele. Měření vydání verzí vychází z reprezentativního hardwaru, zaměřte se na studené i teplé spuštění a zaměřte se na čas k prvnímu interaktivnímu snímku, namísto pouze doby, kdy proces existuje.

Odložit práci co nejdéle

Pokud chcete zlepšit čas spuštění aplikace, udělejte jenom práci, kterou je potřeba udělat, aby uživatel s aplikací začal pracovat. To může být zvlášť užitečné, pokud můžete zpozdit načítání dalších sestavení. Modul CLR (Common Language Runtime) načte sestavení při prvním použití. Pokud můžete minimalizovat počet načtených sestavení, můžete zlepšit dobu spuštění aplikace a její spotřebu paměti.

Nezávislé provádění dlouhotrvající práce

Vaše aplikace může být interaktivní, i když existují části aplikace, které nejsou plně funkční. Pokud například vaše aplikace zobrazuje data, která nějakou dobu trvá načtení, můžete tento kód provést nezávisle na spouštěcím kódu aplikace načtením dat asynchronně. Jakmile jsou data dostupná, naplňte uživatelské rozhraní aplikace daty.

Mnoho rozhraní API, která načítají data, jsou asynchronní, takže pravděpodobně budete data načítat asynchronně i tak. Další informace najdete v tématu Asynchronní programování pomocí async a await. Pokud pracujete tak, že nepoužíváte asynchronní rozhraní API, můžete pomocí Task třídy provádět dlouho běžící práci, abyste uživateli nezablokovali interakci s aplikací. Aplikace tak bude reagovat, když se data načítají.

Pokud načtení části jeho uživatelského rozhraní trvá zvlášť dlouho, zvažte zobrazení zprávy v této oblasti, jako je získání nejnovějších dat, aby uživatelé věděli, že aplikace stále zpracovává.

Minimalizovat čas spuštění

Všechny aplikace kromě těch nejjednodušších vyžadují určitelný čas na načtení prostředků, parsování XAML, vytvoření datových struktur a spuštění logiky během spouštění. U aplikací WinUI je vhodné se zamyslet nad spuštěním ve čtyřech fázích: spuštění procesu, vytvoření okna, vytvoření hlavní stránky a rozložení/vykreslení pro první snímek.

Spouštěcí období je doba mezi okamžikem, kdy uživatel spustí aplikaci, a okamžikem, kdy se aplikace stane funkční. Jedná se o kritickou dobu, protože se jedná o první dojem uživatele vaší aplikace. Uživatelé očekávají okamžitou a nepřetržitou zpětnou vazbu ze systému a z aplikací. Systém a aplikace se považují za nefunkční nebo špatně navržené, když se aplikace nespustí rychle.

Úvod do fází spuštění

Startup zahrnuje řadu pohyblivých částí a všechny je potřeba koordinovat pro co nejlepší uživatelské prostředí. Mezi spuštěním vaší aplikace uživatelem a zobrazením obsahu aplikace dochází k následujícím krokům.

  • Proces se spustí a spouštěcí kód generovaný šablonou volá Main.
  • Objekt Application se vytvoří.
    • Konstruktor aplikace volá InitializeComponent, což způsobí parsování App.xaml a vytvoření objektů.
  • Application.OnLaunched je vyvolán .
    • Kód aplikace vytvoří hlavní okno, přiřadí počáteční obsah a zavolá Activate.
    • Konstruktor hlavní stránky volá InitializeComponent, což způsobí parsování XAML stránky a vytvoření objektů.
  • Framework XAML spustí průchod rozložení, včetně měření a uspořádání.
    • ApplyTemplate způsobí, že se pro každý ovládací prvek vytvoří obsah šablony ovládacího prvku, což je obvykle největší část doby rozložení během spuštění.
  • Vykreslování vytváří vizuální prvky pro obsah okna.
  • Zobrazí se první snímek a práce po spuštění pokračuje asynchronně.

Dělejte méně na své podnikatelské cestě

Udržujte spouštěcí cestu kódu zbavenou všeho, co není potřeba pro první frame.

  • Pokud máte uživatelské knihovny DLL obsahující ovládací prvky, které nejsou potřeba během prvního rámce, zvažte jejich odložené načítání.
  • Pokud máte část uživatelského rozhraní, která závisí na datech z cloudu, rozdělte ho. Nejprve vyvolejte uživatelské rozhraní, které není závislé na cloudových datech, a pak asynchronně vyvolá cloudové uživatelské rozhraní závislé na cloudu. Měli byste také zvážit místní ukládání dat do mezipaměti, aby aplikace fungovala offline nebo nebyla ovlivněna pomalým připojením k síti.
  • Pokud uživatelské rozhraní čeká na data, zobrazte rozhraní průběhu.
  • Dávejte pozor na návrhy aplikací, které zahrnují spoustu analýzy konfiguračních souborů nebo uživatelského rozhraní, které se dynamicky generují kódem.

Snížit počet prvků

Výkon spouštění v aplikaci XAML přímo koreluje s počtem prvků, které vytvoříte během spouštění. Čím méně prvků vytvoříte, tím méně času se aplikace spustí. Jako hrubé srovnání zvažte, že vytvoření každého prvku trvá 1 ms.

  • Šablony použité v ovládacích prvcích položek můžou mít největší dopad, protože se opakují několikrát. Viz Aktualizace uživatelského rozhraní ListView a GridView.
  • Uživatelské ovládací prvky a šablony ovládacích prvků jsou rozšířené, takže je třeba je také zohlednit.
  • Pokud vytvoříte kód XAML, který se na obrazovce nezobrazí, měli byste ospravedlnit, zda by se tyto části XAML měly vytvořit během spouštění.

Okno Visual Studio Live Visual Tree zobrazuje počty podřízených prvků pro každý uzel ve stromu.

Živý vizuální strom

Použijte odložení. Sbalení elementu nebo nastavení jeho neprůhlednosti na hodnotu 0 nezabrání vytvoření prvku. Pomocí x:Load nebo x:DeferLoadStrategymůžete zpozdit načítání části uživatelského rozhraní a v případě potřeby ho načíst. Je to dobrý způsob, jak zpozdit zpracování uživatelského rozhraní, které není viditelné během spouštění, abyste ho mohli načíst v případě potřeby nebo jako součást sady zpožděné logiky. K aktivaci načítání stačí zavolat FindName u prvku. Příklad a další informace naleznete v položkách atribut x:Load a atribut x:DeferLoadStrategy.

Virtualizace. Pokud máte v uživatelském rozhraní obsah ve formě seznamu nebo opakovače, důrazně doporučujeme používat virtualizaci rozhraní. Pokud uživatelské rozhraní seznamu není virtualizované, platíte náklady na vytvoření všech prvků předem a to může zpomalit spuštění. Viz Aktualizace uživatelského rozhraní ListView a GridView.

Výkon aplikace není pouze o nezpracovaný výkon; je to také o vnímání. Změna pořadí operací tak, aby vizuální prvky probíhaly jako první, může uživateli navodit pocit, že aplikace běží rychleji. Uživatelé považují aplikaci za načtenou, když je obsah na obrazovce. Aplikace obvykle potřebují při spuštění vykonat více úloh, a ne všechny z nich jsou nezbytné pro zobrazení uživatelského rozhraní, takže tyto úlohy by měly být zpožděné nebo by měly mít méně prioritní než uživatelské rozhraní.

Tento článek se zabývá prvním snímkem, který pochází z terminologie animací a videa, a je měřítkem, jak dlouho trvá, než koncový uživatel uvidí obsah.

Zlepšení vnímání startupu

Pojďme použít příklad jednoduché online hry k identifikaci jednotlivých fází startupu a různých technik k poskytnutí zpětné vazby uživatelů v průběhu procesu.

V první fázi se proces spustí a aplikace vytvoří její okno. Během této doby uživatel ještě neviděl vlastní obsah aplikace. Vaším cílem je rychle získat lehké okno na obrazovce.

Druhá fáze zahrnuje vytváření a inicializaci struktur, které jsou pro hru kritické. Pokud aplikace dokáže rychle vytvořit své počáteční uživatelské rozhraní s daty dostupnými při spuštění, je tato fáze triviální a uživatelské rozhraní můžete okamžitě zobrazit. Jinak se při inicializaci aplikace zobrazí odlehčená stránka načítání.

Jak vypadá stránka načítání, je na vás; může být stejně jednoduché jako zobrazení indikátoru průběhu nebo okruhu průběhu. Klíčovým bodem je, že aplikace indikuje, že funguje dříve, než plně reaguje. V případě hry vyžaduje počáteční obrazovka načtení některých obrázků a zvuků z disku do paměti. Tyto úkoly zabírají čas, takže aplikace uživatele informuje zobrazením stránky načítání s jednoduchou animací související s motivem hry.

Třetí fáze začíná poté, co hra má minimální sadu informací k vytvoření interaktivního uživatelského rozhraní, které nahrazuje stránku načítání. V tomto okamžiku mohou být jedinými informacemi dostupnými pro online hru obsah, který aplikace načetla z disku. Hra může přijít s dostatečným obsahem k vytvoření interaktivního uživatelského rozhraní, ale protože je to online hra, nebude plně funkční, dokud není připojena k internetu a nestáhne potřebné informace. Dokud nebude mít všechny potřebné informace, může uživatel pracovat s uživatelským rozhraním, ale funkce, které potřebují další data z webu, by měly poskytnout zpětnou vazbu, že se obsah stále načítá. Může nějakou dobu trvat, než se aplikace stane plně funkčním, takže je důležité, aby funkce byly zpřístupněny co nejdříve.

Teď, když jsme identifikovali tři fáze spuštění v online hře, pojďme je spojit se skutečným kódem.

Fáze 1 a fáze 2

Konstruktor aplikace slouží pouze k inicializaci datových struktur, které jsou pro aplikaci důležité. Zaměřte OnLaunched se na rychlé vytvoření prvního okna, přiřazování jednoduchého obsahu a aktivaci okna, aby aplikace okamžitě zobrazila zpětnou vazbu.

public partial class App : Application
{
    public static Window MainWindow { get; private set; } = null!;

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        base.OnLaunched(args);

        MainWindow = new MainWindow();
        MainWindow.Content = new LoadingPage();
        MainWindow.Activate();

        _ = InitializeAsync();
    }

    private async Task InitializeAsync()
    {
        // Asynchronously restore state and load the minimum data needed
        // to create the first interactive UI.
        await LoadInitialDataAsync();

        MainWindow.Content = new GameHomePage();
    }

    private static Task LoadInitialDataAsync()
    {
        // Download data to populate the initial UI.
        return Task.CompletedTask;
    }
}

Jedním z klíčových úkolů v OnLaunched je vytvoření uživatelského rozhraní, jeho přiřazení k Window.Content a volání Window.Activate. Pokud potřebujete více než jeden tok aktivace, mějte stejný princip: rychle ukažte jednoduchý obsah a přesuňte nákladnou práci mimo kritickou spouštěcí cestu.

Aplikace, které během spuštění zobrazují stránku načítání, mohou začít pracovat na vytvoření hlavního uživatelského rozhraní na pozadí. Po vytvoření tohoto prvku dojde k události FrameworkElement.Loaded . V obslužné rutině události můžete nahradit obsah okna, který je aktuálně načítací obrazovkou, nově vytvořenou domovskou stránkou.

Je důležité, aby aplikace s prodlouženou inicializací zobrazovala nahrávací stránku. Kromě poskytnutí zpětné vazby k procesu spuštění by se mělo okno aktivovat rychle, aby uživatelé viděli, že aplikace postupuje.

partial class GameHomePage : Page
{
    public GameHomePage()
    {
        InitializeComponent();

        // Add a handler to be called when the home page has been loaded.
        Loaded += GameHomePageLoaded;

        // Load the minimal amount of image and sound data from disk necessary
        // to create the home page.
    }

    private void GameHomePageLoaded(object sender, RoutedEventArgs e)
    {
        // Set the content of the main window to the home page now that it's
        // ready to be displayed.
        App.MainWindow.Content = this;
    }
}

Fáze 3

Jenom proto, že aplikace zobrazovala uživatelské rozhraní neznamená, že je úplně připravená k použití. V případě naší hry se uživatelské rozhraní zobrazí se zástupnými symboly pro funkce, které vyžadují data z internetu. V tomto okamžiku hra stáhne další data potřebná k tomu, aby aplikace byla plně funkční a postupně umožňuje funkce při získávání dat.

Někdy může být většina obsahu potřebného pro spuštění zabalena do aplikace. To je případ jednoduché hry. Díky tomu je proces spuštění poměrně jednoduchý. Mnoho programů, jako jsou čtenáři zpráv a čtenáři fotek, ale musí získat informace z webu, aby byly funkční. Tato data můžou být velká a stahování může trvat poměrně dlouho. Jak aplikace získá tato data během spouštění, může mít obrovský dopad na vnímaný výkon.

Stránku načítání můžete zobrazit příliš dlouho, pokud se aplikace pokusila stáhnout celou sadu dat, kterou potřebuje pro funkce v první nebo druhé fázi spuštění. Aplikace se tak zablokuje. Doporučujeme, aby aplikace stáhla minimální množství dat potřebných k zobrazení interaktivního uživatelského rozhraní se zástupnými prvky ve fázi 2 a následně postupně načítá data, která nahradí zástupné prvky ve fázi 3. Další informace o práci s daty naleznete v tématu Optimalizace ListView a GridView.

Jak přesně aplikace reaguje na každou fázi spuštění, je zcela na vás, ale poskytuje uživateli co největší zpětnou vazbu pomocí jednoduchého počátečního uživatelského rozhraní, načítání obrazovek a progresivního načítání dat umožňuje, aby se aplikace cítila rychleji.

Minimalizace spravovaných sestavení v cestě po spuštění

Opakovaně použitelný kód často přichází ve formě modulů (DLL) zahrnutých v projektu. Načtení těchto modulů vyžaduje přístup k disku a náklady se mohou časem navyšovat. To má největší dopad na studený start, ale může to mít vliv i na teplé spuštění. V aplikacích .NET se CLR snaží zpožďovat nároky co nejvíce načítáním sestavení podle potřeby. To znamená, že CLR nenačte modul, dokud na něj spuštěná metoda neodkáže. Proto odkazujte pouze na sestavení, která jsou nezbytná ke spuštění aplikace ve spouštěcím kódu, aby modul CLR nenačítá nepotřebné moduly. Pokud máte ve spouštěcí cestě nepoužívané cesty kódu, které mají zbytečné odkazy, přesuňte tyto cesty kódu do jiných metod, abyste se vyhnuli zbytečnému načítání.

Dalším způsobem, jak snížit zatížení modulů, je kombinovat moduly aplikací. Načítání jednoho velkého sestavení obvykle trvá kratší dobu než načtení dvou malých sestavení. To není vždy možné a moduly byste měli kombinovat jenom v případě, že nepředstavuje podstatný rozdíl v produktivitě vývojáře nebo opakované použitelnosti kódu. Pomocí nástrojů, jako je PerfView nebo Windows Performance Analyzer (WPA), můžete zjistit, jaké moduly se načítají při spuštění.

Vytváření inteligentních webových požadavků

Dobu načítání aplikace můžete výrazně zlepšit tím, že obsah zabalíte místně, včetně XAML, obrázků a všech dalších souborů, které jsou pro aplikaci důležité. Diskové operace jsou rychlejší než síťové operace. Pokud aplikace potřebuje při inicializaci určitý soubor, můžete zkrátit celkovou dobu spuštění načtením z disku místo načtení ze vzdáleného serveru.

Efektivní ukládání stránek do deníku a mezipaměti

Ovládací Frame prvek poskytuje navigační funkce. Nabízí navigaci na stránku (Navigate metodu), deník navigace (BackStack a ForwardStack vlastnosti GoForward a GoBack metody), ukládání stránek do mezipaměti (Page.NavigationCacheMode) a podporu serializace (GetNavigationState metoda).

Výkon, na který je potřeba se zaměřit při použití Frame, se týká především zapisování do deníku a mezipaměti stránek.

Deníkování snímků. Když přejdete na stránku s Frame.Navigate, do kolekce Frame.BackStack se přidá PageStackEntry pro aktuální stránku. PageStackEntry je relativně malý, ale neexistuje žádný předdefinovaný limit velikosti BackStack kolekce. Uživatel potenciálně může navigovat ve smyčce a tuto kolekci bez omezení zvětšovat.

Zahrnuje PageStackEntry také parametr, který byl předán metodě Frame.Navigate . Doporučuje se, aby parametr byl primitivní serializovatelný typ, například int nebo string, aby metoda Frame.GetNavigationState mohla fungovat. Tento parametr však může potenciálně odkazovat na objekt, který představuje větší množství pracovních sad nebo jiných prostředků, což každou položku v BackStack činí mnohem nákladnější. Můžete například použít StorageFile jako parametr a v důsledku toho BackStack by mohl zůstat otevřený neurčitý počet souborů.

Proto doporučujeme zachovat parametry navigace malé a omezit velikost BackStack. Jedná se BackStack o standardní kolekci v jazyce C#, takže ji můžete oříznout jednoduše odebráním položek.

Ukládání stránek do mezipaměti Ve výchozím nastavení, když přejdete na stránku s metodou Frame.Navigate, vytvoří se nová instance stránky. Podobně, pokud přejdete zpět na předchozí stránku pomocí Frame.GoBack, bude přidělena nová instance této stránky.

Frame nabízí také volitelnou mezipaměť stránek, která může zabránit těmto instanciacím. Pokud chcete stránku vložit do mezipaměti, použijte Page.NavigationCacheMode tuto vlastnost. Když nastavíte tento režim tak, aby Required donutila stránku k uložení do mezipaměti, zatímco nastavení na Enabled umožnila její uložení do mezipaměti. Ve výchozím nastavení je velikost mezipaměti 10 stránek, ale lze ji přepsat vlastností Frame.CacheSize . Všechny Required stránky jsou uloženy do mezipaměti a pokud je méně než CacheSize potřebných stránek, mohou být uloženy do mezipaměti i Enabled stránky.

Ukládání stránek do mezipaměti může přispět k výkonu tím, že se vyhnete vytváření instancí, a tím zlepšíte výkon navigace. Ukládání do mezipaměti stránky může poškodit výkon kvůli nadměrnému ukládání do mezipaměti a tím ovlivnit pracovní sadu.

Proto doporučujeme používat ukládání stránek do mezipaměti podle potřeby pro vaši aplikaci. Řekněme například, že máte aplikaci, která zobrazuje seznam položek v objektu Framea když vyberete položku, přejde na rámeček na stránku podrobností dané položky. Stránka seznamu by měla být pravděpodobně nastavená na mezipaměť. Pokud je stránka podrobností stejná pro všechny položky, měla by být pravděpodobně uložena i do mezipaměti. Pokud je ale stránka podrobností heterogenní, může být lepší nechat ukládání do mezipaměti vypnuté.