Empiricamente, il sovraccarico sembra essere, nel migliore dei casi, ~ 270% su JIT a 32 bit e ~ 200% su 64 bit (e l'overhead peggiora più volte si "chiama" fixed
). Quindi proverei a ridurre al minimo i tuoi blocchi fixed
se le prestazioni sono davvero critiche.
dispiace, io non sono abbastanza familiarità con il codice fisso/non sicuro di sapere il motivo per cui questo è il caso
dettagli
Ho anche aggiunto alcune TestMore
metodi che richiedono i vostri due metodi di prova 10 volte invece di 2 per offrire uno scenario più realistico di più metodi chiamati sulla tua struttura fixed
.
Il codice che ho usato:
class Program
{
static void Main(string[] args)
{
var someData = new ByteArray();
int iterations = 1000000000;
var multiple = new MultipleFixed();
var single = new SingleFixed();
// Warmup.
for (int i = 0; i < 100; i++)
{
multiple.Test(ref someData);
single.Test(ref someData);
multiple.TestMore(ref someData);
single.TestMore(ref someData);
}
// Environment.
if (Debugger.IsAttached)
Console.WriteLine("Debugger is attached!!!!!!!!!! This run is invalid!");
Console.WriteLine("CLR Version: " + Environment.Version);
Console.WriteLine("Pointer size: {0} bytes", IntPtr.Size);
Console.WriteLine("Iterations: " + iterations);
Console.Write("Starting run for Single... ");
var sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
single.Test(ref someData);
}
sw.Stop();
Console.WriteLine("Completed in {0:N3}ms - {1:N2}/sec", sw.Elapsed.TotalMilliseconds, iterations/sw.Elapsed.TotalSeconds);
Console.Write("Starting run for More Single... ");
sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
single.Test(ref someData);
}
sw.Stop();
Console.WriteLine("Completed in {0:N3}ms - {1:N2}/sec", sw.Elapsed.TotalMilliseconds, iterations/sw.Elapsed.TotalSeconds);
Console.Write("Starting run for Multiple... ");
sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
multiple.Test(ref someData);
}
sw.Stop();
Console.WriteLine("Completed in {0:N3}ms - {1:N2}/sec", sw.Elapsed.TotalMilliseconds, iterations/sw.Elapsed.TotalSeconds);
Console.Write("Starting run for More Multiple... ");
sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
multiple.TestMore(ref someData);
}
sw.Stop();
Console.WriteLine("Completed in {0:N3}ms - {1:N2}/sec", sw.Elapsed.TotalMilliseconds, iterations/sw.Elapsed.TotalSeconds);
Console.ReadLine();
}
}
unsafe struct ByteArray
{
public fixed byte Data[1024];
}
class MultipleFixed
{
unsafe void SetValue(ref ByteArray bytes, int index, byte value)
{
fixed (byte* data = bytes.Data)
{
data[index] = value;
}
}
unsafe bool Validate(ref ByteArray bytes, int index, byte expectedValue)
{
fixed (byte* data = bytes.Data)
{
return data[index] == expectedValue;
}
}
public void Test(ref ByteArray bytes)
{
SetValue(ref bytes, 0, 1);
Validate(ref bytes, 0, 1);
}
public void TestMore(ref ByteArray bytes)
{
SetValue(ref bytes, 0, 1);
Validate(ref bytes, 0, 1);
SetValue(ref bytes, 0, 2);
Validate(ref bytes, 0, 2);
SetValue(ref bytes, 0, 3);
Validate(ref bytes, 0, 3);
SetValue(ref bytes, 0, 4);
Validate(ref bytes, 0, 4);
SetValue(ref bytes, 0, 5);
Validate(ref bytes, 0, 5);
}
}
class SingleFixed
{
unsafe void SetValue(byte* data, int index, byte value)
{
data[index] = value;
}
unsafe bool Validate(byte* data, int index, byte expectedValue)
{
return data[index] == expectedValue;
}
public unsafe void Test(ref ByteArray bytes)
{
fixed (byte* data = bytes.Data)
{
SetValue(data, 0, 1);
Validate(data, 0, 1);
}
}
public unsafe void TestMore(ref ByteArray bytes)
{
fixed (byte* data = bytes.Data)
{
SetValue(data, 0, 1);
Validate(data, 0, 1);
SetValue(data, 0, 2);
Validate(data, 0, 2);
SetValue(data, 0, 3);
Validate(data, 0, 3);
SetValue(data, 0, 4);
Validate(data, 0, 4);
SetValue(data, 0, 5);
Validate(data, 0, 5);
}
}
}
E i risultati in .NET 4.0, 32 bit JIT:
CLR Version: 4.0.30319.239
Pointer size: 4 bytes
Iterations: 1000000000
Starting run for Single... Completed in 2,092.350ms - 477,931,580.94/sec
Starting run for More Single... Completed in 2,236.767ms - 447,073,934.63/sec
Starting run for Multiple... Completed in 5,775.922ms - 173,132,528.92/sec
Starting run for More Multiple... Completed in 26,637.862ms - 37,540,550.36/sec
E in .NET 4.0, 64 bit JIT:
CLR Version: 4.0.30319.239
Pointer size: 8 bytes
Iterations: 1000000000
Starting run for Single... Completed in 2,907.946ms - 343,885,316.72/sec
Starting run for More Single... Completed in 2,904.903ms - 344,245,585.63/sec
Starting run for Multiple... Completed in 5,754.893ms - 173,765,185.93/sec
Starting run for More Multiple... Completed in 18,679.593ms - 53,534,358.13/sec
Grazie - buone informazioni! Mi chiedo ancora quale sia la ragione di fondo per il sovraccarico, ma ottenere le buone prestazioni è l'obiettivo principale. –
Sì, mi rimetto a Skeet, Lippart o Gravel per il "perché". Ma se giochi con le dimensioni della tua struct, potrebbe dirti se il runtime sta facendo una copia della struct ogni 'fixed'. La mia ipotesi è che l'operazione di pinning copi l'intera struttura. (Vedi anche: http://www.dotnetperls.com/fixed-buffer) – ligos
Questo test non è accurato. Stai usando fisso in un modo totalmente irragionevole. L'uso corretto sarebbe quello di risolvere una volta, scrivere più volte, unfix. – JakeSays