使用 SIMD 查找 Span<ushort> 中是否存在 'ushort' 的最快方法?
Fastest way to find if a 'ushort' is present within a Span<ushort> with SIMD?
在 .NET Core 上的 C# 中,我正在寻找最快的方法来检查给定的 ushort
值是否存在于 Span<ushort>
范围内。天真的选项包括枚举跨度,但我强烈怀疑通过 SIMD(即 SSE 或 AVX)存在更快的单核选项。
这里最快的选项是什么? (不安全的代码是可以的)
基本实现(在应用优化之前,例如 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
通常会花费更多微操作,因此相对而言,将其影响最小化更为重要 - 但由于有无法直接访问 ptest
或 pmovmskb
最终的 "vector to condition" AFAIK 仍然必须使用 Vector.EqualsAny
(使用填充有 0xFFFF 的向量)完成,这有点傻..不过它在我的机器上要快一点(经过测试,return 的值将是 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;
}
在 .NET Core 上的 C# 中,我正在寻找最快的方法来检查给定的 ushort
值是否存在于 Span<ushort>
范围内。天真的选项包括枚举跨度,但我强烈怀疑通过 SIMD(即 SSE 或 AVX)存在更快的单核选项。
这里最快的选项是什么? (不安全的代码是可以的)
基本实现(在应用优化之前,例如 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
通常会花费更多微操作,因此相对而言,将其影响最小化更为重要 - 但由于有无法直接访问 ptest
或 pmovmskb
最终的 "vector to condition" AFAIK 仍然必须使用 Vector.EqualsAny
(使用填充有 0xFFFF 的向量)完成,这有点傻..不过它在我的机器上要快一点(经过测试,return 的值将是 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;
}