x86に比べてx64をターゲットにしてMath.Roundを使用してintをdoubleに変換すると、パフォーマンスが非常に低下しています。私はCore i7 3770K上の64ビットWindowsでテストしました。誰でもそれを再現できますか?これがなぜこのような理由があるのでしょうか?多分奇妙な境界条件でしょうか?x64プラットフォームでMath.Roundのパフォーマンスが大幅に低下しました
私はMath.Round
(Test1)と2つの近似を比較しました。条件付きキャスト(Test2)と6755399441055744トリック(Test3)です。回を実行
は以下のとおりです。AndreyAkinshinは親切DOTNETにquestionを掲示した後、いくつかの時間
:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace MathRoundTester
{
class Program
{
private const int IterationCount = 1000000;
private static int dummy;
static void Main(string[] args)
{
var data = new double[100];
var rand = new Random(0);
for (int i = 0; i < data.Length; ++i)
{
data[i] = rand.NextDouble() * int.MaxValue * 2 +
int.MinValue + rand.NextDouble();
}
dummy ^= Test1(data);
dummy ^= Test2(data);
dummy ^= Test3(data);
RecordTime(data, Test1);
RecordTime(data, Test2);
RecordTime(data, Test3);
Console.WriteLine(dummy);
Console.Read();
}
private static void RecordTime(double[] data, Func<double[], int> action)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
var sw = Stopwatch.StartNew();
dummy ^= action(data);
sw.Stop();
Console.WriteLine((sw.ElapsedTicks/(double)Stopwatch.Frequency).ToString("F4"));
}
private static int Test1(double[] data)
{
int d = 0;
for (int i = 0; i < IterationCount; ++i)
{
for (int j = 0; j < data.Length; ++j)
{
var x = data[j];
d ^= (int)Math.Round(x);
}
}
return d;
}
private static int Test2(double[] data)
{
int d = 0;
for (int i = 0; i < IterationCount; ++i)
{
for (int j = 0; j < data.Length; ++j)
{
var x = data[j];
d ^= x > 0 ? (int)(x + 0.5) : (int)(x - 0.5);
}
}
return d;
}
[StructLayout(LayoutKind.Explicit)]
private struct DoubleIntUnion
{
public DoubleIntUnion(double a)
{
Int = 0;
Double = a;
}
[FieldOffset(0)]
public double Double;
[FieldOffset(0)]
public int Int;
}
private static int Test3(double[] data)
{
int d = 0;
for (int i = 0; i < IterationCount; ++i)
{
for (int j = 0; j < data.Length; ++j)
{
var x = data[j];
d ^= new DoubleIntUnion(x + 6755399441055744.0).Int;
}
}
return d;
}
}
}
アップデート2016年11月23日:
---------------------------
| | x86 | x64 |
|-------+--------+--------|
| Test1 | 0,0662 | 0,9975 |
| Test2 | 0,1517 | 0,1513 |
| Test3 | 0,1966 | 0,0978 |
---------------------------
ここではベンチマークのコードです/ coreclr repoでは、1.2.0マイルストーンに追加されました。だから、この問題は単なる見落としであり修正されるようです。
浮動小数点演算が行われている*非常に*異なっあなたはx64のを対象とする場合。 32ビットモードでは、ジッタは従来のFPUを使用します。 64ビットモードでは、プロセッサがSSE2をサポートしていることが確認できます。これはFPUの完全な置き換えではありません。 x86ジッタは、MIS.Round()をintrinsic *にするFISTP命令に依存することができます。言い換えれば、メソッド呼び出しではなく、単一のプロセッサ命令のみです。このようなx64ジッタの不運は、CLRをヘルパー関数に呼び出す必要がないというオーバーヘッドが見えています。 –
MidpointRounding.AwayFromZeroを使用して、32ビット版を遅くすることもできます。今や、組み込み関数はもう動作しません。 SSE2の典型的な結果である64ビット版は高速です。なぜなら、フレームワークがデフォルトの丸めモードとしてウォンキー銀行の丸めを好んだ理由を教えてくれるからです。 –
私のコアi7-4700HQで確認するとさらに悪くなります - 0.0723(32ビット)vs 1,1548(64ビット) –