Componenti Windows Runtime e ottimizzazione dell'interoperabilità

Creare app di Windows che usano componenti Windows Runtime e l'interoperabilità tra tipi nativi e gestiti evitando problemi di prestazioni di interoperabilità.

Procedure consigliate per l'interoperabilità con componenti Windows Runtime

Se non si presta attenzione, l'uso di componenti Windows Runtime può avere un forte impatto sulle prestazioni dell'app. Questa sezione descrive come ottenere buone prestazioni quando l'app usa componenti Windows Runtime.

Introduzione

L'interoperabilità può avere un forte impatto sulle prestazioni e può essere usata senza neanche accorgersene. Windows Runtime gestisce molta interoperabilità per favorire la produttività e per consentire il riutilizzo di codice scritto in altri linguaggi. È consigliabile sfruttare le funzionalità del Windows Runtime, ma tenere presente che può influire sulle prestazioni. Questa sezione descrive le soluzioni per ridurre l'impatto dell'interoperabilità sulle prestazioni dell'app.

Windows Runtime include una libreria di tipi accessibili da qualsiasi linguaggio con cui è possibile scrivere un'app per la piattaforma UWP (Universal Windows Platform). È possibile usare i tipi Windows Runtime in C# o Microsoft Visual Basic allo stesso modo in cui si usano gli oggetti .NET. Per accedere ai componenti Windows Runtime non sono necessarie chiamate del metodo di tipo platform invoke. In questo modo, la scrittura delle app è molto meno complessa, ma è importante ricordare che ne può derivare un'interoperabilità superiore rispetto al previsto. Se un componente Windows Runtime è scritto in un linguaggio diverso da C# o Visual Basic, usandolo si possono superare i limiti di interoperabilità, con un impatto sulle prestazioni di un'app.

Quando si sviluppa un'app per la piattaforma UWP (Universal Windows Platform) in C# o Visual Basic, i due set di API più comunemente usati sono le API di Windows Runtime e le API .NET per le app UWP. In generale, i tipi forniti da Windows basati su Windows Runtime negli spazi dei nomi che iniziano con "Windows" e i tipi .NET si trovano in spazi dei nomi che iniziano con "System". Esistono però delle eccezioni. L'uso di tipi elencati in .NET per le app UWP non richiede interoperabilità. Se si riscontrano prestazioni non ottimali in un'area che usa Windows Runtime, è possibile usare .NET per le app UWP per ottenere prestazioni migliori.

Nota La maggior parte dei componenti Windows Runtime forniti con Windows 10 viene implementata in C++ e, se usata da C# o Visual Basic, può provocare il superamento dei limiti di interoperabilità. Come sempre, assicurarsi di misurare le prestazioni dell'app per determinare l'impatto dell'uso di componenti Windows Runtime prima di apportare modifiche al codice.

In questo argomento, il termine "componenti Windows Runtime" indica i componenti Windows Runtime scritti in un linguaggio diverso da C# o Visual Basic.

 

Ogni volta che si accede a una proprietà o si chiama un metodo in un componente Windows Runtime, si incorre in costi di interoperabilità. Infatti, la creazione di un componente Windows Runtime è molto più costosa rispetto alla creazione di un oggetto .NET. Il motivo è che Windows Runtime deve eseguire codice per la transizione dal linguaggio dell'app al linguaggio del componente. Inoltre, se si passano dati al componente, questi devono essere convertiti tra tipi gestiti e non gestiti.

Uso efficiente dei componenti Windows Runtime

Se si vogliono ottenere prestazioni migliori, assicurarsi che il codice usi i componenti Windows Runtime nel modo più efficiente possibile. Questa sezione illustra alcuni suggerimenti per migliorare le prestazioni quando si usano componenti Windows Runtime.

