Guida per principianti all'ottimizzazione del codice e alla riduzione dei costi di calcolo (C#, Visual Basic, C++, F#)

La riduzione del tempo di calcolo comporta la riduzione dei costi, in modo da poter ottimizzare il codice in modo da risparmiare denaro. In questo articolo viene illustrato come usare vari strumenti di profilatura per eseguire questa attività.

Invece di fornire istruzioni dettagliate, l'intento di seguito è illustrare come usare gli strumenti di profilatura in modo efficace e come interpretare i dati. Lo strumento Utilizzo CPU consente di acquisire e visualizzare la posizione in cui vengono usate le risorse di calcolo nell'applicazione. Le visualizzazioni Utilizzo CPU, ad esempio l'albero delle chiamate e il grafico di fiamma, offrono una bella visualizzazione grafica della posizione in cui viene impiegato il tempo nell'app. Inoltre, i dati analitici automatici possono mostrare ottimizzazioni precise che possono avere un impatto elevato. Altri strumenti di profilatura consentono anche di isolare i problemi. Per confrontare gli strumenti, vedere Quale strumento scegliere?

Avviare un'indagine

  • Avviare l'indagine prendendo una traccia dell'utilizzo della CPU. Lo strumento Utilizzo CPU è spesso utile per avviare indagini sulle prestazioni e ottimizzare il codice per ridurre i costi.
  • Successivamente, se si desidera ottenere informazioni dettagliate aggiuntive per isolare i problemi o migliorare le prestazioni, valutare la possibilità di raccogliere una traccia usando uno degli altri strumenti di profilatura. Ad esempio:
    • Esaminare l'utilizzo della memoria. Per .NET, provare prima lo strumento allocazione oggetti .NET. Per .NET o C++, è possibile esaminare lo strumento Utilizzo memoria.
    • Se l'app usa I/O file, usa lo strumento I/O file.
    • Se si usa ADO.NET o Entity Framework, è possibile provare lo strumento database per esaminare query SQL, tempo di query preciso e così via.

Esempio di raccolta dati

Gli screenshot di esempio illustrati in questo articolo si basano su un'app .NET che esegue query su un database di blog e post di blog associati. Si esaminerà prima di tutto una traccia di utilizzo della CPU per cercare opportunità per ottimizzare il codice e ridurre i costi di calcolo. Dopo aver ottenuto un'idea generale di ciò che sta succedendo, verranno esaminate anche le tracce di altri strumenti di profilatura per isolare i problemi.

La raccolta dei dati richiede i passaggi seguenti (non illustrati di seguito):

  • Impostare l'app su una build di rilascio
  • Selezionare lo strumento Utilizzo CPU dal profiler prestazioni (ALT+F2). I passaggi successivi coinvolgono alcuni degli altri strumenti.
  • Dal Profiler prestazioni avviare l'app e raccogliere una traccia.

Esaminare le aree di utilizzo elevato della CPU

Iniziare raccogliendo una traccia con lo strumento Utilizzo CPU. Quando i dati di diagnostica vengono caricati, controllare prima di tutto la pagina iniziale del report con estensione diagsession che mostra Le informazioni dettagliate principali e il percorso critico. Il percorso critico mostra il percorso del codice con l'utilizzo più elevato della CPU nell'app. Queste sezioni possono fornire suggerimenti utili per identificare rapidamente i problemi di prestazioni che è possibile migliorare.

È anche possibile visualizzare il percorso attivo nella visualizzazione Albero delle chiamate. Per aprire questa visualizzazione, usare il collegamento Apri dettagli nel report e quindi selezionare Albero delle chiamate.

In questa visualizzazione viene visualizzato di nuovo il percorso critico, che mostra un utilizzo elevato della CPU per il GetBlogTitleX metodo nell'app, usando circa una quota del 60% dell'utilizzo della CPU dell'app. Tuttavia, il valore self CPU per GetBlogTitleX è basso, solo circa .10%. A differenza della CPU totale, il valore self CPU esclude il tempo impiegato in altre funzioni, quindi sappiamo di guardare più lontano dalla visualizzazione Albero delle chiamate per il collo di bottiglia reale.

Screenshot della visualizzazione Albero delle chiamate nello strumento Utilizzo CPU.

GetBlogTitleXeffettua chiamate esterne a due DLL LINQ, che usano la maggior parte del tempo di CPU, come evidenziato dai valori auto CPU molto elevati. Questo è il primo indizio che può essere utile cercare una query LINQ come area da ottimizzare.

Screenshot della visualizzazione Albero delle chiamate nello strumento Utilizzo CPU con l'opzione Auto CPU evidenziata.

