用SIMD查找Span <ushort>中是否存在“ ushort”的最快方法?

时间:2018-11-16 17:09:17

标签: c# performance .net-core simd

在.NET Core上的C#中,我正在寻找最快的方法来检查给定的ushort值是否在Span<ushort>范围内。天真的选项包括枚举范围,但我强烈怀疑通过SIMD(即SSE或AVX)存在更快的单核选项。

这里最快的选择是什么? (可以使用不安全的密码)

1 个答案:

答案 0 :(得分:3)

一个基本实现(在应用优化之前,例如Peter在评论中描述的优化)可能会这样工作:

static unsafe bool ContainsUshort(Span<ushort> data, ushort val)
{
    int vecSize = Vector<ushort>.Count;
    var value = new Vector<ushort>(val);
    int i;
    fixed (ushort* ptr = &data[0])
    {
        int limit = data.Length - vecSize;
        for (i = 0; i <= limit; i += vecSize)
        {
            var d = Unsafe.ReadUnaligned<Vector<ushort>>(ptr + i);
            if (Vector.EqualsAny(d, value))
                return true;
        }
    }
    for (; i < data.Length; i++)
    {
        if (data[i] == val)
            return true;
    }
    return false;
}

这需要System.Runtime.CompilerServices.Unsafe包进行不安全的读取,而从跨度(或数组)创建向量的效率要低得多。通过EqualsAny而非(v)ptest来实现(v)pmovmskb内在函数,ptest通常花费更多的时间,因此将其影响最小化相对来说更为重要-但由于存在无需直接访问ptestpmovmskb的最终“条件向量” AFAIK仍然必须使用Vector.EqualsAny(向量填充0xFFFF)来完成,这还是有点愚蠢的。它在我的机器上速度更快(经过测试,其返回值将为false,因此未展开版本的退出较早没有起作用)

var allSet = new Vector<ushort>(0xFFFF);
int limit = data.Length - vecSize * 2;
for (i = 0; i <= limit; i += vecSize * 2)
{
    var d0 = Unsafe.ReadUnaligned<Vector<ushort>>(ptr + i);
    var d1 = Unsafe.ReadUnaligned<Vector<ushort>>(ptr + i + vecSize);
    var eq = Vector.Equals(d0, value) | Vector.Equals(d1, value);
    if (Vector.EqualsAny(eq, allSet))
        return true;
}