Perché l'impatto sulle prestazioni sia evidente, è necessario un numero significativo di chiamate in un breve periodo di tempo. Un'applicazione ben progettata che incapsula chiamate a componenti Windows Runtime da logica di business e altro codice gestito non incorre in costi di interoperabilità eccessivi. Tuttavia, se i test indicano che l'uso di componenti Windows Runtime influisce negativamente sulle prestazioni dell'app, i suggerimenti contenuti in questa sezione consentono di migliorare le prestazioni.

Prendere in considerazione l'uso dei tipi forniti da .NET per le app UWP

In alcuni casi è possibile completare un'attività usando un tipo Windows Runtime o un tipo fornito da .NET per le app UWP. È consigliabile non mescolare tipi .NET e tipi Windows Runtime. Cercare quindi di usare solo un tipo per volta. Ad esempio, è possibile analizzare un flusso di contenuto XML usando il tipo Windows.Data.Xml.Dom.XmlDocument (tipo Windows Runtime) o il tipo System.Xml.XmlReader (tipo .NET). Usare l'API proveniente dalla stessa tecnologia del flusso. Ad esempio, per la lettura di contenuto XML da MemoryStream usare il tipo System.Xml.XmlReader, perché sono entrambi tipi .NET. Se si legge da un file, usare il tipo Windows.Data.Xml.Dom.XmlDocument perché le API del file e XmlDocument sono entrambi implementati in componenti Windows Runtime nativi.

Copiare oggetti Windows Runtime in tipi .NET

Se un componente Windows Runtime restituisce un oggetto Windows Runtime, potrebbe essere utile copiare l'oggetto restituito in un oggetto .NET. Questa operazione è particolarmente importante quando si usano raccolte e flussi.

Se chiami un'API di Windows Runtime che restituisce una raccolta e quindi salvi la raccolta e vi accedi molte volte, potrebbe essere utile copiarla in una raccolta .NET e usare da questo momento in poi la versione .NET.

Memorizzare nella cache i risultati delle chiamate a componenti Windows Runtime per usarli successivamente

Si potrebbero ottenere prestazioni migliori salvando i valori in variabili locali anziché accedere a un tipo Windows Runtime più volte. Questa scelta può essere particolarmente utile se si usa un valore all'interno di un ciclo. Misurare l'app per verificare se l'uso di variabili locali ne migliora le prestazioni. Usando valori memorizzati nella cache è possibile aumentare la velocità dell'app, perché in questo modo potrà dedicare meno tempo all'interoperabilità.

Combinare le chiamate ai componenti Windows Runtime

Provare a completare le attività con il minor numero possibile di chiamate a oggetti UWP. Ad esempio, in genere è meglio leggere una grande quantità di dati da un flusso che non piccole quantità per volta.

Usare API che raggruppano il lavoro in un numero minimo di chiamate anziché API che svolgono una minore quantità di lavoro con un numero di chiamate maggiore. Ad esempio, optare per la creazione di un oggetto mediante la chiamata di costruttori che inizializzano più proprietà invece di chiamare il costruttore predefinito e assegnare le proprietà una per volta.

Creazione di componenti Windows Runtime

Se si scrive un componente Windows Runtime che può essere usato da app scritte in C++ o JavaScript, assicurarsi che sia progettato in modo da garantire buone prestazioni. Tutti i suggerimenti per ottenere buone prestazioni nelle app sono utili anche per ottenere buone prestazioni nei componenti. Misurare le prestazioni del componente per scoprire quali API mostrano modelli di traffico elevato e per queste aree valutare se fornire API che permettano agli utenti di lavorare con numero minore di chiamate.

Mantenere invariata la velocità dell'app quando si usa l'interoperabilità in codice gestito

Windows Runtime semplifica l'interoperabilità tra codice nativo e codice gestito, ma se non si presta attenzione può influire negativamente sulle prestazioni. Ecco alcuni suggerimenti per ottenere buone prestazioni quando si usa l'interoperabilità nelle app UWP gestite.

