Domanda

Ho un numero di classi di dati che rappresentano varie entità.

Che è meglio: scrivere una classe generica (diciamo, per stampare o produrre XML) usando generici e interfacce o scrivere una classe separata per gestire ogni classe di dati?

C'è un vantaggio in termini di prestazioni o qualsiasi altro vantaggio (diverso da quello che mi fa risparmiare il tempo di scrivere classi separate)?

È stato utile?

Soluzione

L'utilizzo di generici comporta un notevole vantaggio in termini di prestazioni: elimini boxing e unboxing . Rispetto allo sviluppo delle tue classi, è un lancio di monete (con un lato della moneta pesato più dell'altro). Esegui il tuo solo se pensi di essere in grado di superare gli autori del framework.

Altri suggerimenti

Non solo sì, ma CONTROLLA SÌ. Non credevo che grande differenza potessero fare. Abbiamo eseguito i test in VistaDB dopo una riscrittura di una piccola percentuale di codice core che utilizzava ArrayLists e HashTables sui generici. Il 250% o più è stato il miglioramento della velocità.

Leggi il mio blog sui test che abbiamo fatto su raccolte generiche vs tipi deboli. I risultati ci hanno fatto impazzire.

Ho iniziato a riscrivere un sacco di vecchio codice che utilizzava le raccolte debolmente tipizzate in raccolte fortemente tipizzate. Uno dei miei maggiori punti di forza con l'interfaccia ADO.NET è che non espongono modi più fortemente tipizzati per ottenere dati dentro e fuori. Il tempo di cast da un oggetto e ritorno è un killer assoluto in applicazioni ad alto volume.

Un altro effetto collaterale della tipizzazione forte è che spesso nel tuo codice troverai problemi di riferimento debolmente digitati. Abbiamo scoperto che attraverso implementazione di strutture in alcuni casi per evitare di esercitare pressioni sul GC potremmo accelerare ulteriormente il nostro codice. Combina questo con la digitazione forte per il tuo miglior aumento di velocità.

A volte è necessario utilizzare interfacce debolmente tipizzate nel runtime dot net. Quando possibile, cerca i modi per rimanere fortemente tipizzato. Fa davvero una differenza enorme nelle prestazioni per applicazioni non banali.

I generici in C # sono tipi veramente generici dal punto di vista CLR. Non dovrebbe esserci alcuna differenza fondamentale tra le prestazioni di una classe generica e una classe specifica che fa esattamente la stessa cosa. Questo è diverso da Java Generics, che sono più di un cast di tipo automatizzato laddove necessario o modelli C ++ che si espandono in fase di compilazione.

Ecco un buon documento, un po 'vecchio, che spiega il design di base: " Progettazione e implementazione di Generics per il .NET Common Language Runtime & Quot; .

Se scrivi a mano le classi per compiti specifici, puoi ottimizzare alcuni aspetti in cui avresti bisogno di deviazioni aggiuntive attraverso un'interfaccia di tipo generico.

In sintesi, potrebbe esserci un vantaggio in termini di prestazioni, ma consiglierei prima la soluzione generica, quindi ottimizza se necessario. Ciò è particolarmente vero se si prevede di creare un'istanza del generico con molti tipi diversi.

Ho fatto un semplice benchmarking su ArrayList's vs Elenchi generici per una domanda diversa: Generics vs. Array Elenchi , il tuo chilometraggio varierà, ma l'Elenco generico era 4,7 volte più veloce dell'ArrayList.

Quindi sì, la boxe / unboxing sono fondamentali se stai facendo molte operazioni. Se stai facendo semplici cose CRUD, non me ne preoccuperei.

I generici sono uno dei modi per parametrizzare il codice ed evitare la ripetizione. Guardando la descrizione del tuo programma e il tuo pensiero di scrivere una classe separata per gestire ogni singolo oggetto di dati, mi appoggiavo ai generici. Avere una singola classe che si prende cura di molti oggetti dati, invece di molte classi che fanno la stessa cosa, aumenta le prestazioni. E ovviamente le tue prestazioni, misurate nella capacità di cambiare il tuo codice, sono generalmente più importanti delle prestazioni del computer. : -)

Secondo Microsoft, i generici sono più veloci del casting (primitive boxe / unboxing) che è vero . Sostengono anche che i generici forniscono prestazioni migliori rispetto al casting tra tipi di riferimento, che sembra essere falso (nessuno può provarlo del tutto).

Tony Northrup - coautore di MCTS 70-536: Application Development Foundation - afferma nello stesso libro quanto segue:

  

I haven & # 8217; t sono stato in grado di riprodurre il file   benefici prestazionali dei generici;   tuttavia, secondo Microsoft,   i generici sono più veloci dell'uso   casting. In pratica, il casting ha dimostrato   essere più volte più veloce dell'uso   un generico. Tuttavia, probabilmente hai vinto & # 8217; t   notare differenze di prestazioni nel tuo   applicazioni. (I miei test oltre 100.000   le iterazioni hanno richiesto solo pochi secondi.)   Quindi dovresti comunque usare i generici   perché sono sicuri per il tipo.

Non sono stato in grado di riprodurre tali benefici in termini di prestazioni con generici rispetto al casting tra tipi di riferimento - quindi direi che il guadagno in termini di prestazioni è " supposto " più di " significativo " ;.

se si confronta un elenco generico (ad esempio) con un elenco specifico esattamente per il tipo che si utilizza, la differenza è minima, i risultati del compilatore JIT sono quasi gli stessi.

