Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/343.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
Python C语言中的快速二维卷积_Python_C_Algorithm_Performance_Optimization - Fatal编程技术网

Python C语言中的快速二维卷积

Python C语言中的快速二维卷积,python,c,algorithm,performance,optimization,Python,C,Algorithm,Performance,Optimization,我正在尝试用Python实现一个卷积神经网络。最初,我使用scipy.signal的convolve2d函数进行卷积,但它有很多开销,而且只需用C实现我自己的算法并从python调用它会更快,因为我知道我的输入是什么样子的 我实现了两个功能: 用不可分核卷积矩阵 用可分离内核卷积矩阵(目前我假设python在将矩阵传递到C之前进行秩检查和拆分) 这两个函数都没有填充,因为我需要降维 不可分二维卷积 与scipy.signal的convolve2d相比,这仍然是一个相当大的改进,同样的操作大约需要

我正在尝试用Python实现一个卷积神经网络。最初,我使用scipy.signal的convolve2d函数进行卷积,但它有很多开销,而且只需用C实现我自己的算法并从python调用它会更快,因为我知道我的输入是什么样子的

我实现了两个功能:

  • 用不可分核卷积矩阵
  • 用可分离内核卷积矩阵(目前我假设python在将矩阵传递到C之前进行秩检查和拆分)
  • 这两个函数都没有填充,因为我需要降维

    不可分二维卷积 与scipy.signal的convolve2d相比,这仍然是一个相当大的改进,同样的操作大约需要2秒,但我需要更高的速度,因为我将调用此函数数千次。将数据类型更改为float目前不是一个选项,尽管这会导致相当大的加速

    有没有办法进一步优化这些算法?我可以应用任何缓存技巧或例程来加速它吗


    任何建议都会被赏识。

    如果你在X86上运行,那么只考虑使用SSE或AVX-SIMD优化。对于

    double
    数据,吞吐量的提高将是适度的,但是如果您可以切换到
    float
    ,那么您可以使用SSE实现大约4倍的提高,或者使用AVX实现8倍的提高。关于StackOverflow这个主题,已经有很多问题和答案,您可以从中获得一些关于实现的想法。另外,也有许多可用的库,其中包括高性能2D卷积(滤波)例程,这些库通常利用SIMD来提高性能,例如Intel的IPP(商用)或OpenCV(免费)

    另一种可能是利用多个核心-将图像分割成块,并在其自己的线程中运行每个块。例如,如果您有一个4核CPU,则将图像分割为4个块。(见附件)

    当然,如果您真的想充分优化此操作,您可以将上述两种想法结合起来


    可以应用于当前代码和任何未来实现(例如SIMD)的一些小优化:

    • 如果内核是对称的(或奇数对称的),那么可以通过添加(减去)对称输入值并执行一次乘法而不是两次乘法来减少操作数

    • 对于可分离的情况,不是分配一个完整的帧临时缓冲区,而是考虑使用一个“条带挖掘”方法——分配一个更小的缓冲器,它是全宽度的,但是相对较少的行数,然后在“条”中处理图像,交替地应用水平内核和垂直内核。这样做的好处是,您有一个更友好的缓存访问模式和更小的内存占用


    关于编码风格的几点意见:

    • register
      关键字多年来一直是多余的,如果你试图使用它,现代编译器会发出警告-通过舍弃它来节省一些噪音(和一些打字)

    • 在C中铸造
      malloc
      的结果是不受欢迎的-这是一个错误

    • 使任何输入参数
      const
      (即只读)并对任何永远不会出现别名的参数使用
      restrict
      (例如
      a
      result
      )-这不仅有助于避免编程错误(至少在
      const
      的情况下),但在某些情况下,它可以帮助编译器生成更好的优化代码(特别是在潜在的别名指针的情况下)


    谢谢!我必须仔细阅读SSE,因为我以前从未使用过它。您是否也知道可以在此处应用的BLAS/LAPACK例程?不是BLAS/LAPACK,但您可能希望查看其他库,例如Intel的IPP(商业版)或OpenCV(免费版)。因此,我必须使用SSE显式编写代码吗?我环顾四周,似乎gcc会自动生成带有-O3标志集的SSE代码。gcc和其他编译器有时会自动向量化-甚至可能您的代码已经被向量化了(检查生成的代码)。人类通常可以做得更好,尤其是当编译器根本无法矢量化时。“用C实现我自己的算法”-是的,如果你做对了,那可能会非常快。“将其移植到python”-等等,什么?移植它?我希望您在这里使用了错误的术语,因为Python端口会非常慢。优化Python与优化C非常不同。次要的一点是:而不是
    double*tmp=(double*)malloc(IMG_DIM*RESULT_DIM*sizeof(double)),建议
    double*tmp=malloc(sizeof*tmp*IMG\u DIM*RESULT\u DIM)1)cast不需要2)
    *tmp
    double
    更易于维护3)没有
    int
    溢出问题。您是否尝试过使用gcc选项
    -march
    -mtune
    ?@user2357112我所说的“移植”不是Python中代码的重复,我只是通过某种接口调用C代码,喜欢类型。正确的术语是什么?参见
    // a - 2D matrix (as a 1D array), w - kernel
    double* conv2(double* a, double* w, double* result)
    {
        register double acc;
        register int i; 
        register int j;
        register int k1, k2;
        register int l1, l2;
        register int t1, t2;
    
        for(i = 0; i < RESULT_DIM; i++) 
        {
            t1 = i * RESULT_DIM; // loop invariants
            for(j = 0; j < RESULT_DIM; j++) 
            {   
                acc = 0.0;
                for(k1 = FILTER_DIM - 1, k2 = 0; k1 >= 0; k1--, k2++)
                {
                    t2 = k1 * FILTER_DIM;  // loop invariants
                    for(l1 = FILTER_DIM - 1, l2 = 0; l1 >= 0; l1--, l2++)
                    {
                        acc += w[t2 + l1] * a[(i + k2) * IMG_DIM + (j + l2)];
                    }
                }
                result[t1 + j] = acc;
            }
        }
    
        return result;
    }
    
    // a - 2D matrix, w1, w2 - the separated 1D kernels
    double* conv2sep(double* a, double* w1, double* w2, double* result)
    {
        register double acc;
        register int i; 
        register int j;
        register int k1, k2;
        register int t;
        double* tmp = (double*)malloc(IMG_DIM * RESULT_DIM * sizeof(double));
    
        for(i = 0; i < RESULT_DIM; i++) // convolve with w1 
        {
            t = i * RESULT_DIM;
            for(j = 0; j < IMG_DIM; j++)
            {
                acc = 0.0;
                for(k1 = FILTER_DIM - 1, k2 = 0; k1 >= 0; k1--, k2++)
                {
                    acc += w1[k1] * a[k2 * IMG_DIM + t + j];
                }
                tmp[t + j] = acc;
            }
        }
    
        for(i = 0; i < RESULT_DIM; i++) // convolve with w2
        {
            t = i * RESULT_DIM;
            for(j = 0; j < RESULT_DIM; j++)
            {
                acc = 0.0;
                for(k1 = FILTER_DIM - 1, k2 = 0; k1 >= 0; k1--, k2++)
                {
                    acc += w2[k1] * tmp[t + (j + k2)];
                }
    
                result[t + j] = acc;
            }
        }
    
        free(tmp);
        return result;
    }
    
    271.21900 ms
    127.32000 ms