Ieri ho assistito ad una sessione sull’ottimizzazione delle performance sul Compact Framework. Sessione interessante dove, dal mio punto di vista, mi sono confermato alcuni aspetti da tenere BENE in considerazione effettuando il design di un’applicazione, e dove sono riuscito ad aggiungere alcuni nuovi.
Loader&Startup
Chiunque abbia svilupapto un’applicazione mobile ha notato la particolare lentezza di caricamento alla prima esecuzione, rispetto a codice sviluppato in maniera nativa; questo deriva principalmente dal JITCompiler che deve provvedere a compilare in maniera nativa il codice che deve essere eseguito. Oltre a ciò però vengono fatti una serie di verifiche di security su tutti gli i file eseguibili dell’applicativo (che per fortuna vengono poi messi in cache), nonchè viene fatto un hashing check per verificare lo strong name. Da queste affermazioni un unica considerazione: cercare di mantenere il codice più piccolo possibile, lasciando eventuali risorse (immagini o altro) non embedded nel codice, ma come risorse esterne: La verifica dello strong name pesa per 1 secondo per megabite di codice allo startup (non male!); potrebbe, in questo senso, essere valutabile se firmare o meno il progetto.
GarebadgeCollector
Se il processo sta andando out of memery, rimuove dalla memoria il codice che è stato compilato dal Jit, questo accade quando ci sono 1milione di oggetti istanziati (sono tanti, ma con il performance monitor si può verificare che il numero di oggetti istanziati da un’applicazione semplice è comunque notevole); la stessa cosa viene fatta quando l’applicazione va in background: questo è perchè si vuole lasciare più memoria possibile al sistema.
CG.Collect non va utilizzato! Questo in realtà lo sappiamo già, ma è bene ricordarselo 🙂 Il GC sa quando deve fare le cose senza che noi glielo ricordiamo.
Alcune funzioni del .net framework impattano moltissimo il garbade collector, come la manipolazione delle stringhe ed il boxing dei tipi (piuttosto è meglio usare i generics).
Tips & Tricks
Per la gestione delle form, valgono sempre le solite considerazioni: laricare i form in background, usare sempre BeginUpdate/EndUpdate quando disponibili, impostare SuspendLayout/ResumeLayout sulla form quando si aggiungono o spostano controlli sulla form.
Un consiglio utile che ho avuto, è stato: “L’exception handling non è costosa in termini di performance… fintanto che non ne fai una throw”: l’eccezione è una ECCEZIONE, e così deve essere trattata; non deve essere gestita per controllare il flusso dell’applicativo (ogni tanto si cade in questo errore).
Per quanto riguarda la gestione dell’IO, cercare se possibile di usare le stess dimensioni dei buffer interni, per reference:
- FileStream: 128 bytes
- StreamReader, StreamWriter: 1024
Infine due dati sul reflection dei tipi, (dai… parte di una slide la “prendo in prestito” 🙂
Performance cost
- Type comparisons (for example: typeof())—inexpensive
- Member access (for example: Type.InvokeMember())—think 10-100x slower
Working set cost
- Member enumerations (for example: Type.GetFields())—expensive
- Runtime data structures: ~100 bytes/type, ~80 bytes/method
Be aware of APIs that use reflection as a side effect
- Override Object.ToString()
- Override GetHashCode() and Equals() for value types