我可以用Vector <t>检查算术

时间:2016-09-25 20:17:31

标签: .net vector simd system.numerics

我一直在尝试使用Vector来使用HW来并行化整数运算。有没有办法用向量运算启用溢出检查?

一个例子是将两列(等长阵列)的int一起添加。此处c=a+b表示c[0] = a[0] + b[0]c[1] = a[1] + b[1]等。

我想我可以这样做:

overflow[i] = b[i] >= 0 ? c[i] < a[i] : c[i] >= a[i];

但是这个(分支)可能比.Net的自动溢出检查慢,并且可能会否定使用Vector<T>的性能优势。

我们还想优化我们最常用的操作:乘法,减法,在较小程度上整数除法。

编辑:我想到了这个,并提出了这个,这是未经检查的矢量添加速度的2.5倍。似乎有很多额外的开销。

    public Vector<int> Calc(Vector<int> a, Vector<int> b)
    {
        var result = a + b;
        var overflowFlag = Vector.GreaterThan(b, Vector<int>.Zero) * Vector.LessThan(result,a)
            + Vector.LessThan(b,Vector<int>.Zero) * Vector.GreaterThan(result, a);

        // It makes no sense to add the flags to the result, but haven't decided what to do with them yet, 
        // and don't want the compiler to optimise the overflow calculation away
        return result + overflowFlag;
    }

计时:( 4k次迭代添加一对100k阵列)

  • 正常添加:618ms
  • 正常已检查添加:1092ms
  • Vector Add:208ms
  • Vector Checked Add:536ms

1 个答案:

答案 0 :(得分:1)

使用从Hacker的Delight借来的一些技巧(第2章,溢出检测部分),这里有一些溢出谓词(未经测试):

签名补充:

var sum = a + b;
var ovf = (sum ^ a) & (sum ^ b);

结果是标志,而不是完整的面具。也许这已经足够了,也许不是,在这种情况下,我通常会推荐一个正确的转变,但Vector<T>没有正确的转变(缺少太多的东西)。你可以比较零。

无符号加法:为了完整性?

var sum = a + b;
var ovf = Vector.LessThan(sum, a);

乘:

据我所知,没有合理的方法可以做到。即使在原生SSE中它也有点烦人,但是pmuldq和一些改组它并不太糟糕。
在C#SIMD中,这似乎毫无希望。没有高mul(除了16位整数也没有原始SSE,也很烦人),没有加宽乘法(并且无论如何都无法缩小结果),也没有合理的方法可以提前扩大。即使你可以加宽(他们可以把它添加到API中,但是很认真),multiplying 64bit integers with SSE is annoying,所以使用标量算法做这件事并不是一件坏事,这会让人失望。

所以我建议不要在SIMD中这样做,至少不要在C#中。

这并不意味着您使用内置溢出检测。虽然溢出是一个致命错误,但这是合适的,如果它是常见的和预期的,那么它会是灾难性的慢,你只想在布尔标志中出现溢出状态。在这种情况下,您可以使用:

签名乘法:

long ext_prod = (long)a * b;
int prod = (int)ext_prod;
bool ovf = (prod >> 31) != (int)(ext_prod >> 32);

无符号乘法:

ulong ext_prod = (ulong)a * b;
uint prod = (uint)ext_prod;
bool ovf = (ext_prod >> 32) != 0;

在SIMD中它的工作方式基本相同,即测试高半部分是否填充了符号的副本(在无符号的情况下定义为零),但扩展使得它在本机SIMD中很烦人而在C#中无望SIMD。

相关问题