c# - 使用 Vector<T> 的 SIMD 向量化 C# 代码运行速度比经典循环慢

标签 c# .net vector vectorization benchmarking

我看过一些描述 Vector<T> 的文章支持 SIMD 并使用 JIT 内在函数实现,因此编译器在使用它时将正确输出 AVS/SSE/... 指令,允许比经典线性循环(示例 here)更快的代码。

我决定尝试重写一个方法,我必须看看我是否设法获得了一些加速,但到目前为止我失败了,矢量化代码的运行速度比原始代码慢 3 倍,我不确定为什么。这里有两个版本的方法检查是否两个 Span<float>实例具有相同位置的所有项目对,它们共享相对于阈值的相同位置。

// Classic implementation
public static unsafe bool MatchElementwiseThreshold(this Span<float> x1, Span<float> x2, float threshold)
{
    fixed (float* px1 = &x1.DangerousGetPinnableReference(), px2 = &x2.DangerousGetPinnableReference())
        for (int i = 0; i < x1.Length; i++)
            if (px1[i] > threshold != px2[i] > threshold)
                return false;
    return true;
}

// Vectorized
public static unsafe bool MatchElementwiseThresholdSIMD(this Span<float> x1, Span<float> x2, float threshold)
{
    // Setup the test vector
    int l = Vector<float>.Count;
    float* arr = stackalloc float[l];
    for (int i = 0; i < l; i++)
        arr[i] = threshold;
    Vector<float> cmp = Unsafe.Read<Vector<float>>(arr);
    fixed (float* px1 = &x1.DangerousGetPinnableReference(), px2 = &x2.DangerousGetPinnableReference())
    {
        // Iterate in chunks
        int
            div = x1.Length / l,
            mod = x1.Length % l,
            i = 0,
            offset = 0;
        for (; i < div; i += 1, offset += l)
        {
            Vector<float>
                v1 = Unsafe.Read<Vector<float>>(px1 + offset),
                v1cmp = Vector.GreaterThan<float>(v1, cmp),
                v2 = Unsafe.Read<Vector<float>>(px2 + offset),
                v2cmp = Vector.GreaterThan<float>(v2, cmp);
            float*
                pcmp1 = (float*)Unsafe.AsPointer(ref v1cmp),
                pcmp2 = (float*)Unsafe.AsPointer(ref v2cmp);
            for (int j = 0; j < l; j++)
                if (pcmp1[j] == 0 != (pcmp2[j] == 0))
                    return false;
        }

        // Test the remaining items, if any
        if (mod == 0) return true;
        for (i = x1.Length - mod; i < x1.Length; i++)
            if (px1[i] > threshold != px2[i] > threshold)
                return false;
    }
    return true;
}

正如我所说,我已经使用 BenchmarkDotNet 测试了两个版本,以及使用 Vector<T> 的版本。运行速度比另一个慢 3 倍左右。我尝试使用不同长度的跨度(从大约 100 到超过 2000)运行测试,但矢量化方法一直比另一种方法慢得多。

我是否遗漏了一些明显的东西?

谢谢!

编辑: 我使用不安全代码并尝试在不并行化的情况下尽可能优化此代码的原因是此方法已从 Parallel.For 中调用。迭代。

此外,具有在多个线程上并行化代码的能力通常不是不优化单个并行任务的好理由。

最佳答案

我遇到了同样的问题。解决方案是取消选中项目属性中的首选 32 位 选项。

SIMD is only enabled for 64-bit processes. So make sure your app either is targeting x64 directly or is compiled as Any CPU and not marked as 32-bit preferred. [Source]

关于c# - 使用 Vector<T> 的 SIMD 向量化 C# 代码运行速度比经典循环慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48198734/

相关文章:

c++ - 在 C++ 中返回字符串和不同 vector 数据类型的映射

c# - .NET PInvoke 在 Linux 和 Mac OS X 平台上可用吗?

c# - CLR 中的派生类能否拦截/隐藏父类的方法,同时仍保持智能感知?

c# - 如何清除 javascript 中 OnUnload 事件中的 asp.net session 值

c# - .net 项目和设计器文件的属性错误被锁定

.net - EF Core 使用类库项目和 DB 脚手架从用户 secret 中读取连接字符串

serialization - Data.Vector.Binary 与 Binary [a] 实例重叠

vector - 如何计算3d球体的速度顶点 - 爆炸(粒子系统中)

c# - 在服务器上运行时如何调试 .cs 代码文件?

c# - 如何在不使调用链异步的情况下调用 ApplicationTokenProvider?