Sdílet prostřednictvím


Měření dopadu rozšíření při spuštění

Zaměření na výkon rozšíření v sadě Visual Studio 2017

Na základě zpětné vazby od zákazníků byla jednou z zaměřených oblastí pro vydání sady Visual Studio 2017 výkon při spouštění a načítání řešení. Jako tým platformy Sady Visual Studio pracujeme na vylepšení výkonu při spouštění a načítání řešení. Obecně platí, že naše měření naznačují, že nainstalovaná rozšíření mohou mít také značný dopad na tyto scénáře.

Abychom uživatelům pomohli porozumět tomuto dopadu, přidali jsme v sadě Visual Studio novou funkci, která uživatelům oznámí pomalé rozšíření. Visual Studio někdy zjistí nové rozšíření, které zpomalí načtení řešení nebo spuštění. Po zjištění zpomalení se uživatelům v integrovaném vývojovém prostředí zobrazí oznámení, které je nasměruje na nové dialogové okno Spravovat výkon sady Visual Studio. K tomuto dialogovému oknem se dá získat přístup také nabídka nápovědy a procházet dříve zjištěná rozšíření.

manage Visual Studio performance

Cílem tohoto dokumentu je pomoct vývojářům rozšíření popisem výpočtu dopadu rozšíření. Tento dokument také popisuje, jak se dá místně analyzovat dopad rozšíření. Místní analýza dopadu rozšíření určí, jestli se rozšíření může zobrazit jako rozšíření ovlivňující výkon.

Poznámka:

Tento dokument se zaměřuje na dopad rozšíření na spouštění a načítání řešení. Rozšíření také ovlivňují výkon sady Visual Studio, když způsobí, že uživatelské rozhraní přestane reagovat. Další informace o tomto tématu najdete v tématu Postupy: Diagnostika zpoždění uživatelského rozhraní způsobených rozšířeními.

Vliv rozšíření na spuštění

Jedním z nejběžnějších způsobů, jak rozšíření ovlivnit výkon při spuštění, je zvolit automatické načtení v jednom ze známých kontextů uživatelského rozhraní spouštění, jako je NoSolutionExists nebo ShellInitialized. Tyto kontexty uživatelského rozhraní se aktivují během spouštění. Všechny balíčky, které obsahují ProvideAutoLoad atribut v definici s těmito kontexty, budou načteny a inicializovány v daném okamžiku.

Při měření dopadu rozšíření se primárně zaměříme na čas strávený těmito rozšířeními, která se rozhodnou automaticky načíst v kontextech výše. Měřené časy by zahrnovaly mimo jiné:

  • Načítání sestavení rozšíření pro synchronní balíčky
  • Čas strávený v konstruktoru třídy balíčku pro synchronní balíčky
  • Čas strávený v metodě Inicializace balíčku (nebo SetSite) pro synchronní balíčky
  • U asynchronních balíčků se výše uvedené operace spouští ve vlákně na pozadí. Proto jsou operace vyloučené z monitorování.
  • Čas strávený v jakékoli asynchronní práci naplánované během inicializace balíčku ke spuštění v hlavním vlákně
  • Čas strávený v obslužných rutinách událostí, konkrétně aktivace kontextového kontextu prostředí nebo změna stavu zombie prostředí
  • Od sady Visual Studio 2017 Update 3 také začneme monitorovat čas strávený na nečinných voláních před inicializacem prostředí. Dlouhé operace v nečinných obslužných rutinách také způsobují nereagující integrované vývojové prostředí (IDE) a přispívají k vnímané době spuštění uživatelem.

Od sady Visual Studio 2015 jsme přidali mnoho funkcí. Tyto funkce pomáhají s odebráním nutnosti automatického načítání balíčků. Tyto funkce také odloží potřebu načtení balíčků do konkrétnějších případů. Mezi tyto případy patří příklady, kdy by uživatelé měli větší jistotu, že rozšíření používají, nebo snížit dopad rozšíření při automatickém načítání.

