【发布时间】:2019-07-04 19:32:59
【问题描述】:
对于非 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;
}
【问题讨论】:
-
@elgonzo:不,那不是真的。该标签可用于例如当 OP 想要一个用 C# 专门表达的答案,和/或想要明确上下文是 C# 时。但是这个问题具体是关于 C# 代码抛出的溢出异常,这是 C# 语言的一个特性。但是如果 C# 语言本身没有任何特定于 SSE 的支持,为什么我们会期望它为 SSE 代码生成溢出异常呢? C# 不处理任何 SSE 操作...仅当数据交付到本身支持它的库时才会发生。
-
@elgonzo:没问题。坦率地说,如果 OP 能澄清他的情况,那将会有所帮助。鉴于 C# 本身缺乏 SSE 功能,显然他正在使用 something else 来访问 SSE 功能,并且有多种可能性。根据具体情况,也许这样的“某事”可能实际上会引发溢出异常(取决于模式和/或操作)。但是这里没有足够的细节来知道是否是这样。
-
我相信大多数 SSE 指令都使用饱和数学 不。大多数 SSE 指令都是正常的包装二进制数学,用于元素宽度从 1 到 8 字节的加/减/乘/移位. (
paddb/w/d/q)。有 有 有符号和无符号饱和版本可用于 add/sub,但仅适用于 8 and 16-bit elements),以及从 32 到 16 和 16 到 8 的饱和包,仅此而已。PMADDUBSW的横向添加也有饱和度。 -
无论如何,在大多数 SIMD 整数运算中,没有有效的硬件方法来检测有符号溢出。如果一种语言想要检查数学,if 当然可以使用 pcmpeq 和分支来模拟它,但这比在设置 OF 的标量
add指令之后插入intO或jo指令的标量等价物的开销更大有符号溢出标志。大概 C# 的checked东西只适用于标量数学,而不适用于Vector<>包装的内在运算。我不使用 C#,所以没有发布答案,但语言以这种方式工作似乎是完全合理的。 -
@PeterCordes 我想如果你真的想要检查 SIMD 添加,稳态开销是 2 条指令/添加大量添加到 检测 如果整数溢出已签名(如果未签名且