Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么大型阵列的C#SIMD性能增益比小型阵列低? 我一直在深度学习lib库,一个人写。在矩阵运算中,获得最佳性能是我的关键。我一直在研究编程语言及其在数值运算中的性能。过了一会儿,我发现C#SIMD与C++SIMD的性能非常相似。因此,我决定用C#编写这个库_C#_Performance_Vectorization_Simd_Memory Bandwidth - Fatal编程技术网

为什么大型阵列的C#SIMD性能增益比小型阵列低? 我一直在深度学习lib库,一个人写。在矩阵运算中,获得最佳性能是我的关键。我一直在研究编程语言及其在数值运算中的性能。过了一会儿,我发现C#SIMD与C++SIMD的性能非常相似。因此,我决定用C#编写这个库

为什么大型阵列的C#SIMD性能增益比小型阵列低? 我一直在深度学习lib库,一个人写。在矩阵运算中,获得最佳性能是我的关键。我一直在研究编程语言及其在数值运算中的性能。过了一会儿,我发现C#SIMD与C++SIMD的性能非常相似。因此,我决定用C#编写这个库,c#,performance,vectorization,simd,memory-bandwidth,C#,Performance,Vectorization,Simd,Memory Bandwidth,首先,我测试了C#SIMD(我测试了很多东西,但不打算在这里写)。我注意到当使用更小的数组时,它的效果要好得多。使用较大阵列时,效率不好。我认为这很可笑。通常情况下,事情越大,效率越高 我的问题是“为什么在C#中使用更大的数组时矢量化工作较慢?” 我将使用BenchmarkNet分享基准测试(由我自己完成) 如您所见,我将大小增加1000倍,这意味着将数组的大小增加1000000倍P2最初为154纳秒。在第二次测试中,耗时170毫秒,这是我们预期的1000多倍。另外,P3的速度是以前的1000倍

首先,我测试了C#SIMD(我测试了很多东西,但不打算在这里写)。我注意到当使用更小的数组时,它的效果要好得多。使用较大阵列时,效率不好。我认为这很可笑。通常情况下,事情越大,效率越高

我的问题是“为什么在C#中使用更大的数组时矢量化工作较慢?”

我将使用BenchmarkNet分享基准测试(由我自己完成)

如您所见,我将大小增加1000倍,这意味着将数组的大小增加1000000倍P2最初为154纳秒。在第二次测试中,耗时170毫秒,这是我们预期的1000多倍。另外,P3的速度是以前的1000倍(100ns-100ms),但是,我想在这里指出的是,矢量化循环的P1的性能明显低于以前。我想知道为什么

请注意,P3与本主题无关。P1是P2的矢量化版本。所以,我们可以说矢量化的效率是P2/P1。我的代码如下:

矩阵类:

public sealed class Matrix1
{
    public float[] Array;
    public int D1, D2;
    const int size = 110000000;
    private static ArrayPool<float> sizeAwarePool = ArrayPool<float>.Create(size, 100);

    public Matrix1(int d1, int d2)
    {
        D1 = d1;
        D2 = d2;
        if(D1*D2 > size)
        { throw new Exception("Size!"); }
        Array = sizeAwarePool.Rent(D1 * D2);
    }

    bool Deleted = false;
    public void Dispose()
    {
        sizeAwarePool.Return(Array);
        Deleted = true;
    }

    ~Matrix1()
    {
        if(!Deleted)
        {
            throw new Exception("Error!");
        }
    }