Další podrobnosti o těchto funkcích najdete v následujících dokumentech:

Kontexty uživatelského rozhraní založené na pravidlech: Rozsáhlejší modul založený na pravidlech založený na kontextech uživatelského rozhraní umožňuje vytvářet vlastní kontexty založené na typech, příchutích a atributech projektu. Vlastní kontexty lze použít k načtení balíčku během konkrétnějších scénářů. Mezi tyto konkrétní scénáře patří přítomnost projektu s konkrétní funkcí místo spuštění. Vlastní kontexty také umožňují , aby viditelnost příkazů byla svázána s vlastním kontextem na základě komponent projektu nebo jiných dostupných termínů. Tato funkce eliminuje potřebu načíst balíček pro registraci obslužné rutiny dotazu stavu příkazu.

Podpora asynchronního balíčku: Nová základní třída AsyncPackage v sadě Visual Studio 2015 umožňuje, aby se balíčky sady Visual Studio načítaly na pozadí asynchronně, pokud bylo načtení balíčku požadováno atributem automatického načtení nebo asynchronním dotazem služby. Toto načítání na pozadí umožňuje, aby integrované vývojové prostředí (IDE) zůstalo responzivní. Integrované vývojové prostředí (IDE) reaguje i v době, kdy se rozšíření inicializuje na pozadí a kritické scénáře, jako je spouštění a načítání řešení, by to nemělo vliv.

Asynchronní služby: S podporou asynchronních balíčků jsme také přidali podporu pro asynchronní dotazování služeb a schopnost registrovat asynchronní služby. Důležitější je, že pracujeme na převodu základních služeb sady Visual Studio na podporu asynchronního dotazu, aby většina práce v asynchronním dotazu probíhala ve vláknech na pozadí. SComponentModel (hostitel MeF sady Visual Studio) je jednou z hlavních služeb, které teď podporují asynchronní dotaz, aby rozšíření podporovala asynchronní načítání úplně.

Snížení dopadu automaticky načtených rozšíření

Pokud se při spuštění stále musí automaticky načíst balíček, je důležité minimalizovat práci provedenou během inicializace balíčku. Minimalizace inicializační práce balíčku snižuje riziko rozšíření, které má vliv na spuštění.

Mezi příklady, které by mohly způsobit, že inicializace balíčků bude nákladná, jsou:

Použití synchronního načtení balíčku místo asynchronního načtení balíčku

Vzhledem k tomu, že synchronní balíčky se ve výchozím nastavení načítají do hlavního vlákna, doporučujeme vlastníkům rozšíření, kteří mají automaticky načtené balíčky, místo toho používat asynchronní základní třídu balíčků, jak jsme zmínili dříve. Změna automaticky načteného balíčku tak, aby podporovala asynchronní načítání, usnadní vám také řešení dalších níže uvedených problémů.

Synchronní požadavky na vstupně-výstupní operace souborů nebo sítě

V ideálním případě by se měl v hlavním vlákně vyhnout všechny synchronní požadavky na vstupně-výstupní operace v síti. Jejich dopad bude záviset na stavu počítače a v některých případech může blokovat dlouhou dobu.

Použití asynchronního načítání balíčků a asynchronních vstupně-výstupních rozhraní API by se mělo ujistit, že inicializace balíčků v takových případech neblokuje hlavní vlákno. Uživatelé také můžou dál pracovat se sadou Visual Studio, zatímco vstupně-výstupní požadavky probíhají na pozadí.

Počáteční inicializace služeb, komponent

Jedním z běžných vzorů inicializace balíčků je inicializace služeb, které používá nebo poskytuje tento balíček v balíčku constructor nebo initialize metodě. I když to zajišťuje, že služby jsou připravené k použití, můžou také při načítání balíčků přidat zbytečné náklady, pokud se tyto služby nepoužívají okamžitě. Místo toho by se takové služby měly inicializovat na vyžádání, aby se minimalizovala práce provedená při inicializaci balíčků.

