在 C# 中添加 long/ulong SSE 没有抛出溢出异常?
No overflow exception thrown for long/ulong SSE addition in C#?
对于非 SSE 代码,如以下问题 (No overflow exception for int in C#?) 中所回答,在加法周围添加一个 checked
部分,在添加 Int64.MaxValue 和 1 时抛出溢出异常。但是,围绕 checked
部分的 SSE 添加似乎不会引发 long[] arrLong = new long[] { 5, 7, 16, Int64.MaxValue, 3, 1 };
的溢出异常。我相信大多数 SSE 指令都使用饱和数学,它们到达 Int64.MaxValue
并且不会超过它并且永远不会绕回负数。在 C# 中有什么方法可以为添加 SSE 抛出溢出异常,还是不可能因为 CPU 可能不支持引发溢出标志?
下面的代码显示了我使用 SSE 对 long[] 求和的 C# SSE 实现。对于上面的数组,结果是一个负数,因为正数环绕并且不会饱和,因为 C# 必须使用该版本的 SSE 指令(因为有两个版本:一个环绕,一个饱和)。不知道 C# 是否允许开发者选择使用哪个版本。下面的代码只有串行代码部分会抛出溢出异常,而SSE部分不会。
using System.Numerics;
private static long SumSseInner(this long[] arrayToSum, int l, int r)
{
var sumVector = new Vector<long>();
int sseIndexEnd = l + ((r - l + 1) / Vector<long>.Count) * Vector<long>.Count;
int i;
for (i = l; i < sseIndexEnd; i += Vector<long>.Count)
{
var inVector = new Vector<long>(arrayToSum, i);
checked
{
sumVector += inVector;
}
}
long overallSum = 0;
for (; i <= r; i++)
{
checked
{
overallSum += arrayToSum[i];
}
}
for (i = 0; i < Vector<long>.Count; i++)
{
checked
{
overallSum += sumVector[i];
}
}
return overallSum;
}
下面是在C#中使用SSE实现ulong求和。我发布了它,因为它比长的总和要短得多,也更容易理解。
private static decimal SumToDecimalSseFasterInner(this ulong[] arrayToSum, int l, int r)
{
decimal overallSum = 0;
var sumVector = new Vector<ulong>();
var newSumVector = new Vector<ulong>();
var zeroVector = new Vector<ulong>(0);
int sseIndexEnd = l + ((r - l + 1) / Vector<ulong>.Count) * Vector<ulong>.Count;
int i;
for (i = l; i < sseIndexEnd; i += Vector<ulong>.Count)
{
var inVector = new Vector<ulong>(arrayToSum, i);
newSumVector = sumVector + inVector;
Vector<ulong> gteMask = Vector.GreaterThanOrEqual(newSumVector, sumVector); // if true then 0xFFFFFFFFFFFFFFFFL else 0L at each element of the Vector<long>
if (Vector.EqualsAny(gteMask, zeroVector))
{
for(int j = 0; j < Vector<ulong>.Count; j++)
{
if (gteMask[j] == 0) // this particular sum overflowed, since sum decreased
{
overallSum += sumVector[j];
overallSum += inVector[ j];
}
}
}
sumVector = Vector.ConditionalSelect(gteMask, newSumVector, zeroVector);
}
for (; i <= r; i++)
overallSum += arrayToSum[i];
for (i = 0; i < Vector<ulong>.Count; i++)
overallSum += sumVector[i];
return overallSum;
}
为了产生完美准确的结果,ulong[] 和 long[] 使用 SSE 求和并累加到十进制,已添加到我维护的 HPCsharp nuget 包(开源)中。 long[] 的版本在 SumParallel.cs 中,称为 SumToDecimalSseFasterInner()。
能够使用 SSE 对 long[] 或 ulong[] 数组求和,处理 SSE 中的算术溢出,这非常酷,因为 CPU 不会为 SSE 生成溢出标志,并且在SSE 速度,多核!
对于非 SSE 代码,如以下问题 (No overflow exception for int in C#?) 中所回答,在加法周围添加一个 checked
部分,在添加 Int64.MaxValue 和 1 时抛出溢出异常。但是,围绕 checked
部分的 SSE 添加似乎不会引发 long[] arrLong = new long[] { 5, 7, 16, Int64.MaxValue, 3, 1 };
的溢出异常。我相信大多数 SSE 指令都使用饱和数学,它们到达 Int64.MaxValue
并且不会超过它并且永远不会绕回负数。在 C# 中有什么方法可以为添加 SSE 抛出溢出异常,还是不可能因为 CPU 可能不支持引发溢出标志?
下面的代码显示了我使用 SSE 对 long[] 求和的 C# SSE 实现。对于上面的数组,结果是一个负数,因为正数环绕并且不会饱和,因为 C# 必须使用该版本的 SSE 指令(因为有两个版本:一个环绕,一个饱和)。不知道 C# 是否允许开发者选择使用哪个版本。下面的代码只有串行代码部分会抛出溢出异常,而SSE部分不会。
using System.Numerics;
private static long SumSseInner(this long[] arrayToSum, int l, int r)
{
var sumVector = new Vector<long>();
int sseIndexEnd = l + ((r - l + 1) / Vector<long>.Count) * Vector<long>.Count;
int i;
for (i = l; i < sseIndexEnd; i += Vector<long>.Count)
{
var inVector = new Vector<long>(arrayToSum, i);
checked
{
sumVector += inVector;
}
}
long overallSum = 0;
for (; i <= r; i++)
{
checked
{
overallSum += arrayToSum[i];
}
}
for (i = 0; i < Vector<long>.Count; i++)
{
checked
{
overallSum += sumVector[i];
}
}
return overallSum;
}
下面是在C#中使用SSE实现ulong求和。我发布了它,因为它比长的总和要短得多,也更容易理解。
private static decimal SumToDecimalSseFasterInner(this ulong[] arrayToSum, int l, int r)
{
decimal overallSum = 0;
var sumVector = new Vector<ulong>();
var newSumVector = new Vector<ulong>();
var zeroVector = new Vector<ulong>(0);
int sseIndexEnd = l + ((r - l + 1) / Vector<ulong>.Count) * Vector<ulong>.Count;
int i;
for (i = l; i < sseIndexEnd; i += Vector<ulong>.Count)
{
var inVector = new Vector<ulong>(arrayToSum, i);
newSumVector = sumVector + inVector;
Vector<ulong> gteMask = Vector.GreaterThanOrEqual(newSumVector, sumVector); // if true then 0xFFFFFFFFFFFFFFFFL else 0L at each element of the Vector<long>
if (Vector.EqualsAny(gteMask, zeroVector))
{
for(int j = 0; j < Vector<ulong>.Count; j++)
{
if (gteMask[j] == 0) // this particular sum overflowed, since sum decreased
{
overallSum += sumVector[j];
overallSum += inVector[ j];
}
}
}
sumVector = Vector.ConditionalSelect(gteMask, newSumVector, zeroVector);
}
for (; i <= r; i++)
overallSum += arrayToSum[i];
for (i = 0; i < Vector<ulong>.Count; i++)
overallSum += sumVector[i];
return overallSum;
}
为了产生完美准确的结果,ulong[] 和 long[] 使用 SSE 求和并累加到十进制,已添加到我维护的 HPCsharp nuget 包(开源)中。 long[] 的版本在 SumParallel.cs 中,称为 SumToDecimalSseFasterInner()。
能够使用 SSE 对 long[] 或 ulong[] 数组求和,处理 SSE 中的算术溢出,这非常酷,因为 CPU 不会为 SSE 生成溢出标志,并且在SSE 速度,多核!