System.Numerics.Vector< T>.NET Framework的初始化性能 [英] System.Numerics.Vector<T> Initialization Performance on .NET Framework

查看:47
本文介绍了System.Numerics.Vector< T>.NET Framework的初始化性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

System.Numerics.Vector为.NET Core和.NET Framework带来了SIMD支持.它适用于.NET Framework 4.6+和.NET Core.

System.Numerics.Vector brings SIMD support to .NET Core and .NET Framework. It works on .NET Framework 4.6+ and .NET Core.

// Baseline
public void SimpleSumArray() 
{
    for (int i = 0; i < left.Length; i++)
        results[i] = left[i] + right[i];
}

// Using Vector<T> for SIMD support
public void SimpleSumVectors() 
{
    int ceiling = left.Length / floatSlots * floatSlots;
    
    for (int i = 0; i < ceiling; i += floatSlots)
    {
        Vector<float> v1 = new Vector<float>(left, i);
        Vector<float> v2 = new Vector<float>(right, i);
        (v1 + v2).CopyTo(results, i);
    }
    for (int i = ceiling; i < left.Length; i++)
    {
        results[i] = left[i] + right[i];
    }
}

不幸的是,Vector的初始化可能是限制步骤.要解决此问题,一些资源建议使用MemoryMarshal将源数组转换为Vectors [1] [2]的数组.例如:

Unfortunately, the initialization of the Vector can be the limiting step. To work around this, several sources recommend using MemoryMarshal to transform the source array into an array of Vectors [1][2]. For example:

// Improving Vector<T> Initialization Performance
public void SimpleSumVectorsNoCopy() 
{
    int numVectors = left.Length / floatSlots;
    int ceiling = numVectors * floatSlots;
    // leftMemory is simply a ReadOnlyMemory<float> referring to the "left" array
    ReadOnlySpan<Vector<float>> leftVecArray = MemoryMarshal.Cast<float, Vector<float>>(leftMemory.Span);
    ReadOnlySpan<Vector<float>> rightVecArray = MemoryMarshal.Cast<float, Vector<float>>(rightMemory.Span);
    Span<Vector<float>> resultsVecArray = MemoryMarshal.Cast<float, Vector<float>>(resultsMemory.Span);
    for (int i = 0; i < numVectors; i++)
        resultsVecArray[i] = leftVecArray[i] + rightVecArray[i];
}

当在.NET Core上运行时,这会显着提高性能 :

This brings a dramatic improvement in performance when running on .NET Core:

|                 Method |      Mean |     Error |    StdDev |
|----------------------- |----------:|----------:|----------:|
|         SimpleSumArray | 165.90 us | 0.1393 us | 0.1303 us |
|       SimpleSumVectors |  53.69 us | 0.0473 us | 0.0443 us |
| SimpleSumVectorsNoCopy |  31.65 us | 0.1242 us | 0.1162 us |

不幸的是,在 .NET Framework 上,这种初始化向量的方式具有相反的效果.实际上会导致性能下降:

Unfortunately, on .NET Framework, this way of initializing the vector has the opposite effect. It actually leads to worse performance:

|                 Method |      Mean |    Error |   StdDev |
|----------------------- |----------:|---------:|---------:|
|         SimpleSumArray | 152.92 us | 0.128 us | 0.114 us |
|       SimpleSumVectors |  52.35 us | 0.041 us | 0.038 us |
| SimpleSumVectorsNoCopy |  77.50 us | 0.089 us | 0.084 us |

是否有一种方法可以优化.NET Framework上Vector的初始化,并获得与.NET Core类似的性能?使用此示例应用程序[1]进行了测量.

Is there a way to optimize the initialization of Vector on .NET Framework and get similar performance to .NET Core? Measurements have been performed using this sample application [1].

[1] https://github.com/CBGonzalez/SIMDPerformance

[2] https://stackoverflow.com/a/62702334/430935

推荐答案

据我所知,在.NET Framework 4.6或4.7中加载矢量的唯一有效方法(大概在5.0中将全部更改)是不安全的代码,例如使用 Unsafe.Read< Vector< float>> (或其适用的非变体形式):

As far as I know, the only efficient way to load a vector in .NET Framework 4.6 or 4.7 (presumably this will all change in 5.0) is with unsafe code, for example using Unsafe.Read<Vector<float>> (or its unaliged variant if applicable):

public unsafe void SimpleSumVectors()
{
    int ceiling = left.Length / floatSlots * floatSlots;

    fixed (float* leftp = left, rightp = right, resultsp = results)
    {
        for (int i = 0; i < ceiling; i += floatSlots)
        {
            Unsafe.Write(resultsp + i, 
                Unsafe.Read<Vector<float>>(leftp + i) + Unsafe.Read<Vector<float>>(rightp + i));
        }
    }
    for (int i = ceiling; i < left.Length; i++)
    {
        results[i] = left[i] + right[i];
    }
}

这使用了您可以通过NuGet获得的 System.Runtime.CompilerServices.Unsafe 包,但也可以通过不使用它来完成.

This uses the System.Runtime.CompilerServices.Unsafe package which you can get via NuGet, but it could be done without that too.

这篇关于System.Numerics.Vector&lt; T&gt;.NET Framework的初始化性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