Windows Runtime permette agli sviluppatori di scrivere app usando XAML con il linguaggio preferito grazie alle proiezioni delle API di Windows Runtime disponibili in ogni linguaggio. Quando si scrive un'app in C# o Visual Basic, questo vantaggio comporta un costo in termini di interoperabilità perché le API di Windows Runtime vengono in genere implementate in codice nativo e per qualsiasi chiamata di Windows Runtime da C# o Visual Basic è necessario che CLR esegua la transizione da uno stack frame gestito a uno nativo ed esegua il marshalling dei parametri delle funzioni in rappresentazioni accessibili dal codice nativo. Questo sovraccarico è trascurabile per la maggior parte delle app. Tuttavia, se esegui molte chiamate (da centinaia di migliaia fino diversi milioni) ad API di Windows Runtime nel percorso critico di un'app, l'impatto può diventare significativo. In generale è preferibile fare in modo che il tempo dedicato alla transizione tra linguaggi sia limitato rispetto all'esecuzione del resto del codice. Tale condizione è illustrata dal diagramma seguente.

Le transizioni di interoperabilità non devono dominare il tempo di esecuzione del programma.

I tipi elencati in .NET per app di Windows non comportano questo impatto in termini di interoperabilità se vengono usati da C# o Visual Basic. Come regola generale, si può supporre che i tipi negli spazi dei nomi che iniziano con "Windows." facciano parte del set di API Windows Runtime fornito da Windows, mentre quelli all'interno di spazi dei nomi che iniziano con "System." siano tipi .NET. Ricordare che anche il semplice utilizzo di tipi Windows Runtime comporta un costo in termini di interoperabilità per l'allocazione o l'accesso alle proprietà.

Misurare le prestazioni dell'app e determinare se l'interoperabilità occupa gran parte del tempo di esecuzione dell'app prima di ottimizzare i costi di interoperabilità. Quando si analizzano le prestazioni dell'app con Visual Studio, è possibile ottenere facilmente un limite superiore sui costi di interoperabilità usando la visualizzazione Funzioni e osservando il tempo occupato dai metodi che chiamano Windows Runtime.

Se la tua app è lenta per il sovraccarico causato dall'interoperabilità, puoi migliorarne le prestazioni riducendo le chiamate alle API di Windows Runtime in percorsi di codice critici. Ad esempio, il motore di un gioco che esegue un numero elevato di calcoli fisici con ricerca costante della posizione e delle dimensioni di UIElements può risparmiare molto tempo archiviando le informazioni necessarie di UIElements in variabili locali, eseguendo i calcoli su questi valori memorizzati nella cache e riassegnando il risultato finale a UIElements al termine dei calcoli. Un altro esempio è una raccolta con accesso frequente da codice C# o Visual Basic. È più efficiente usare una raccolta dallo spazio dei nomi System.Collections anziché dallo spazio dei nomi Windows.Foundation.Collections. È anche possibile combinare le chiamate ai componenti Windows Runtime, una scelta possibile, ad esempio, usando le API Windows.Storage.BulkAccess.

Creazione di un componente UWP

Se si crea un componente Windows Runtime da usare in app scritte in C++ o JavaScript, assicurarsi che sia progettato in modo da garantire buone prestazioni. La superficie dell'API definisce il limite di interoperabilità e la misura in cui gli utenti dovranno considerare le indicazioni fornite in questo argomento. Se si prevede di distribuire i componenti ad altre parti, questo aspetto assume un'importanza particolare.

Tutti i suggerimenti per ottenere buone prestazioni nelle app sono applicabili anche per ottenere buone prestazioni nei componenti. Misurare le prestazioni del componente per scoprire quali API mostrano modelli di traffico elevato e per tali aree valutare se fornire API che permettano agli utenti di lavorare con un numero minore di chiamate. Un impegno particolare è stato dedicato a progettare l'ambiente Windows Runtime in modo da permettere alle app di usarlo senza dover superare troppo spesso il limite di interoperabilità.