Pro globální služby poskytované balíčkem můžete použít AddService metody, které funkci lazily inicializovat pouze v případě, že je požadován komponentou. U služeb používaných v rámci balíčku můžete pomocí Lazy<T> nebo AsyncLazy<T> zajistit, aby se služby inicializovaly nebo dotazovaly při prvním použití.

Měření dopadu rozšíření automatického načtení pomocí protokolu aktivit

Od sady Visual Studio 2017 Update 3 teď protokol aktivit sady Visual Studio bude obsahovat položky pro dopad balíčků na výkon během spouštění a načítání řešení. Abyste viděli tato měření, musíte otevřít Visual Studio s přepínačem /log a otevřít soubor ActivityLog.xml .

V protokolu aktivit budou položky ve zdroji Spravovat výkon sady Visual Studio a budou vypadat jako v následujícím příkladu:

Component: 3cd7f5bf-6662-4ff0-ade8-97b5ff12f39c, Inclusive Cost: 2008.9381, Exclusive Cost: 2008.9381, Top Level Inclusive Cost: 2008.9381

Tento příklad ukazuje, že balíček s identifikátorem GUID 3cd7f5bf-6662-4ff0-ade8-97b5ff12f39c strávil 2008 ms při spuštění sady Visual Studio. Všimněte si, že Visual Studio při výpočtu dopadu balíčku považuje náklady nejvyšší úrovně za primární číslo, protože by to bylo úspory, které uživatelé uvidí, když rozšíření pro daný balíček zakáže.

Měření dopadu rozšíření automatického načtení pomocí perfView

I když analýza kódu může pomoct identifikovat cesty kódu, které mohou zpomalit inicializaci balíčků, můžete také využít trasování pomocí aplikací, jako je PerfView , abyste pochopili dopad načtení balíčku při spuštění sady Visual Studio.

PerfView je nástroj pro trasování v celém systému. Tento nástroj vám pomůže porozumět horkým cestám v aplikaci z důvodu využití procesoru nebo blokování systémových volání. Níže je rychlý příklad analýzy ukázkového rozšíření pomocí perfView.

Příklad kódu:

Tento příklad je založený na níže uvedeném vzorovém kódu, který je navržený tak, aby ukázal některé běžné příčiny zpoždění:

protected override void Initialize()
{
    // Initialize a class from another assembly as an example
    MakeVsSlowServiceImpl service = new MakeVsSlowServiceImpl();

    // Costly work in main thread involving file IO
    string systemPath = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
    foreach (string file in Directory.GetFiles(systemPath))
    {
        DateTime creationDate = File.GetCreationTime(file);
    }

    // Costly work after shell is initialized. This callback executes on main thread
    KnownUIContexts.ShellInitializedContext.WhenActivated(() =>
    {
        DoMoreWork();
    });

    // Start async work on background thread
    DoAsyncWork().Forget();
}

private async Task DoAsyncWork()
{
    // Switch to background thread to do expensive work
    await TaskScheduler.Default;
    System.Threading.Thread.Sleep(500);
}

private void DoMoreWork()
{
    // Costly work
    System.Threading.Thread.Sleep(500);
    // Blocking call to an asynchronous work.
    ThreadHelper.JoinableTaskFactory.Run(async () => { await DoAsyncWork(); });
}

Záznam trasování pomocí perfView:

Jakmile nastavíte prostředí sady Visual Studio s nainstalovaným rozšířením, můžete zaznamenat trasování spuštění otevřením perfView a otevřením dialogového okna Collect z nabídky Collect.

perfview collect menu

Výchozí možnosti poskytují zásobníky volání pro spotřebu procesoru, ale vzhledem k tomu, že nás zajímá také blokování času, měli byste také povolit zásobníky času vlákna. Jakmile jsou nastavení připravená, můžete po spuštění záznamu kliknout na Spustit kolekci a potom otevřít Visual Studio.

