2010-09-08 38 views
3

您可以想出一些方法来优化这段代码吗?它意味着在ARMv7处理器(Iphone 3GS)中执行:优化C++代码的性能

4.0% inline float BoxIntegral(IplImage *img, int row, int col, int rows, int cols) 
     { 
0.7% float *data = (float *) img->imageData; 
1.4% int step = img->widthStep/sizeof(float); 

     // The subtraction by one for row/col is because row/col is inclusive. 
1.1% int r1 = std::min(row,   img->height) - 1; 
1.0% int c1 = std::min(col,   img->width) - 1; 
2.7% int r2 = std::min(row + rows, img->height) - 1; 
3.7% int c2 = std::min(col + cols, img->width) - 1; 

     float A(0.0f), B(0.0f), C(0.0f), D(0.0f); 
8.5% if (r1 >= 0 && c1 >= 0) A = data[r1 * step + c1]; 
11.7% if (r1 >= 0 && c2 >= 0) B = data[r1 * step + c2]; 
7.6% if (r2 >= 0 && c1 >= 0) C = data[r2 * step + c1]; 
9.2% if (r2 >= 0 && c2 >= 0) D = data[r2 * step + c2]; 

21.9% return std::max(0.f, A - B - C + D); 
3.8% } 

所有此代码取自OpenSURF库。这里的功能的情况下(有些人要求的上下文中):

//! Calculate DoH responses for supplied layer 
void FastHessian::buildResponseLayer(ResponseLayer *rl) 
{ 
    float *responses = rl->responses;   // response storage 
    unsigned char *laplacian = rl->laplacian; // laplacian sign storage 
    int step = rl->step;      // step size for this filter 
    int b = (rl->filter - 1) * 0.5 + 1;   // border for this filter 
    int l = rl->filter/3;     // lobe for this filter (filter size/3) 
    int w = rl->filter;      // filter size 
    float inverse_area = 1.f/(w*w);   // normalisation factor 
    float Dxx, Dyy, Dxy; 

    for(int r, c, ar = 0, index = 0; ar < rl->height; ++ar) 
    { 
    for(int ac = 0; ac < rl->width; ++ac, index++) 
    { 
     // get the image coordinates 
     r = ar * step; 
     c = ac * step; 

     // Compute response components 
     Dxx = BoxIntegral(img, r - l + 1, c - b, 2*l - 1, w) 
      - BoxIntegral(img, r - l + 1, c - l * 0.5, 2*l - 1, l)*3; 
     Dyy = BoxIntegral(img, r - b, c - l + 1, w, 2*l - 1) 
      - BoxIntegral(img, r - l * 0.5, c - l + 1, l, 2*l - 1)*3; 
     Dxy = + BoxIntegral(img, r - l, c + 1, l, l) 
      + BoxIntegral(img, r + 1, c - l, l, l) 
      - BoxIntegral(img, r - l, c - l, l, l) 
      - BoxIntegral(img, r + 1, c + 1, l, l); 

     // Normalise the filter responses with respect to their size 
     Dxx *= inverse_area; 
     Dyy *= inverse_area; 
     Dxy *= inverse_area; 

     // Get the determinant of hessian response & laplacian sign 
     responses[index] = (Dxx * Dyy - 0.81f * Dxy * Dxy); 
     laplacian[index] = (Dxx + Dyy >= 0 ? 1 : 0); 

#ifdef RL_DEBUG 
     // create list of the image coords for each response 
     rl->coords.push_back(std::make_pair<int,int>(r,c)); 
#endif 
    } 
    } 
} 

一些问题:
这是个好主意,该功能是内联? 会使用内联汇编提供显着的加速吗?

+5

对您的问题的单一正确答案是:措施。 – dirkgently 2010-09-08 14:02:09

+0

是的,看看最近的C++问题 - 有一个关于向量与数组的速度 - 代码显示了如何使用升压定时器进行性能分析。您也可以查看graphics.stanford.edu/~seander/bithacks.html - 这里的许多小黑客可以提供更快捷的处理方式。内联汇编 - 也许 - 我不知道那个CPU,所以不能说。 – 2010-09-08 14:16:54

+0

r1,r2,c1,c2中的任何一个为负数?那些测试应该都是多余的。 – phkahler 2010-09-08 14:45:10

回答

8

边缘专用,以便您不需要检查每行和每列。我假设这个调用是嵌套循环,并且被称为很多。此功能将变为:

inline float BoxIntegralNonEdge(IplImage *img, int row, int col, int rows, int cols) 
{ 
    float *data = (float *) img->imageData; 
    int step = img->widthStep/sizeof(float); 

    // The subtraction by one for row/col is because row/col is inclusive. 
    int r1 = row - 1; 
    int c1 = col - 1; 
    int r2 = row + rows - 1; 
    int c2 = col + cols - 1; 

    float A(data[r1 * step + c1]), B(data[r1 * step + c2]), C(data[r2 * step + c1]), D(data[r2 * step + c2]); 

    return std::max(0.f, A - B - C + D); 
} 

您摆脱了条件和分支为每个分和两个条件和每个if分支。如果您已经符合条件,则只能调用此函数 - 在调用者中检查整行的一次而不是每个像素。

我写了一些技巧,当你需要做的每一个像素工作,优化图像处理:

http://www.atalasoft.com/cs/blogs/loufranco/archive/2006/04/28/9985.aspx

从博客的其他东西:

  1. 您重新计算的位置在有2个乘法的图像数据中(索引是乘法) - 你应该增加一个指针。

  2. 不是传入img,row,row,col和cols,而是传递指向要处理的确切像素的指针 - 从增量指针而不是索引获得。

  3. 如果你不这样做,所有像素的步骤是一样的,在调用者计算并传入。如果你做1和2,你根本不需要步骤。

