Я отвечал на вопрос об обзоре кода, и я обнаружил интересную разницу в производительности (например, много) между x64 и x86.
class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<ModVsOptimization>();
Console.ReadLine();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static public ulong Mersenne5(ulong dividend)
{
dividend = (dividend >> 32) + (dividend & 0xFFFFFFFF);
dividend = (dividend >> 16) + (dividend & 0xFFFF);
dividend = (dividend >> 8) + (dividend & 0xFF);
dividend = (dividend >> 4) + (dividend & 0xF);
dividend = (dividend >> 4) + (dividend & 0xF);
if (dividend > 14) { dividend = dividend - 15; } // mod 15
if (dividend > 10) { dividend = dividend - 10; }
if (dividend > 4) { dividend = dividend - 5; }
return dividend;
}
}
public class ModVsOptimization
{
[Benchmark(Baseline = true)]
public ulong RawModulo_5()
{
ulong r = 0;
for (ulong i = 0; i < 1000; i++)
{
r += i % 5;
}
return r;
}
[Benchmark]
public ulong OptimizedModulo_ViaMethod_5()
{
ulong r = 0;
for (ulong i = 0; i < 1000; i++)
{
r += Program.Mersenne5(i);
}
return r;
}
}
x86:
// * Summary *
BenchmarkDotNet=v0.10.8, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i7-5930K CPU 3.50GHz (Broadwell), ProcessorCount=12
Frequency=3415991 Hz, Resolution=292.7408 ns, Timer=TSC
[Host] : Clr 4.0.30319.42000, 32bit LegacyJIT-v4.7.2098.0
DefaultJob : Clr 4.0.30319.42000, 32bit LegacyJIT-v4.7.2098.0
Method | Mean | Error | StdDev | Scaled |
---------------------------- |---------:|----------:|----------:|-------:|
RawModulo_5 | 4.601 us | 0.0121 us | 0.0107 us | 1.00 |
OptimizedModulo_ViaMethod_5 | 7.990 us | 0.0060 us | 0.0053 us | 1.74 |
// * Hints *
Outliers
ModVsOptimization.RawModulo_5: Default -> 1 outlier was removed
ModVsOptimization.OptimizedModulo_ViaMethod_5: Default -> 1 outlier was removed
// * Legends *
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Scaled : Mean(CurrentBenchmark) / Mean(BaselineBenchmark)
1 us : 1 Microsecond (0.000001 sec)
// ***** BenchmarkRunner: End *****
x64:
// * Summary *
BenchmarkDotNet=v0.10.8, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i7-5930K CPU 3.50GHz (Broadwell), ProcessorCount=12
Frequency=3415991 Hz, Resolution=292.7408 ns, Timer=TSC
[Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2098.0
DefaultJob : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2098.0
Method | Mean | Error | StdDev | Scaled |
---------------------------- |---------:|----------:|----------:|-------:|
RawModulo_5 | 8.323 us | 0.0042 us | 0.0039 us | 1.00 |
OptimizedModulo_ViaMethod_5 | 2.597 us | 0.0956 us | 0.0982 us | 0.31 |
// * Hints *
Outliers
ModVsOptimization.OptimizedModulo_ViaMethod_5: Default -> 2 outliers were removed
// * Legends *
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Scaled : Mean(CurrentBenchmark) / Mean(BaselineBenchmark)
1 us : 1 Microsecond (0.000001 sec)
// ***** BenchmarkRunner: End *****
Теперь вот интересная деталь, которая меня не всегда удивляет (из-за того, как я особенно работаю над компилятором С#), и сборки x86 и x64 имеют один и тот же IL для метода RawModulo_5
.method public hidebysig instance uint64
RawModulo_5() cil managed
{
.custom instance void [BenchmarkDotNet.Core]BenchmarkDotNet.Attributes.BenchmarkAttribute::.ctor() = ( 01 00 01 00 54 02 08 42 61 73 65 6C 69 6E 65 01 ) // ....T..Baseline.
// Code size 31 (0x1f)
.maxstack 3
.locals init ([0] uint64 r,
[1] uint64 i)
IL_0000: ldc.i4.0
IL_0001: conv.i8
IL_0002: stloc.0
IL_0003: ldc.i4.0
IL_0004: conv.i8
IL_0005: stloc.1
IL_0006: br.s IL_0014
IL_0008: ldloc.0
IL_0009: ldloc.1
IL_000a: ldc.i4.5
IL_000b: conv.i8
IL_000c: rem.un
IL_000d: add
IL_000e: stloc.0
IL_000f: ldloc.1
IL_0010: ldc.i4.1
IL_0011: conv.i8
IL_0012: add
IL_0013: stloc.1
IL_0014: ldloc.1
IL_0015: ldc.i4 0x3e8
IL_001a: conv.i8
IL_001b: blt.un.s IL_0008
IL_001d: ldloc.0
IL_001e: ret
} // end of method ModVsOptimization::RawModulo_5
Теперь я не уверен, где искать дальше, но я подозреваю, что проблема находится где-то в JITter, хотя я тестировал на RyuJIT и LegacyJIT, оба имели тот же общий результат с архитектурой x64 (хотя LegacyJIT был немного медленнее в целом). Они запускаются в режиме Release вне Visual Studio, поэтому я предполагаю, что там не было присоединенного сеанса отладки.
Итак, мне любопытно, что вызывает это? Я не знаю, как исследовать дальше, но если у кого-нибудь есть идеи относительно дальнейших шагов расследования, не стесняйтесь комментировать, и я с удовольствием попробую их выполнить.