se si confronta un elenco generico con un elenco di oggetti, allora si ottiene un vantaggio significativo dall'elenco generico: niente boxing / unboxing per i tipi di valore e nessun controllo del tipo per i tipi di riferimento.

anche le classi di raccolta generiche nella libreria .net sono state fortemente ottimizzate e è improbabile che tu faccia di meglio da solo.

I generici sono più veloci!

Ho anche scoperto che Tony Northrup ha scritto cose sbagliate sull'esecuzione di generici e non generici nel suo libro.

Ho scritto di questo sul mio blog: http://andriybuday.blogspot.com/2010/ 01 / generici-prestazioni-vs-non-generics.html

Ecco un ottimo articolo in cui l'autore confronta le prestazioni di generici e non generici:

nayyeri.net/use-generics-to-improve-performance

Se stai pensando a una classe generica che chiama metodi su alcune interfacce per fare il suo lavoro, sarà più lenta di classi specifiche che usano tipi noti, perché chiamare un metodo di interfaccia è più lento di una chiamata di funzione (non virtuale) .

Naturalmente, a meno che il codice non sia la parte lenta di un processo critico per le prestazioni, dovresti concentrarti sulla chiarezza.

Nel caso di raccolte generiche vs. boxing et al., con raccolte precedenti come ArrayList, i generici sono una vittoria in termini di prestazioni. Ma nella stragrande maggioranza dei casi questo non è il vantaggio più importante dei generici. Penso che ci siano due cose che sono di grande beneficio:

  1. Digita sicurezza.
  2. Autodocumentazione, ovvero più leggibile.

I generici promuovono la sicurezza dei tipi, forzando una raccolta più omogenea. Immagina di imbatterti in una stringa quando ti aspetti un int. Ouch.

Le raccolte generiche sono anche più auto-documentate. Considera le due raccolte seguenti:

ArrayList listOfNames = new ArrayList();
List<NameType> listOfNames = new List<NameType>();

Leggendo la prima riga potresti pensare listOfNames è un elenco di stringhe. Sbagliato! In realtà sta memorizzando oggetti di tipo NameType. Il secondo esempio non solo impone che il tipo deve essere NameType (o un discendente), ma il codice è più leggibile. So subito che devo trovare TypeName e imparare come usarlo solo guardando il codice.

Ho visto molti di questi " x funziona meglio di y " domande su StackOverflow. La domanda qui è stata molto chiara e, a quanto pare, i generici sono una vittoria in qualunque modo scuoiate il gatto. Ma alla fine, il punto è fornire all'utente qualcosa di utile. Sicuramente l'applicazione deve essere in grado di funzionare, ma non deve arrestarsi in modo anomalo e devi essere in grado di rispondere rapidamente ai bug e alle richieste di funzionalità. Penso che puoi vedere come questi ultimi due punti si collegano con la sicurezza del tipo e la leggibilità del codice delle raccolte generiche. Se fosse il caso contrario, se ArrayList superasse List & Lt; & Gt ;, probabilmente prenderei ancora l'Elenco & Lt; & Gt; implementazione a meno che la differenza di prestazioni non sia stata significativa.

Per quanto riguarda le prestazioni (in generale), sarei disposto a scommettere che troverai la maggior parte dei colli di bottiglia delle prestazioni in queste aree nel corso della tua carriera:

  1. Scarsa progettazione di database o query di database (inclusi indicizzazione, ecc.)
  2. Cattiva gestione della memoria (dimenticando di chiamare dispose, stack profondi, trattenendo oggetti troppo a lungo, ecc.),
  3. Gestione errata dei thread (troppi thread, non chiamando IO su un thread in background nelle app desktop, ecc.)
  4. Progetto IO scadente.

Nessuno di questi è risolto con soluzioni a linea singola. Come programmatori, ingegneri e geek vogliamo conoscere tutti i piccoli trucchi prestazionali. Ma è importante tenere gli occhi sulla palla. Credo che concentrarsi su buone pratiche di progettazione e programmazione nelle quattro aree che ho menzionato sopra causerà molto di più che preoccuparsi di piccoli miglioramenti delle prestazioni.

Vedi anche il Blog di Rico Mariani su MSDN:

http://blogs.msdn.com/ricom /archive/2005/08/26/456879.aspx

  

Q1: quale è più veloce?

     

La versione di Generics è considerevolmente   più veloce, vedi sotto.

L'articolo è un po 'vecchio, ma fornisce i dettagli.

Non solo puoi eliminare la boxe, ma le implementazioni generiche sono leggermente più veloci rispetto alle controparti non generiche con tipi di riferimento a causa di un cambiamento nell'implementazione sottostante.

Gli originali sono stati progettati tenendo presente un particolare modello di estensione. Questo modello non è mai stato realmente utilizzato (e sarebbe stata comunque una cattiva idea) ma la decisione di progettazione ha costretto un paio di metodi a essere virtuali e quindi non in linea (in base alle ottimizzazioni JIT attuali e passate in questo senso).

Questa decisione è stata rettificata nelle classi più recenti ma non può essere modificata nelle classi precedenti senza che si tratti di un potenziale cambiamento di rottura binaria.

Inoltre iterazione tramite foreach su un Elenco < > (piuttosto che IList < >) è più veloce a causa dell'enumeratore di ArrayList che richiede un'allocazione di heap. Certamente questo ha portato a un bug oscuro

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top