私は小さなライブラリを書いていて、配列を合計できるようにしたいと思っています。シングルスレッドバージョンは正常に機能しますが、マルチスレッドを追加すると、すべてが機能しなくなります。
私はパーティショナーを使用してブロック上のデータを分割し、すべての部分を1つの結果に合計しています。それから私はそれを返します。ただし、データは無効です。プログラムを再起動するたびに同じ結果が生じるため、競合状態は発生していないようです。
[Pure]
public static double Sum(this double[] source)
{
source.IsNotNull("source");
if (source.Length < Constants.SingleThreadExecutionThreshold)
return Sum(source, 0, source.Length);
double result = 0;
object syncRoot = new object();
Parallel.ForEach(Partitioner.Create(0, source.Length),
() => (double)0,
(range, state, sum) => Sum(source, range.Item1, range.Item2),
x =>
{
lock (syncRoot)
result += x;
});
return result;
}
Sum(source, from, to)
常に正しい結果を出します。実装は次のとおりです。
[Pure]
private static double Sum(this double[] source, int startIndex, int endIndex)
{
double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;
checked
{
int i;
for (i = startIndex; i < endIndex - Constants.Step + 1; i += Constants.Step)
{
sum1 += source[i];
sum2 += source[i + 1];
sum3 += source[i + 2];
sum4 += source[i + 3];
}
if (i == source.Length)
return ((sum1 + sum2) + (sum3 + sum4));
if (i == source.Length - 1)
return ((sum1 + sum2) + (sum3 + sum4) + source[i]);
if (i == source.Length - 2)
return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1]));
return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1] + source[i + 2]));
}
}
internal static class Constants
{
public const int Step = 4;
public const int SingleThreadExecutionThreshold = 1024;
}
どうすれば修正できますか?
コード例:http://ideone.com/8sD0JL
さて、私はそれを修正したと思います。2つの大きなバグを見つけました。
これらの変更により、-0.000576496124267578の差が得られます。これは、二重和の丸め誤差の予想内です。
[Pure]
public static double Sum(this double[] source, int startIndex, int endIndex)
{
double sum1 = 0, sum2 = 0, sum3 = 0, sum4 = 0;
checked
{
int i;
int j = 0;
for (i = startIndex; i < endIndex - Constants.Step + 1; i += Constants.Step)
{
sum1 += source[i];
sum2 += source[i + 1];
sum3 += source[i + 2];
sum4 += source[i + 3];
j += Constants.Step;
}
var segmentLength = endIndex - startIndex;
if (j == segmentLength)
return ((sum1 + sum2) + (sum3 + sum4));
if (j == segmentLength - 1)
return ((sum1 + sum2) + (sum3 + sum4) + source[i]);
if (j == segmentLength - 2)
return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1]));
return ((sum1 + sum2) + (sum3 + sum4) + (source[i] + source[i + 1] + source[i + 2]));
}
}
internal static class Constants
{
public const int Step = 4;
public const int SingleThreadExecutionThreshold = 1024;
}
[Pure]
public static double Sum(this double[] source)
{
if (source.Length < Constants.SingleThreadExecutionThreshold)
return Sum(source, 0, source.Length);
double result = 0;
object syncRoot = new object();
Parallel.ForEach(Partitioner.Create(0, source.Length),
(range) => {
var x = Sum(source, range.Item1, range.Item2);
lock (syncRoot)
result += x;
});
return result;
}
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加