【问题标题】:C optimzation techniquesC 优化技术
【发布时间】:2013-10-23 05:08:37
【问题描述】:

我正在从以下函数中寻找最快的优化,而无需进入汇编,因为它似乎是我的应用程序的瓶颈。请记住,以下函数已被内联声明。

定义:P = 10 和 N = 240

void autocorrelation( int32_t *data , float *r){
    for ( int m=0 ; m < P+1 ; m++)
    {
        register float temp = 0;
        for ( int n=0 ; n<N-m ; n++)
        {
            temp += (float)(data[n])*(float)(data[n+m]);
        }
        r[m] = temp;
    }
}

感谢任何帮助。

谢谢!

编辑:

组装:

temp += (float)(data[n])*(float)(data[n+m]);
800063A8  lddsp R8, SP[0x0]      
800063AA  add R1, R2, R8<<0      
800063AE  ld.w R12, R1[R7<<0]        
800063B2  mcall 0x80006f58       
800063B6  mov R4, R12        
800063B8  ld.w R12, R2[R7<<0]        
800063BC  mcall 0x80006f58       
800063C0  mov R11, R12       
800063C2  mov R12, R4        
800063C4  mcall 0x80006f5c       
800063C8  mov R11, R12       
800063CA  mov R12, R5        
800063CC  mcall 0x80006f60       
800063D0  mov R5, R12        
for ( int n=0 ; n<N-m ; n++)
800063D2  sub R6, -1         
800063D4  sub R7, -4         
800063D6  cp.w R6, R3        
800063D8  brne 0x800063ae        
r[m] = temp;
800063DA  ld.w R10, PC[2954]         
800063DE  lddsp R9, SP[0x0]      
800063E0  st.w R10[R9<<0], R5        
800063E4  sub R0, 1      
800063E6  sub R9, -4         
800063E8  stdsp SP[0x0], R9      
for ( int m=0 ; m < P+1 ; m++)
800063EA  cp.w R0, 229       
800063EE  breq 0x800063fc        
800063F0  mov R3, R0         
for ( int n=0 ; n<N-m ; n++)
800063F2  cp.w R0, 0         
800063F4  brgt 0x800063a2        
800063F8  mov R5, 0      
800063FA  rjmp 0x800063da

/////////////////////////////////////// /////////////////////////////

所以我把代码改成:

void autocorrelation( float *data , float *r){
    for ( int m=0 ; m < P+1 ; m++)
    {
        register float temp = 0;
        for ( int n=0 ; n<N-m ; n++)
        {
            temp += data[n]*data[n+m];
        }
        r[m] = temp;
    }
}

并将时间缩短三分之一(每个刻度为 1/16000Hz)- 最初 - 108 滴答声 现在 - 70 滴答声

新程序集:

temp += data[n]*data[n+m];
800063C2  add R2, R3, R0<<0      
800063C6  ld.w R11, R3[R7<<0]        
800063CA  ld.w R12, R2[R7<<0]        
800063CE  mcall 0x80006f68       
800063D2  mov R11, R12       
800063D4  mov R12, R5        
800063D6  mcall 0x80006f6c       
800063DA  mov R5, R12        
for ( int n=0 ; n<N-m ; n++)
800063DC  sub R6, -1         
800063DE  sub R7, -4         
800063E0  cp.w R6, R4        
800063E2  brne 0x800063c6        
r[m] = temp;
800063E4  ld.w R9, PC[2960]      
800063E8  st.w R9[R0<<0], R5         
800063EC  sub R1, 1      
800063EE  sub R0, -4         
for ( int m=0 ; m < P+1 ; m++)
800063F0  cp.w R1, 229       
800063F4  breq 0x80006402        
800063F6  mov R4, R1         
for ( int n=0 ; n<N-m ; n++)
800063F8  cp.w R1, 0         
800063FA  brgt 0x800063bc        
800063FE  mov R5, 0      
80006400  rjmp 0x800063e4   

/////////////////////////////////////// /////// FINAL 最终解决方案:(再次更改)