1

有几个地方重新使用临时变量,但它是否会提高性能,就必须测量dirkgently说:

变化

if (r1 >= 0 && c1 >= 0) A = data[r1 * step + c1]; 
    if (r1 >= 0 && c2 >= 0) B = data[r1 * step + c2]; 
    if (r2 >= 0 && c1 >= 0) C = data[r2 * step + c1]; 
    if (r2 >= 0 && c2 >= 0) D = data[r2 * step + c2]; 

if (r1 >= 0) { 
    int r1Step = r1 * step; 
    if (c1 >= 0) A = data[r1Step + c1]; 
    if (c2 >= 0) B = data[r1Step + c2]; 
    } 
    if (r2 >= 0) { 
    int r2Step = r2 * step; 
    if (c1 >= 0) C = data[r2Step + c1]; 
    if (c2 >= 0) D = data[r2Step + c2]; 
    } 

你如果你的if语句很少提供真实的话,它可能实际上最终会经常做临时多重定位。

+0

如果使用适当的优化标志,这将自动处理 – 2010-09-08 21:35:22

0

编译器可能会自动处理inling它在哪里是适当的。

没有关于上下文的任何知识。是否需要检查(r1> = 0 & & c1> = 0)?

是否需要行和列参数> 0?

float BoxIntegral(IplImage *img, int row, int col, int rows, int cols) 
{ 
    assert(row > 0 && col > 0); 
    float *data = (float*)img->imageData; // Don't use C-style casts 
    int step = img->widthStep/sizeof(float); 

    // Is the min check rly necessary? 
    int r1 = std::min(row,   img->height) - 1; 
    int c1 = std::min(col,   img->width) - 1; 
    int r2 = std::min(row + rows, img->height) - 1; 
    int c2 = std::min(col + cols, img->width) - 1; 

    int r1_step = r1 * step; 
    int r2_step = r2 * step; 

    float A = data[r1_step + c1]; 
    float B = data[r1_step + c2]; 
    float C = data[r2_step + c1]; 
    float D = data[r2_step + c2]; 

    return std::max(0.0f, A - B - C + D); 
} 
0

我不知道,如果你的问题本身借给SIMD但这可能允许您一次在图像上执行多个操作,并给你一个很好的性能提升。我假设你正在内联和优化,因为你正在多次执行操作。看一看:

  1. http://blogs.arm.com/software-enablement/coding-for-neon-part-1-load-and-stores/
  2. http://blogs.arm.com/software-enablement/coding-for-neon-part-2-dealing-with-leftovers/
  3. http://blogs.arm.com/software-enablement/coding-for-neon-part-3-matrix-multiplication/
  4. http://blogs.arm.com/software-enablement/coding-for-neon-part-4-shifting-left-and-right/

编译器确实有霓虹灯一定的支撑,如果正确的标志被启用,但您可能需要推出自己出去一些。

编辑 要获得编译器支持的霓虹灯,你将需要使用编译器标志-mfpu=neon

+0

是否有任何编译器标志明确启用氖支持? – Diego 2010-09-08 15:25:13

+0

@Diego - 参见编辑 – doron 2010-09-08 16:08:21

1

你不感兴趣的四个变量ABCD,但只有组合A - B - C + D

尝试

float result(0.0f); 
if (r1 >= 0 && c1 >= 0) result += data[r1 * step + c1]; 
if (r1 >= 0 && c2 >= 0) result -= data[r1 * step + c2]; 
if (r2 >= 0 && c1 >= 0) result -= data[r2 * step + c1]; 
if (r2 >= 0 && c2 >= 0) result += data[r2 * step + c2]; 

if (result > 0f) return result; 
return 0f; 
+0

'if(result> 0f)'。 – 2010-09-08 14:59:49

+0

@Steve:你当​​然是对的......我记得'std :: max'函数是用来确定最小值的,从那里开始我所有的推理都是倒退的。 – 2010-09-08 19:17:47

+0

+1。保存寄存器,好主意。 – MSalters 2010-09-09 09:38:27

0

一些例子说初始化ABCD直接与0跳过初始化,但是这是在某些方面要比你原来的代码功能不同。不过,我想这样做:

inline float BoxIntegral(IplImage *img, int row, int col, int rows, int cols) { 

    const float *data = (float *) img->imageData; 
    const int step = img->widthStep/sizeof(float); 

    // The subtraction by one for row/col is because row/col is inclusive. 
    const int r1 = std::min(row,   img->height) - 1; 
    const int r2 = std::min(row + rows, img->height) - 1; 
    const int c1 = std::min(col,   img->width) - 1; 
    const int c2 = std::min(col + cols, img->width) - 1; 

    const float A = (r1 >= 0 && c1 >= 0) ? data[r1 * step + c1] : 0.0f; 
    const float B = (r1 >= 0 && c2 >= 0) ? data[r1 * step + c2] : 0.0f; 
    const float C = (r2 >= 0 && c1 >= 0) ? data[r2 * step + c1] : 0.0f; 
    const float D = (r2 >= 0 && c2 >= 0) ? data[r2 * step + c2] : 0.0f; 

    return std::max(0.f, A - B - C + D); 
} 

喜欢你的原代码,这将使ABCD要么从data[]一个值,如果条件是true0.0f如果条件是假的。此外,我会(如我所示)在适当的地方使用const。许多编译器都不能根据const的性能改善代码,但它肯定不会给编译器提供有关它所操作数据的更多信息。最后,我重新排序了变量以鼓励重新使用所提取的宽度和高度。

很明显,您需要进行配置文件确定是否有任何改进。