为啥 SIMD 比蛮力慢

Posted

技术标签:

【中文标题】为啥 SIMD 比蛮力慢【英文标题】:Why SIMD is slower than brute force为什么 SIMD 比蛮力慢 【发布时间】:2014-04-26 16:43:34 【问题描述】:

也许我做错了什么,但我知道 SIMD 比标量版本慢。

我只想增加数组的值。我正在使用 Microsoft SIMD(NuGet 包 Microsoft.Bcl.Simd Prerelease)。它是 Beta 版,但它应该可以与 int 和 float 一起正常工作,但事实并非如此。

我的长凳

using System;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;

namespace ConsoleApplication58

    class Program
    
        static void Main()
        
            var r = new Random();
            var sw = Stopwatch.StartNew();
            int[] values = Enumerable.Range(0, 1000000).ToArray();
            sw.Stop();
            Console.WriteLine("TEST GENERATED IN 0", sw.Elapsed);
            int trash = 0;
            Stopwatch sw1 = new Stopwatch(), sw2 = new Stopwatch();
            for (int i = 0; i < 100; i++)
            
                sw1.Start();
                var result = SimdIncrement(values, 10);
                sw1.Stop();
                sw2.Start();
                var result2 = SimpleIncrement(values, 10);
                sw2.Stop();

                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();

                unchecked
                
                    trash ^= result[r.Next(values.Length)];
                    trash ^= result2[r.Next(values.Length)];
                
            
            Console.WriteLine("SIMD = 0", sw1.Elapsed);
            Console.WriteLine("Brute = 0", sw2.Elapsed);

            Console.WriteLine("Trash value = 0", trash);
        

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static int[] SimpleIncrement(int[] values,int inc)
        
            int[] result = new int[values.Length];
            for (int i = 0; i < result.Length; i++)
            
                result[i] = values[i] + inc;
            
            return result;
        

        [MethodImpl(MethodImplOptions.NoInlining)]
        private static int[] SimdIncrement(int[] values,int inc)
        
            int[] result = new int[values.Length];
            for (int i = 0; i < values.Length; i += 4)
            
                var vector = new Vector<int>(values, i);
                var increment = new Vector<int>(inc);
                vector += increment;
                vector.CopyTo(result, i);
            
            return result;
        
    

结果:

TEST GENERATED IN 00:00:00.0171804
SIMD = 00:00:02.1456817
Brute = 00:00:00.1576084
Trash value = 548547
Press any key . . .

【问题讨论】:

除了其他可能的错误,你应该看看simd标签的描述;特别是“用于更长的流”和“天真优化的 SIMD 代码更慢”。使用 SIMD(以及一般的并行化)会产生开销;除非您将其用于足够大的数据量,否则可能无法获得补偿。 为什么var increment = new Vector&lt;int&gt;(inc); 在循环内? VectorMath.IsHardwareAccelerated 是否为您返回 true? (不知道你是否正确设置了 RyuJIT?) 【参考方案1】:

我不想变得更加狡猾,但是这里有 SIMD 吗? Microsoft SIMD 包不会执行 SIMD 指令……它是普通的字节码。要让它使用 SIMD,你必须安装 RyuJit 并告诉它。

这是兼容模式 - MS SIMD 类包含用于正常操作的字节码。新的运行时将知道如何在不接触字节码的情况下处理它们,但您必须立即安装它(预发布)。

http://blogs.msdn.com/b/dotnet/archive/2013/09/30/ryujit-the-next-generation-jit-compiler.aspx

让我从包装中引用:

这个包中的类型是在 IL 中实现的,这允许它们 用于非 SIMD 启用的 JIT 编译器和硬件。然而,在 为了实际使用 SIMD 指令,您需要在 JIT 上运行 知道这些类型以便发出 SIMD 的编译器 指示。当前的 .NET 4.5.1 运行时没有。 .NET 代码 生成团队发布了新 JIT 的 CTP,代号为 “RyuJIT”。 CTP 在为 x64 编译时添加了 SIMD 支持。

【讨论】:

【参考方案2】:

您的 SIMD 版本应更改为基于实向量的加法:

[MethodImpl(MethodImplOptions.NoInlining)]
private static int[] simdIncrement(int[] values, int inc)
    
    var vector = new Vector<int>(values);
    var vectorAddResults = vector + new Vector<int>(inc);

    var result = new int[values.Length];
    vectorAddResults.CopyTo(result);
    return result;

【讨论】:

以上是关于为啥 SIMD 比蛮力慢的主要内容,如果未能解决你的问题,请参考以下文章

为啥 OpenMP 'simd' 比 'parallel for simd' 有更好的性能?

为啥内核中不使用 SIMD 指令?

为啥并行 SIMD/SSE/AVX 需要置换?

为啥访问单个 SIMD 元素这么慢

为啥 strchr 比我的 simd 代码快两倍

为啥此 SIMD 代码运行速度比等效标量慢?