    public float this[int x, int y]
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get
        {
            return Array[x * D2 + y];
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        set
        {
            Array[x * D2 + y] = value;
        }
    }
}
公共密封类Matrix1
{
公共浮点数[]数组;
公共int D1、D2;
常数int size=110000000;
私有静态ArrayPool sizeAwarePool=ArrayPool.Create(大小,100);
公共矩阵1(整数d1,整数d2)
{
D1=D1;
D2=D2;
如果(D1*D2>尺寸)
{抛出新异常(“大小!”;}
数组=sizeAwarePool.Rent(D1*D2);
}
bool Deleted=false;
公共空间处置()
{
sizeAwarePool.Return(数组);
删除=真;
}
~x1()
{
如果(!已删除)
{
抛出新异常(“错误!”);
}
}
公共浮动本[int x,int y]
{
[MethodImpl(MethodImplOptions.AggressiveInline)]
得到
{
返回数组[x*D2+y];
}
[MethodImpl(MethodImplOptions.AggressiveInline)]
设置
{
数组[x*D2+y]=值;
}
}
}
课程类别:

public class Program
{
    const int Size = 10000;

    [Benchmark]
    public void P1()
    {
        Matrix1 a = Program.a, b = Program.b, c = Program.c;
        int sz = Vector<float>.Count;
        for (int i = 0; i < Size * Size; i += sz)
        {
            var v1 = new Vector<float>(a.Array, i);
            var v2 = new Vector<float>(b.Array, i);
            var v3 = v1 + v2;
            v3.CopyTo(c.Array, i);
        }

    }

    [Benchmark]
    public void P2()
    {
        Matrix1 a = Program.a, b = Program.b, c = Program.c;
        for (int i = 0; i < Size; i++)
            for (int j = 0; j < Size; j++)
                c[i, j] = a[i, j] + b[i, j];
    }
    [Benchmark]
    public void P3()
    {
        Matrix1 a = Program.a;
        for (int i = 0; i < Size; i++)
            for (int j = 0; j < Size; j++)
                a[i, j] = i + j - j; 
                //could have written a.Array[i*size + j] = i + j
                //but it would have made no difference in terms of performance.
                //so leave it that way
    }


    public static Matrix1 a = new Matrix1(Size, Size);
    public static Matrix1 b = new Matrix1(Size, Size);
    public static Matrix1 c = new Matrix1(Size, Size);

    static void Main(string[] args)
    {
        for (int i = 0; i < Size; i++)
            for (int j = 0; j < Size; j++)
                a[i, j] = i;
        for (int i = 0; i < Size; i++)
            for (int j = 0; j < Size; j++)
                b[i, j] = j;
        for (int i = 0; i < Size; i++)  
            for (int j = 0; j < Size; j++)
                c[i, j] = 0;

        var summary = BenchmarkRunner.Run<Program>();
        a.Dispose();
        b.Dispose();
        c.Dispose();
    }
}     
公共类程序
{
常数int Size=10000;
[基准]
公共图书馆P1(
{
矩阵1 a=程序a,b=程序b,c=程序c;
int sz=向量计数;
对于(int i=0;i

我向您保证,
x[I,j]
不会影响性能。与使用
x.Array[i*Size+j]

相同,这可能不是全部原因:他们使用锯齿阵列将P1从140毫秒加速到120毫秒

所以,在这个大箱子里,可能有什么额外的东西阻碍了它。我会使用性能计数器来调查和检查
ld\u块\u部分。地址\u别名
(4k别名->加载对存储的错误依赖)。和/或查看从C#分配器获得的内存地址,也许可以查看它们是否接近但不完全相同相对于4k边界的对齐方式

我不认为在同一集中需要3条热缓存线会是一个问题;L1d在任何CPU上都是8路关联的,使用AVX(即,使用256位加载/存储和ALU)可以提供大于4倍的加速。但是,如果您的所有阵列相对于4k边界具有相同的对齐方式,那么当您访问相同的索引时,它们都将在32kiB L1d缓存中为相同的集合别名

哦,这里有一个理论:锯齿状数组错开了页面行走,而不是所有3个流(2个src 1个dst)同时到达新页面,并且都有需要行走的TLB未命中。尝试确保您的代码使用2M大页面,而不仅仅是4k,以减少TLB未命中。(例如,在Linux上,您将使用系统调用。)

检查性能计数器事件是否有
dtlb\u加载\u未命中。未命中导致行走和/或
dtlb\u加载\u未命中。stlb\u命中
。有TLB预回迁功能,因此将它们交错放置可以使TLB预回迁功能在一个或两个页面上并行工作,而不是同时执行所有3个页面漫游。

public class Program { const int Size = 10000; [Benchmark] public void P1() { Matrix1 a = Program.a, b = Program.b, c = Program.c; int sz = Vector<float>.Count; for (int i = 0; i < Size * Size; i += sz) { var v1 = new Vector<float>(a.Array, i); var v2 = new Vector<float>(b.Array, i); var v3 = v1 + v2; v3.CopyTo(c.Array, i); } } [Benchmark] public void P2() { Matrix1 a = Program.a, b = Program.b, c = Program.c; for (int i = 0; i < Size; i++) for (int j = 0; j < Size; j++) c[i, j] = a[i, j] + b[i, j]; } [Benchmark] public void P3() { Matrix1 a = Program.a; for (int i = 0; i < Size; i++) for (int j = 0; j < Size; j++) a[i, j] = i + j - j; //could have written a.Array[i*size + j] = i + j //but it would have made no difference in terms of performance. //so leave it that way } public static Matrix1 a = new Matrix1(Size, Size); public static Matrix1 b = new Matrix1(Size, Size); public static Matrix1 c = new Matrix1(Size, Size); static void Main(string[] args) { for (int i = 0; i < Size; i++) for (int j = 0; j < Size; j++) a[i, j] = i; for (int i = 0; i < Size; i++) for (int j = 0; j < Size; j++) b[i, j] = j; for (int i = 0; i < Size; i++) for (int j = 0; j < Size; j++) c[i, j] = 0; var summary = BenchmarkRunner.Run<Program>(); a.Dispose(); b.Dispose(); c.Dispose(); } }