Před zastavením shromažďování chcete zajistit, aby se sada Visual Studio plně inicializovala, hlavní okno je zcela viditelné a pokud má vaše rozšíření nějaké části uživatelského rozhraní, které se automaticky zobrazí, jsou také viditelné. Po úplném načtení sady Visual Studio a inicializace rozšíření můžete zastavit záznam a analyzovat trasování.

Analýza trasování pomocí nástroje PerfView:

Po dokončení nahrávání perfView se automaticky otevře trasování a možnosti rozbalení.

Pro účely tohoto příkladu nás zajímá hlavně zobrazení Zásobníky času vláken, které najdete v části Pokročilá skupina. Toto zobrazení zobrazí celkovou dobu strávenou vláknem metodou, včetně času procesoru i blokovaného času, například vstupně-výstupních operací disku nebo čekání na popisovače.

thread time stacks

Při otevírání zobrazení Zásobníky času vláken byste měli zvolit proces odstranění odvenv a zahájit analýzu.

Nástroj PerfView obsahuje podrobné pokyny ke čtení zásobníků času vláken v nabídce vlastní nápovědy pro podrobnější analýzu. Pro účely tohoto příkladu chceme toto zobrazení dál filtrovat pouze zahrnutím zásobníků s názvem modulu balíčků a spouštěcím vláknem.

  1. Nastavte GroupPats na prázdný text, aby se ve výchozím nastavení odebraly všechny přidané seskupení.
  2. Nastavte IncPats tak, aby zahrnovala část názvu sestavení a spouštěcí vlákno kromě existujícího filtru procesů. V tomto případě by měl být devenv; Spouštěcí vlákno; MakeVsSlowExtension.

Teď se v zobrazení zobrazí jenom náklady spojené se sestaveními souvisejícími s rozšířením. V tomto zobrazení platí, že kdykoliv uvedený ve sloupci Inc (Inclusive cost) vlákna se vztahuje k našemu filtrovanému rozšíření a bude mít vliv na spuštění.

V příkladu výše by byly některé zajímavé zásobníky volání:

  1. Vstupně-výstupní operace využívající System.IO třídu: I když inkluzivní náklady na tyto rámce nemusí být v trasování příliš drahé, představují potenciální příčinu problému, protože rychlost vstupně-výstupních operací souborů se bude lišit od počítače po počítač.

    system io frames

  2. Blokování volání čekajících na jinou asynchronní práci: V tomto případě by inkluzivní čas představoval čas, kdy je hlavní vlákno blokováno při dokončení asynchronní práce.

    blocking call frames

Jedním z dalších zobrazení v trasování, které bude užitečné k určení dopadu, bude zásobníky načítání obrázků. Můžete použít stejné filtry jako použité v zobrazení Zásobníky času vláken a zjistit všechna sestavení načtená z důvodu kódu spouštěného vaším automaticky načteným balíčkem.

Je důležité minimalizovat počet načtených sestavení uvnitř rutiny inicializace balíčku, protože každé další sestavení bude zahrnovat další vstupně-výstupní operace disku, které mohou výrazně zpomalit spouštění na pomalejších počítačích.

Shrnutí

Spuštění sady Visual Studio je jednou z oblastí, na které průběžně získáváme zpětnou vazbu. Naším cílem, jak jsme uvedli dříve, je, aby všichni uživatelé měli konzistentní spouštěcí prostředí bez ohledu na komponenty a rozšíření, která si nainstalovali. Chtěli bychom spolupracovat s vlastníky rozšíření, abychom jim pomohli dosáhnout tohoto cíle. Výše uvedené pokyny by měly být užitečné při pochopení dopadu rozšíření na spuštění a jak zabránit nutnosti automatického načítání, nebo ho asynchronně načíst, aby se minimalizoval dopad na produktivitu uživatelů.