Výkonnost nastavení UWP aplikací

Univerzální platforma Windows obsahuje ApplicationData API, které poskytuje snadnou cestu pro ukládání a čtení aplikačních a uživatelských nastavení. Pokud jej však ve vaší aplikaci potřebujete používat velmi často, můžete narazit na výkonnostní problémy. Jak s nimi naložit?

Výkonnost nastavení

Čtení uložených nastavení pomocí ApplicationData  je velmi jednoduché:

Pomocí klíče můžete přečíst nastavení jako z obyčejného slovníku. Z tohoto důvodu to může vypadat, že ve skutečnosti přistupujeme pouze ke slovníku, který je plně uložen v paměti a nějakým magickým způsobem je automaticky persistován na pevný disk na pozadí. Bohužel to vypadá, že tato doměnka není úplně správná.

Benchmark

Pro ilustraci problému jsem vytvořil jednoduchý benchmark (můžete si jej prohlédnout zde na mém GitHubu). Tato aplikace provádí čtení nastavení dvěma způsoby:

Přímo pomocí ApplicationData API :

A nepřímo cachováním hodnoty po prvním přečtení nastavení:

Benchmark toto čtení provádí oběma způsoby 100 000 krát za sebou. Bez výjimky vypadá výsledek podobně jako na následujícím obrázku:

Rozdíl mezi oběma přístupy je markantní! Přímý přístup přes  ApplicationData byl v tomto případě téměř 1 500krát pomalejší než verze s cachováním!

Vypadá to, že  ApplicationData  API cachuje pouze částečně nebo ve skutečnosti čte nastavení přímo z disku (ačkoliv o tomto pochybuji, protože rozhraní by pak bylo pravděpodobně asynchronní). V každém případě není přímý přístup bez cachování ideální pro výkonnostně intenzivní aplikace.

Aktualizace: Jak podotkl Petr Hudeček, který na problém narazil první při práci na našem softwarovém projektu, nízká rychlost je pravděpdobně způsobena tím, že čtení hodnoty nastavení z ApplicationData způsobuje alokace paměti na pozadí a to v mnoha opakováních vede na mnoho cyklů garbage collection, které jsou, jak víme, velmi drahé z hlediska výkonu.

Settings service s cachováním

Protože my vývojáři máme rádi zapouzdřování, chtěli bychom určitě zabalit caching s přístupem k ApplicationData  tak, abychom nad touto logikou nemuseli přemýšlet při vývoji aplikace.

Protože UWP nastavení podporují dvě různé lokace nastavení, nejprve definujeme užitečný výčtový typ:

Můžete si všimnout, že již v UWP API existuje typ ApplicationDataLocality. Rozhodl jsem se ale tento typ nepoužívat, protože obsahuje další hodnoty, které pro nastavení nejsou podporována jako Temporary  či SharedLocal .

Nyní můžeme začít psát samotnou Settings service. Nejprve deklarujeme slovník, který bude obsahovat cachovaná nastavení, která již byla načtena do paměti:

Pro načtení nastevní nejprve zkontrolujeme, zda jej musíme přečíst z   ApplicationData  nebo již máme k dispozici cachovanou verzi.

K tomuto kódu uvedeme ještě několik poznámek:

  • Pomocí parametru typu Func<T>  můžeme vytvořit výchozí hodnotu pokud nastavení v  ApplicationData není uloženo
  • Používáme výčtový typ SettingLocality  abychom určili, zda je nastavení lokální nebo podporuje roaming
  • Parametr forceResetCache  umožňuje vynutit čtení z  ApplicationData , což může být užitečné například když dojde k synchronizaci roaming settings

Metoda RetrieveSettingFromApplicationData  jednoduše přečte nastavení a vrátí výchozí hodnotu pokud není nalezeno. Také jsme zahrnuli kontrolu, zda uložená hodnota je skutečně správného typu.

Nakonec ještě vytvoříme metodu pro uložení nastavení. Ta je v porovnání s předchozími velmi jednoduchá:

Hodnotu pouze uložíme a provedeme invalidaci cache, aby příští přístup četl novou hodnotu.

Zdrojový kód

Celý kód Settings service je dostupný na mém GitHubu.

Shrnutí

Ukázali jsme si, že ApplicationData  API je velmi praktické, ale může být zároveň pomalé pokud jej potřebujeme používat velmi často. Pro některé aplikace se tak může stát úzkým hrdlem výkonu. Naštěstí je poměrně jednoduché se problému vyhnout pomocí cachování.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

*