我结合了标记的解决方案,并将循环与我编写的应用程序展开并保持在 64 位,直到最后,性能从 60ticks 增加到 20ticks。超过 6 个具有相同调整的功能,我能够从一开始似乎优化的代码从 250 个滴答声缩短到 50 个滴答声,我的乒乓缓冲区需要在 160 个滴答声内完成所有事情,所以我有一些头脑房间:

void fastAutocorrelation( int64_t *data , float *r){

int64_t *temp;
int64_t *datan = data;
int64_t *datanm = data;

*temp = (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
*r++ = (float)(*temp)/int64ToFloat;

datan = data;
datanm = data + 1;
*temp = (*datan++)*(*datanm++);
*temp += (*datan++)*(*datanm++);
... 

【问题讨论】:

  • 到目前为止您尝试过哪些优化失败了?
  • 内联函数不会做任何事情。这些浮动转换将使您付出代价。 data 中的值的范围是多少?您是否输出了函数的程序集以查看您的编译器对N-mn+m 的重复计算做了什么?
  • 我目前正在更改代码,以便传入的数据是“浮动”的,这样我就可以消除重铸 2400 多次。 . .除此之外,不多。我希望对指针技巧有一些建议?或任何其他可能有帮助的东西(不包括 O1 之后的编译器优化,之后由于某种原因算法变得混乱)
  • 数据最大为 255*128*128 = 4177920,这是一个 32 位系统
  • 您是否考虑过使用 FFT 和 IFFT 计算自相关? en.wikipedia.org/wiki/Autocorrelation#Efficient_computation - 我不确定这会比蛮力方法更有效。

标签: c performance function optimization


【解决方案1】:

请注意您的处理器没有任何浮点功能;浮点运算正在软件中进行模拟。这意味着瓶颈不是循环控制(编译器已经在降低强度方面做得很好)。瓶颈是浮点模拟器。

鉴于您的处理器没有原生浮点,它很可能也没有大型 L1 缓存。更改循环控件的顺序可能会改善数据局部性。原始代码对一个 240 个元素的数组进行了 10 次扫描,这是局部性较差的。最好是在数组中进行一次扫描,一次研究 10 个项目。

void autocorrelation( int32_t *data , float *r){
  int m, n;
  for (m = 0; m < P + 1; m++) r[m] = 0.0f;
  for (n = 0; n < N; n++) {
    int limit = min(P + 1, N - n);
    for (m = 0; m < limit; m++) {
      r[m] += data[n] * data[n+m];
    }
  }
}

(请注意,转换为指针无济于事,因为编译器已经对原始代码进行了优化以使用指针。)

【讨论】:

  • 有人怀疑最后进行一次浮点转换可能会更好。
  • @BenVoigt 当然,如果我们可以避免浮点,代码的运行速度会明显更快。但我有点假设问题场景需要浮点。 (看起来像是某种信号处理。)
  • 嗯,整数乘法和加法都是精确的,你已经依赖于乘法不会溢出。因此,在所有加法之后,大概可以转换为浮点数,而不是之前。
  • @BenVoigt 但如果原始值有小数或大于 2^32,整数将不起作用。
  • 当问题中的参数类型 int32_t* data 开始携带分数信息时,我将不得不交出我的 C++ 帽子。
【解决方案2】:

我看到你通过移除石膏来减少了一点。如果您追求指针技巧,这里有一个尝试。根据您的编译器,您的里程会有所不同:

void autocorrelation( float *restrict data, float *restrict r)
{
    float *data_end = data + N;

    for ( int m=0 ; m < P+1 ; m++)
    {
        float temp = 0;

        for( float *data_n = data, *data_nm = data + m;
             data_nm != data_end;
             data_n++, data_nm++ )
        {
            temp += *data_n * *data_nm;
        }

        r[m] = temp;
    }
}

我添加了 restrict 关键字,以便编译器知道 datar 不重叠或指向同一事物。

【讨论】:

  • 感谢 Paddy,我根据您的 cmets 构建了我的解决方案(请参阅上面我最后编辑的正文段落)
  • 天哪,我放松了很多。您至少可以分块完成(如Duff's Device)。您是否尝试对数据源和累加器使用 64 位整数?这样你就可以在最后转换为浮动,并且可能会快很多。假设您的处理器乘以和添加比 64 位整数慢的浮点数。情况可能并非如此。不过值得进行基准测试。
  • 是的,这是我做的最后一件事,我一直在同时优化我的所有功能(展开它们等),第二次我从浮点数切换到 64 位(我的拱门是32 位浮点硬件)上面的函数下降到 20 个滴答声*(1/16000Hz)~ 1.25ms。我在以下位置切换回浮点数: *r++ = (float)(temp)/268435456; // 其中 268435456 来自 int 倍数,以保持分辨率 (128*128) 然后在此函数中平方(平方和)
【解决方案3】:

如果您想进行一些冒险实验,您可以尝试英特尔的 ICC 编译器(那是 x86 汇编器吗?)。使用正确的命令行开关,将通过为每个循环迭代使用单独的线程来自动并行化您的 for 循环。然而,循环确实需要相当丰富,线程的开销才值得。

另一种方法是使用 SSE 和 AVX 搞定。更好的是,我认为最新的 Intel x86s 有一个乘法/加法指令,这是相关性、FFT 等所需要的。这样你就可以对你的代码进行矢量化,并且每个时钟周期会发生几个操作(超出普通的CPU流水线可以实现)。实际上,有一些额外的“功能”直接映射到 SSE/AVX 操作码,使它们可以在 C 代码中轻松使用。编译器确实必须知道这些(英特尔的肯定知道),否则您将放入自己的在线汇编程序。然后,您还会遇到在运行时处理不同版本的 CPU 的问题;并非每台 PC 都配备最新的英特尔芯片。

或者您可能像我一样非常懒惰并使用预先优化的例程库,例如英特尔的 IPP/MKL。与 ICC 一样,它需要花钱,但如果速度 = 项目中的大笔资金,这将是非常值得的。

【讨论】:

  • 对于包含 x86 处理器的平台来说是一个很好的答案。不幸的是,OP 的代码不适用于 x86(寄存器是命名的,而不是编号的。其他助记符也表示不同的平台)无论如何,得到我的投票。
  • 我确实想知道它是不是 x86!但是,一般主题在其他平台上也适用。 VSIPL 可在各种 CPU 上使用,其工作与英特尔的 IPP/MKL 相同。相当多的 CPU 都有像 SSE 这样的 SIMD 扩展,并且通常可以找到聪明的编译器。祝你好运
【解决方案4】:

你试过了吗:

1) 推进指针而不是索引r 数组? (就像@paddy 为data_ndata_m 所做的那样)。例如。 *(r++) = temp 而不是 r[m] = temp

2) 循环展开。您的编译器显然不会这样做,例如,这将大大提高内部循环的速度。具体来说,请查看http://www.quickiwiki.com/en/Duff's_device 以获得整洁的循环展开。

3) 将循环展开发挥到极致! (也许不漂亮,我知道,但很酷):你知道NP 的值:你可以完全展开循环,并编写所有代码(虽然很长)没有任何分支。根据您的架构(足够的预取管道,加上哑分支预测),这可能会很好地提高速度。您甚至可以编写一个小实用程序为您生成完整的展开循环,并将生成的代码包含在您的.c 文件中。或者只使用宏和元编程。

【讨论】:

  • 如果您查看原始程序集,您会发现编译器已经将数组索引操作简化为指针。 R7 是相对指针。此外,大部分工作都在浮点函数中,而不是循环控制。展开不会有太大帮助,而且可能会伤害 icache。
  • 这不是“可能”的问题,您需要测试并查看。但我同意它可能会更慢,甚至写道结果是拱形的。依赖。
  • 是的,展开后性能略有提高是对的,但这足以保持浮动(这有助于保持光谱密度结果易于理解)。后期阶段是浮动的(直到最后),所以它必须在某个地方强制转换类型。
猜你喜欢
  • 2012-12-08
  • 2013-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-25
  • 1970-01-01
相关资源
最近更新 更多