2015-12-18 13 views
5

Quanto guadagno di prestazioni si può ottenere usando struct over class? Devo progettare le app focalizzandole al massimo utilizzo della struttura rispetto alla classe?C# struct vs Class performace, Design focus

Posso convertire le mie classi in struct e spostare le funzioni in altre classi statiche dovunque sia possibile?

+1

L'utilizzo di una struttura eviterà un ulteriore dereferenziamento di un riferimento per accedere ai campi della struttura ma al costo aggiuntivo che ogni volta che è necessario passare la struttura attorno ad esso viene copiato a meno che non lo si passi esplicitamente per riferimento. Questo può essere utile, ma molto probabilmente l'utilizzo di strutture su classi sarà dannoso per il tuo codice. –

+0

Ciò che può essere più significativo è evitare l'allocazione dinamica. le strutture, ad esempio i tipi di valore, si comportano in modo molto simile agli inte; quando li crei sono memoria locale o statica che è significativamente più economica dell'allocazione dinamica. Il rovescio della medaglia è, come dice Martin, che il valore viene copiato per assegnazione, reso, ecc. Questo è più costoso solo per gli oggetti di grandi dimensioni, piuttosto che restituire un riferimento. –

risposta

3

Le raccomandazioni MSDN (elencati in un'altra risposta) offre qualche indicazione. Per le prestazioni dovresti considerare il loro utilizzo e dove conta la differenza tra le strutture e le classi. La cosa più importante è usare solo le strutture quando il tipo è un tipo di valore effettivo, cioè la semantica valore. L'utilizzo di una classe per un tipo di valore o una struttura per qualcosa che dovrebbe essere un tipo di riferimento genererà rapidamente risultati confusi con copie non intenzionali o riferimenti non voluti. Ricorda inoltre che le strutture dovrebbero essere sempre immutabili.

Solo in situazioni estremamente sensibili alle prestazioni si dovrebbe ignorare una qualsiasi di queste regole di base (Caso in questione: il framework infrange le regole per la struttura List.Enumerator!).

considerazioni Perf per le strutture vs classi:

Se si passa lo struct come argomento di un metodo, si creerà una copia della struct ogni volta. Questo è il motivo per cui i documenti raccomandano di non rendere le strutture più grandi di 16 byte.

double Distance(Vector3D a, Vector3D b) // Copies both 24-byte structs 
{ 
    return Math.Sqrt((a.X - b.X *....); 
} 

Nello scenario precedente, un 3D vettore di 3 doppie renderebbe 24 byte e sarebbe più grande della raccomandata 16, ma mi piacerebbe ancora sostengono che una struct ha senso dal momento che è chiaramente un tipo di valore, in particolare hai un Vector2D contenente due doppi (16 byte) che è una struttura!

La chiave per utilizzare le strutture in modo efficiente e dove le loro prestazioni brilla davvero è usarle per la località di cache ed evitare molte allocazioni. Per riutilizzare l'esempio Vettoriale sopra, se si dispone di questo metodo

double AverageDistanceFromOrigin(Vector3D[] points) // Single reference passed 
{ 
    double sum = 0.0; 
    for(...)           
     sum += Math.Sqrt(points[i].X... + ... + ...) // No copies 
    return sum/points.Length; 
} 

Si può notare una buona differenza di prestazioni a favore delle strutture. Il motivo è che ora si sta passando un singolo riferimento (l'array) al metodo, quindi non c'è nessun overhead aggiuntivo per copiare la struct per call (si noti che il metodo non chiama un metodo distance per ogni voce nell'array) .

La matrice di strutture viene inoltre impostata in modo consecutivo in memoria come [x1, y1, z1, x2, y2, ...] in modo che la CPU carichi, ad es. 16 coordinate alla volta nella cache, portando a pochi errori di cache. Confrontalo con un'implementazione class Vector3D: ora verrà assegnata una matrice di vettori e ogni voce nell'array sarà anche essere un riferimento che deve essere assegnato all'heap e successivamente eliminato. L'array è ora una matrice di riferimenti [ref to v1, ref to v2, ref to v3] ognuno dei quali può avere qualsiasi indirizzo nell'heap e non può sedersi uno accanto all'altro a. Questo può portare a molti più errori di cache rispetto al caso struct. solo

  • Usa struct se il tipo ha valore semantica (e viceversa).
  • Non passare le grandi strutture singolarmente ai metodi; Operare su liste/matrici di strutture per evitare di copiare
  • Non considerare il limite di 16 byte come un limite rigido: è possibile utilizzare strutture molto più grandi, ma ricordarsi di evitare di passarle singolarmente ai metodi.
+0

Questa è una spiegazione molto buona. Grazie. – DHN

8

Il MSDN ha buoni punti a quello:

✓ considerare la definizione di una struct invece di una classe se le istanze del tipo sono piccole e spesso di breve durata o sono comunemente incorporati in altri oggetti.

X evitare la definizione di una struttura meno che il tipo ha tutte le seguenti caratteristiche:

  1. Esso rappresenta logicamente un singolo valore, simili ai tipi primitivi (int, double, ecc).
  2. Ha una dimensione dell'istanza inferiore a 16 byte.
  3. È immutabile.
  4. Non dovrà essere inserito frequentemente in una scatola.

Anche il MSDN ha Struct Design

Dovrei progettare le applicazioni di messa a fuoco l'uso ingrandimento di struct sulla classe?

Dipende da cosa si sta tentando di implementare. Le strutture vengono utilizzate quando si dispone di piccole strutture che si sta tentando di implementare e che si comportano come valori. Quindi dire che usare struct over class non è una dichiarazione o un approccio ideale.

2

Bene, la risposta breve è "È possibile, ma non si dovrebbe".

La differenza principale tra classi e strutture è che una struttura definisce un tipo di valore, mentre una classe definisce un tipo di riferimento. Quindi non puoi in realtà sostituire la parola "classe" con "struct" e spostare le funzioni, perché il comportamento del tuo programma potrebbe essere pesantemente influenzato dalla differenza sopra menzionata. Quindi, se stai progettando una nuova applicazione da zero, puoi considerare se definire gli oggetti logici usando strutture o classi dove è appropriato, ma il tuo codice deve tenerlo a mente. Spero che questo aiuti!

0

Convertire le tue strutture in classi non migliorerà automaticamente le prestazioni della tua applicazione.
Dipende da come li usi, e da come hai usato i tuoi oggetti di classe.

Se creare e distruggere un sacco di oggetti di una classe, è possibile riutilizzare un oggetto di quella classe, o creare una struct e riutilizzarlo troppo
(in modo da vedere, si potrebbe farlo con una classe troppo) .

Quando si chiama un metodo e inviare i parametri o restituire un valore,
una classe viene passato per riferimento , ed una struct viene passato per valore .
Così qui si può godere di un guadagno in termini di prestazioni, se si passa parecchio tempo.
(ancora una volta, con una buona programmazione si può anche evitare di passarlo tutto il tempo al metodo, rendendo i dati come un campo (i) della classe che contiene i metodi).

Per quanto riguarda la seconda domanda,
è possibile avere anche metodi all'interno di una struttura, non è necessario spostarli in una classe statica.

struct SimpleStruct 
{ 
    private int xval; 

    public int X 
    { 
     get 
     { 
      return xval; 
     } 
     set 
     { 
      if (value < 100) xval = value; 
     } 
    } 

    public void DisplayX() 
    { 
     Console.WriteLine("The stored value is: "+xval); 
    } 
} 

https://msdn.microsoft.com/en-us/library/aa288471.aspx

1

Quanto guadagno di prestazioni si può ottenere utilizzando la struttura sulla classe?

Per "potrebbe essere" la risposta sarebbe che si potrebbe molte volte più veloce.

Tuttavia potrebbe anche essere molte volte più lento.

La maggior parte delle volte non sarà né l'una né l'altra, ma solo un leggero guadagno o una perdita.

Quindi "userò le strutture per essere più veloce" è, di per sé, un pensiero sbagliato.

Se il punto di partenza è considerare se qualcosa dovrebbe essere un ValueType perché dovrebbe essere effettivamente un tipo di valore; definito, usato e ragionato - solo in termini di valore, o non dovrebbe essere un ValueType perché non è un tale tipo di valore, quindi probabilmente avrai la scelta più performante per quel particolare caso un buon 95% o più del tempo.

Del rimanente 5%, probabilmente il 90% di quelli sarà solo meno negligentemente performante se tu fossi andato dall'altra parte, e/o semplicemente non si troveranno in percorsi abbastanza caldi perché ciò sia importante. (Supponiamo che tu risparmi 20 millisecondi su un determinato metodo. Se quel metodo viene chiamato un milione di volte, hai risparmiato cinque ore e mezza di tempo di esecuzione, ma se viene chiamato dopo aver salvato 20 millisecondi non ti accorgi nemmeno di).

Dei casi in cui vorremmo effettivamente cambiare tra valori e tipi di riferimento per motivi di prestazioni, potremmo molto spesso passare da struct a class anziché viceversa. Anzi a tal punto che evitare che i tipi di valore abbiano una dimensione di oltre 16 byte è una linea guida spesso citata, anche se in realtà si tratta di un'ottimizzazione delle prestazioni piuttosto che di una parte intrinseca di ciò che significa essere un valore o un tipo di riferimento.

Vale anche la pena notare le differenze di valori e tipi di riferimento chiamando il codice. Ci sono alcune operazioni che possono essere molto più veloci su array di strutture mutabili rispetto agli array (o qualsiasi altra raccolta) di tipi di riferimento mutabili. Ma approfittarne c'è bisogno che entrambi rendano pubblici i campi e che i tipi di valore siano mutabili; entrambe le cose che possono causare notevoli difficoltà. Tali ottimizzazioni sono ragionevoli per i tipi private all'interno di una classe che li utilizza e al push per i tipi internal, ma se vengono eseguiti con i tipi public, si tratta di problemi.