2010-11-10 13 views
10

Sto cercando di ottenere quello che io chiamo sistema di unità di misura avvolgendo il doppio in struct. Ho strutture C# come Meter, Second, Degree, ecc. La mia idea originale era che dopo che il compilatore fosse in linea, avrei avuto una performance uguale a quella del double.Uso dei tipi C# per esprimere le unità di misura

I miei operatori espliciti e impliciti sono semplici e diretti, e il compilatore li in realtà li allinea, tuttavia il codice con Meter e Second è 10 volte più lento dello stesso codice che usa double.

La mia domanda è: perché il compilatore C# non può rendere il codice usando Second come ottimale come il codice che usa double se allinea tutto comunque?

In secondo luogo è definito come segue:

struct Second 
{ 
    double _value; // no more fields. 

    public static Second operator + (Second left, Second right) 
    { 
     return left._value + right._value; 
    } 
    public static implicit Second operator (double value) 
    { 
     // This seems to be faster than having constructor :) 
     return new Second { _value = value }; 
    } 

    // plenty of similar operators 
} 

Aggiornamento:

Non ho chiesto se struct si inserisce qui. Lo fa.

Non ho chiesto se il codice sarà in linea. JIT lo fa in linea.

Ho controllato le operazioni di assemblaggio emesse in runtime. Erano diversi per il codice come questo:

var x = new double(); 
for (var i = 0; i < 1000000; i++) 
{ 
    x = x + 2; 
    // Many other simple operator calls here 
} 

e in questo modo:

var x = new Second(); 
for (var i = 0; i < 1000000; i++) 
{ 
    x = x + 2; 
    // Many other simple operator calls here 
} 

Non c'erano istruzioni di chiamata in smontaggio, quindi le operazioni sono state infatti inline. Eppure la differenza è significativa. I test delle prestazioni mostrano che l'utilizzo di Second è 10 volte più lento rispetto all'utilizzo del doppio.

Quindi le mie domande sono (attenzione!): Perché il codice IA64 generato da JIT è diverso per i casi precedenti? Cosa si può fare per rendere la struttura più veloce del doppio? Sembra che non ci siano differenze teoriche tra il doppio e il secondo, qual è la ragione profonda della differenza che ho visto?

+1

è un operatore 'implicito' o' + '? –

+0

Potresti essere interessato a questa domanda correlata: http://stackoverflow.com/questions/348853/units-of-measure-in-c-almost – Benjol

+1

So che stai usando C#, ma hai considerato F #? Ha costruito in unità statiche il controllo che è una specie di ciò che sembra essere alla ricerca. Vedi http://stackoverflow.com/questions/40845/how-do-f-units-of-measure-work –

risposta

1

Il compilatore C# non inline nulla - JIT potrebbe farlo, ma non è obbligato a. Dovrebbe comunque essere abbondante velocemente. Io probabilmente togliere la conversione implicita nel + se (vedere l'utilizzo del costruttore di seguito) - un operatore di più per guardare attraverso:

private readonly double _value; 
public double Value { get { return _value; } } 
public Second(double value) { this._value = value; } 
public static Second operator +(Second left, Second right) { 
    return new Second(left._value + right._value); 
} 
public static implicit operator Second(double value) { 
    return new Second(value); 
} 

JIT inlining è limitata a scenari specifici. Questo codice li soddisferà? Difficile dire - ma dovrebbe lavoro e lavoro abbastanza veloce per la maggior parte degli scenari. Il problema con + è che esiste un codice operativo IL per l'aggiunta di doppi; funziona quasi a no funziona - dove, come il tuo codice chiama alcuni metodi statici e un costruttore; ci sarà sempre un po 'di overhead, anche quando è in linea.

+0

Ho fatto il wrapping di un 'int' in una struct (implementando il punto fisso) e quando il jitter ha allineato il codice (IMO deve essere in codice inline più aggressivo) ha prodotto un codice assembly perfetto. Quindi se inline non c'è probabilmente nessun sovraccarico. – CodesInChaos

4

Questa è la mia opinione, si prega di scrivere un commento se non siete d'accordo, invece di downvoting silenzioso.

Il compilatore C# non lo in linea. Il compilatore JIT potrebbe, ma questo è indeterministico per noi, perché il comportamento di JITer non è semplice.

In caso di double non viene effettivamente richiamato alcun operatore. Gli operatori vengono aggiunti direttamente nello stack utilizzando l'opcode add. Nel tuo caso, viene invocato il metodo op_Add più tre struct copia da e verso stack.

Per ottimizzarlo, iniziare con la sostituzione di struct con class. Almeno ridurrà al minimo la quantità di copie.

+2

... e le strutture ti metteranno nei guai lungo la strada. –

+0

Perché vuoi usare qui le classi invece delle strutture? I problemi con il suo appoach (la quantità enorme di operatori e tipi) non cambieranno se usasse 'class', ma la performance probabilmente diminuirà molto. – CodesInChaos

+0

@CodeInChaos non uso mai 'struct'. Penso davvero che dovrebbe essere usato solo con interop. "le prestazioni probabilmente diminuiranno molto" perché? spiega per favore. – Andrey

Problemi correlati