C 32x32乘法和加法优化

C 32x32乘法和加法优化,c,optimization,loops,C,Optimization,Loops,我正在优化一个应用程序。我发现我需要优化一个内部循环以提高性能。 该滤波器是一个16位阵列 for (i = 0; i < iLen; i++) { iPredErr = (I32)*rgiResidue; rgiFilter = rgiFilterBuf; rgiPrevVal = rgiPrevValRdBuf + iRecent; rgiUpdate = rgiUpdateRdBuf + iRecent; iPred = iScalingOf

我正在优化一个应用程序。我发现我需要优化一个内部循环以提高性能。 该滤波器是一个16位阵列

for (i = 0; i < iLen; i++) {
    iPredErr = (I32)*rgiResidue;
    rgiFilter = rgiFilterBuf;
    rgiPrevVal = rgiPrevValRdBuf + iRecent;
    rgiUpdate = rgiUpdateRdBuf + iRecent;

    iPred = iScalingOffset;

    for (j = 0; j < iOrder_Div_8; j++) {


                 iPred += (I32) rgiFilter[0] * rgiPrevVal[0]; 
                 rgiFilter[0] += rgiUpdate[0];

                 iPred += (I32) rgiFilter[1] * rgiPrevVal[1]; 
                 rgiFilter[1] += rgiUpdate[1];

                 iPred += (I32) rgiFilter[2] * rgiPrevVal[2]; 
                 rgiFilter[2] += rgiUpdate[2];

                 iPred += (I32) rgiFilter[3] * rgiPrevVal[3]; 
                 rgiFilter[3] += rgiUpdate[3];

                 iPred += (I32) rgiFilter[4] * rgiPrevVal[4]; 
                 rgiFilter[4] += rgiUpdate[4];

                 iPred += (I32) rgiFilter[5] * rgiPrevVal[5]; 
                 rgiFilter[5] += rgiUpdate[5];

                 iPred += (I32) rgiFilter[6] * rgiPrevVal[6]; 
                 rgiFilter[6] += rgiUpdate[6];

                 iPred += (I32) rgiFilter[7] * rgiPrevVal[7]; 
                 rgiFilter[7] += rgiUpdate[7];

                    rgiFilter += 8;
        rgiPrevVal += 8;
                    rgiUpdate += 8;



}
(i=0;i{ iPredErr=(I32)*残基; rgiFilter=rgiFilterBuf; rgiproval=rgiprovalrdbuf+iRecent; rgiUpdate=rgiupdaterbuf+iRecent; iPred=iScalingOffset; 对于(j=0;j
ode在这里

我不认为您可以在C语言中对其进行太多优化。您的编译器可能有生成SIMD代码的选项,但如果性能至关重要,您可能需要编写自己的SIMD汇编代码……

您唯一的赌注是一次执行多个操作,这意味着以下三个选项之一:

  • SSE指令(SIMD)。您可以使用一条指令处理多个内存位置
  • 多线程(MIMD)。如果您有一个以上的cpu内核,这一点最为有效。将阵列拆分为多个大小相似的独立条带(依赖关系会大大增加此选项的复杂性,如果您需要大量锁,则比按顺序计算所有内容要慢)。请注意,阵列必须足够大,以抵消额外的上下文切换和同步开销(非常小,但不可忽略)。最适合4核或更多核
  • 如果你的阵型真的很大,你可以通过两者的结合获得很多
  • 确保iPred保存在寄存器中(在每次+=操作之前不从内存读取,在每次+=操作之后不写回内存)
  • 优化一级缓存的内存布局。确保3个阵列不会争夺相同的缓存项。这取决于CPU体系结构,一点也不简单

  • 如果
    rgiFilterBuf
    rgiprovalrdbuf
    rgipupdaterbuf
    是不带别名的函数参数,请使用
    restrict
    限定符声明它们。这将允许编译器更积极地进行优化


    正如其他一些人所评论的,您的内部循环看起来很适合向量处理指令(如SSE,如果您在x86上)。请检查编译器的内部函数。

    循环展开和向量化应由编译器完成


    请参见

    您可以用很少的SSE2内部函数替换内部循环

    请参见[u mm_madd_epi16][1]以替换八个

    iPred += (I32) rgiFilter[] * rgiPrevVal[];
    
    以及(二)或(三)以取代八项建议

    rgiFilter[] += rgiUpdate[];
    
    你应该看到一个很好的加速

    这些内部函数特定于Microsoft和Intel编译器。 我确信GCC存在等价物,我只是没有使用它们

    编辑:根据下面的评论,我将更改以下内容

    如果您有混合类型,编译器并不总是足够聪明来解决它。 我建议以下几点,让它更明显,并给它一个更好的机会 在自动矢量化

  • 将rgiFilter[]声明为的I32位 此函数的目的。您 我会付一份
  • 将iPred也更改为iPred[]作为I32
  • 在内部(甚至外部)循环之外进行iPred[]求和

  • 四人一组打包类似说明

    iPred[0]+=rgiFilter[0]*RGIProval[0]

    iPred[1]+=rgiFilter[1]*RGIProval[1]

    iPred[2]+=rgiFilter[2]*RGIProval[2]

    iPred[3]+=rgiFilter[3]*RGIProval[3]

    rgiFilter[0]+=rgiFilter[0]

    rgiFilter[1]+=rgiFilter[1]

    rgiFilter[2]+=rgiFilter更新[2]

    rgiFilter[3]+=rgiFilter更新[3]


  • 这对于英特尔编译器来说应该足够了,可以通过确保数据在内存中呈线性排列,这样就不会出现缓存未命中来解决问题。不过,这似乎不是问题

    如果无法执行这些操作(如果编译器失败,请查看程序集),请尝试将较小的for循环分成几个不同的for循环(每个for循环对应一个0..8)。编译器往往能够对执行较少操作的循环进行更好的优化(除非在这种情况下,它可能能够进行矢量化/SSE)

    对于32/64位体系结构来说,16位整数的使用成本更高(除非它们有特定的16位寄存器)。在执行循环之前,请尝试将其转换为32位(大多数64位体系结构也有32位寄存器)。

    非常好的代码

    在每一步中,你基本上要做三件事,一个乘法和两个加法

    另外,我有时发现,如果我将这些活动划分为不同的循环,我会得到更快的代码,比如

    • 一个循环执行乘法并保存到临时数组

    • iPred
      中对该数组求和的一个循环

    • rgiUpdate
      添加到
      rgiFilter
      的一个循环

    展开时,循环开销可以忽略不计,但如果
    for (i = 0; i < iLen; i++) {
    
    for (i = iLen-1; i <= 0; i--) {
    
    for (p = rgiFilter; p <= rgiFilter+8; ) {
         iPred += (I32) (*p) + *rgiPreval++;
         *p++ += *rgiUpdate++;
    
         ....
    
    }
    
    for (p = rgiFilter; p <= rgiFilter+8; ) {
         I16 x = *p;
         I16 y = *(p+1); // Hope that the compiler can combine these loads
         iPred += (I32) x + *rgiPreval++;
         iPred += (I32) y + *rgiPreval++;
    
         *p++ += *rgiUpdate++;
         *p++ += *rgiUpdate++; // Hope that the complier can combine these stores
    
         ....
    
    }
    
    __builtin_prefetch (const void * addr)
    __builtin_prefetch (const void * addr, int rw)
    __builtin_prefetch (const void * addr, int rw, int locality)