在.NET Core上的C#中,我正在寻找最快的方法来检查给定的ushort
值是否在Span<ushort>
范围内。天真的选项包括枚举范围,但我强烈怀疑通过SIMD(即SSE或AVX)存在更快的单核选项。
这里最快的选择是什么? (可以使用不安全的密码)
答案 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
通常花费更多的时间,因此将其影响最小化相对来说更为重要-但由于存在无需直接访问ptest
或pmovmskb
的最终“条件向量” 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;
}