优化提示

anu*_*nup 4 c optimization

int *s;
allocate memory for s[100];
void func (int *a, int *b)
{
    int i;

    for (i = 0; i < 100; i++)
    {
        s[i] = a[i] ^ b[i];
    }
}
Run Code Online (Sandbox Code Playgroud)

假设这个特定的代码片段被调用了1000次,这是我代码中最耗时的操作.还假设每次更改a和b的地址.'s'是一个全局变量,使用a和b的不同值集更新.

据我所知,主要的性能瓶颈是内存访问,因为唯一的其他操作是XOR,这非常简单.

您能否建议我如何以最佳方式优化我的代码?

我真的想问的问题,但我认为没有得到恰当的传达,例如,这个for循环包含10个这样的XOR操作,循环计数为100,函数调用1000次,点是高内存如果代码要在单个核心机器上执行,那么改进的范围是什么?

Pet*_*nna 9

我已经测试了提出的解决方案,以及其他两个.由于保存到s []的结果不正确,我无法测试onemasse的提议.我也无法修复它.我不得不对moonshadow代码做一些改变.测量单位是时钟周期,因此越低越好.

原始代码:

#define MAX 100
void inline STACKO ( struct timespec *ts, struct timespec *te ){

    int i, *s, *a, *b;

    for (i = 0; i < MAX; ++i){
        s = (int *) malloc (sizeof (int)); ++s;
        a = (int *) malloc (sizeof (int)); ++a;
        b = (int *) malloc (sizeof (int)); ++b;
    }

    srand ( 1024 );
    for (i = 0; i < MAX; ++i){
        a[i] = ( rand() % 2 );
        b[i] = ( rand() % 2 );
    }

    rdtscb_getticks ( ts ); /* start measurement */

    for (i = 0; i < MAX; i++)
        s[i] = a[i] ^ b[i];

    rdtscb_getticks ( te ); /* end measurement */

/*
    printf("\n");
    for (i = 0; i < MAX; ++i)
        printf("%d", s[i]);
    printf("\n");
*/
}
Run Code Online (Sandbox Code Playgroud)

新提案1:注册int

从:

int i, *s, *a, *b;
Run Code Online (Sandbox Code Playgroud)

至:

register int i, *s, *a, *b;
Run Code Online (Sandbox Code Playgroud)

新提案2:没有数组表示法

s_end = &s[MAX];
for (s_ptr = &s[0], a_ptr = &a[0], b_ptr = &b[0]; \
   s_ptr < s_end; \
   ++s_ptr, ++a_ptr, ++b_ptr){
    *s_ptr = *a_ptr ^ *b_ptr;
}
Run Code Online (Sandbox Code Playgroud)

moonshadow建议优化:

s_ptr = &s[0];
a_ptr = &a[0];
b_ptr = &b[0];

for (i = 0; i < (MAX/4); i++){

    s_ptr[0] = a_ptr[0] ^ b_ptr[0];
    s_ptr[1] = a_ptr[1] ^ b_ptr[1];
    s_ptr[2] = a_ptr[2] ^ b_ptr[2];
    s_ptr[3] = a_ptr[3] ^ b_ptr[3];
    s_ptr+=4; a_ptr+=4; b_ptr+=4;
}
Run Code Online (Sandbox Code Playgroud)

moonshadow提出优化+寄存器int:

从:

int i, *s, ...
Run Code Online (Sandbox Code Playgroud)

至:

register int i, *s, ...
Run Code Online (Sandbox Code Playgroud)

Christoffer提议优化:

#pragma omp for
for (i = 0; i < MAX; i++)
{
    s[i] = a[i] ^ b[i];
}
Run Code Online (Sandbox Code Playgroud)

结果:

性能图

Original Code   1036.727264
New Proposal 1  611.147928
New proposal 2  450.788845
moonshadow      713.3845
moonshadow2     452.481192
Christoffer     1054.321943
Run Code Online (Sandbox Code Playgroud)

还有其他简单的方法可以优化生成的二进制文件.将-O2传递给gcc告诉您需要优化.要确切了解-O2的作用,请参阅gcc手册页.

启用-O2后: 性能图

Original Code   464.233031
New Proposal 1  452.620255
New proposal 2  454.519383
moonshadow      428.651083
moonshadow2     419.317444
Christoffer     452.079057
Run Code Online (Sandbox Code Playgroud)

源代码见:http://goo.gl/ud52m


moo*_*dow 5

不要使用循环变量来索引.展开循环.

for (i = 0; i < (100/4); i++)
{
  s[0] = a[0] ^ b[0];
  s[1] = a[1] ^ b[1];
  s[2] = a[2] ^ b[2];
  s[3] = a[3] ^ b[3];
  s+=4; a+=4; b+=4;
}
Run Code Online (Sandbox Code Playgroud)

了解如何在您的平台上执行SIMD XOR.

将这些XOR作为显式步骤执行可能比作为另一个计算的一部分执行更昂贵:您必须从a和b读取并将结果存储在s中 - 如果再次读取s以进行更多计算,则可以节省每次迭代读取和写入,以及所有函数调用和循环开销,通过在那里进行异或; 同样,如果a和b是某些其他函数的输出,则可以通过在其中一个函数的末尾执行XOR来做得更好.

  • 并且你认为编译器不能自己做这个,给定恒定的上限?!它将,*和*它将插入适当的SIMD指令(如果它们恰好在这里是有益的,可能就是这种情况). (3认同)
  • @Konrad:我整天都在看反汇编的编译器输出.很多时候,在我们支持的所有三个平台上,编译器都不能很好地完成这类工作.我没有看到编译器在没有明确提示的情况下生成合理的SIMD代码(即使用平台的SIMD类型和内在函数). (3认同)
  • 自动矢量化并不那么容易.编译器需要能够证明a,b和s可被16整除,如果没有,则插入适当的填充,这会增加开销.这种开销实际上可能会恶化代码的性能,我注意到-fno-tree-vectorize在某些关键循环上提高了性能. (3认同)