Per ottenere un albero delle chiamate visualizzato e una visualizzazione diversa dei dati, passare alla visualizzazione Grafico fiamma (selezionare dallo stesso elenco dell'albero delle chiamate). Anche in questo caso, sembra che il GetBlogTitleX metodo sia responsabile di un sacco di utilizzo della CPU dell'app (mostrato in giallo). Le chiamate esterne alle DLL LINQ vengono visualizzate sotto la GetBlogTitleX casella e usano tutto il tempo di CPU per il metodo .

Screenshot della visualizzazione Flame Graph nello strumento Utilizzo CPU.

Raccogliere dati aggiuntivi

Spesso, altri strumenti possono fornire informazioni aggiuntive per facilitare l'analisi e isolare il problema. Ad esempio, poiché sono state identificate le DLL LINQ, si proverà prima lo strumento Database. È possibile selezionare più questo strumento insieme all'utilizzo della CPU. Dopo aver raccolto una traccia, selezionare la scheda Query nella pagina diagnostica.

Nella scheda Query per la traccia database è possibile visualizzare la prima riga che mostra la query più lunga, 2446 ms. La colonna Record mostra il numero di record letti dalla query. È possibile usare queste informazioni per un confronto successivo.

Screenshot delle query di database nello strumento Database.

Esaminando l'istruzione SELECT generata da LINQ nella colonna Query, si identifica la prima riga come query associata al GetBlogTitleX metodo . Per visualizzare la stringa di query completa, espandere la larghezza della colonna, se necessario. La stringa di query completa è:

SELECT "b"."Url", "b"."BlogId", "p"."PostId", "p"."Author", "p"."BlogId", "p"."Content", "p"."Date", "p"."MetaData", "p"."Title"
FROM "Blogs" AS "b" LEFT JOIN "Posts" AS "p" ON "b"."BlogId" = "p"."BlogId" ORDER BY "b"."BlogId"

Si noti che qui si stanno recuperando molti valori di colonna, forse più di quanto necessario.

Per vedere cosa succede con l'app in termini di utilizzo della memoria, raccogliere una traccia usando lo strumento di allocazione di oggetti .NET (per C++, usare invece lo strumento Utilizzo memoria). La visualizzazione Albero delle chiamate nella traccia di memoria mostra il percorso critico e consente di identificare un'area di utilizzo elevato della memoria. Nessuna sorpresa a questo punto, il GetBlogTitleX metodo sembra generare un sacco di oggetti! Oltre 900.000 allocazioni di oggetti, infatti.

Screenshot della visualizzazione Albero delle chiamate nello strumento Allocazione oggetti .NET.

La maggior parte degli oggetti creati sono stringhe, matrici di oggetti e Int32. È possibile vedere come questi tipi vengono generati esaminando il codice sorgente.

Ottimizza codice

È il momento di esaminare il GetBlogTitleX codice sorgente. Nello strumento Allocazione oggetti .NET fare clic con il pulsante destro del mouse sul metodo e scegliere Vai al file di origine. Nel codice sorgente per GetBlogTitleXè disponibile il codice seguente che usa LINQ per leggere il database.

foreach (var blog in db.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
  {
    foreach (var post in blog.Posts)
    {
      if (post.Author == "Fred Smith")
      {
        Console.WriteLine($"Post: {post.Title}");
      }
  }
}

Questo codice usa foreach cicli per cercare nel database tutti i blog con "Fred Smith" come autore. Esaminandolo, è possibile notare che molti oggetti vengono generati in memoria: una nuova matrice di oggetti per ogni blog nel database, stringhe associate per ogni URL e valori per le proprietà contenute nei post, ad esempio l'ID blog.

Si eseguono alcune ricerche e si trovano alcune raccomandazioni comuni per ottimizzare le query LINQ e ottenere questo codice.

foreach (var x in db.Posts.Where(p => p.Author.Contains("Fred Smith")).Select(b => b.Title).ToList())
{
  Console.WriteLine("Post: " + x);
}

In questo codice sono state apportate diverse modifiche per ottimizzare la query:

  • Aggiungere la Where clausola ed eliminare uno dei foreach cicli.
  • Proiettare solo la proprietà Title nell'istruzione Select , che è sufficiente in questo esempio.

Eseguire quindi il nuovo test usando gli strumenti di profilatura.

Controllare i risultati

Dopo aver aggiornato il codice, eseguire nuovamente lo strumento Utilizzo CPU per raccogliere una traccia. La visualizzazione Albero delle chiamate mostra che GetBlogTitleX è in esecuzione solo 1754 ms, usando il 37% del totale della CPU dell'app, un miglioramento significativo del 59%.

Screenshot del miglioramento dell'utilizzo della CPU nella visualizzazione Albero delle chiamate dello strumento Utilizzo CPU.

Passare alla visualizzazione Flame Graph per visualizzare un'altra visualizzazione del miglioramento. In questa visualizzazione viene GetBlogTitleX usata anche una parte più piccola della CPU.

Screenshot del miglioramento dell'utilizzo della CPU nella visualizzazione Flame Graph dello strumento Utilizzo CPU.

Controllare i risultati nella traccia dello strumento database e solo due record vengono letti usando questa query, anziché 100.000. Inoltre, la query è molto semplificata ed elimina l'OPERAZIONE LEFT JOIN non necessaria generata in precedenza.

Screenshot del tempo di query più veloce nello strumento Database.

Successivamente, controllare nuovamente i risultati nello strumento allocazione oggetti .NET e verificare che GetBlogTitleX sia responsabile solo di 56.000 allocazioni di oggetti, quasi un 95% di riduzione rispetto al 900.000!

Screenshot delle allocazioni di memoria ridotte nello strumento Allocazione oggetti .NET.

Iterare

Potrebbero essere necessarie più ottimizzazioni ed è possibile continuare a eseguire l'iterazione con le modifiche del codice per verificare quali modifiche migliorano le prestazioni e riducono i costi di calcolo.

Passaggi successivi

I post di blog seguenti forniscono altre informazioni per imparare a usare gli strumenti per le prestazioni di Visual Studio in modo efficace.