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];
}
}
假设此特定代码段被调用1000次,这是我的代码中最耗时的操作。还假设每次更改a和b的地址。 's'是一个全局变量,用一组&amp;的不同值集更新。湾
据我所知,主要的性能瓶颈是内存访问,因为唯一的其他操作是XOR,这非常简单。
请您建议我如何以最佳方式优化我的代码?
我真的想问的问题,但我认为没有得到恰当的传达,例如,这个for循环包含10个这样的XOR操作,循环计数为100,函数调用1000次,点高内存访问。如果要在单个核心机器上执行代码,有哪些改进范围?
答案 0 :(得分: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");
*/
}
新提案1:注册int
自:
int i, *s, *a, *b;
要:
register int i, *s, *a, *b;
新提案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;
}
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;
}
moonshadow建议优化+注册int:
自:
int i, *s, ...
要:
register int i, *s, ...
Christoffer建议优化:
#pragma omp for
for (i = 0; i < MAX; i++)
{
s[i] = a[i] ^ b[i];
}
Original Code 1036.727264
New Proposal 1 611.147928
New proposal 2 450.788845
moonshadow 713.3845
moonshadow2 452.481192
Christoffer 1054.321943
还有其他简单的方法可以优化生成的二进制文件。将-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
答案 1 :(得分: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;
}
了解如何在您的平台上执行SIMD XOR。
执行这些XOR作为显式步骤可能比作为另一个计算的一部分执行它们更昂贵:您必须从a和b读取并将结果存储在s中 - 如果再次读取s以进行更多计算,则通过在那里进行异或来保存每次迭代的读取和写入,以及所有函数调用和循环开销。同样,如果a和b是某些其他函数的输出,则可以通过在其中一个函数的末尾执行XOR来做得更好。
答案 2 :(得分:0)
int *s;
allocate memory for s[100];
void func (int *a, int *b)
{
int i;
#pragma omp for
for (i = 0; i < 100; i++)
{
s[i] = a[i] ^ b[i];
}
}
当然,对于只有一百个元素,您可能看不到任何特别的改进: - )
答案 3 :(得分:0)
这里只是一个猜测。如果这是一个缓存问题,你可以试试这个:
int *s;
allocate memory for s[100];
void func (int *a, int *b)
{
int i;
memcpy( s, a, 100 );
for (i = 0; i < 100; i++)
{
s[i] = s[i] ^ b[i];
}
}
memcpy,尽管如果size参数是常量,编译器通常会内联函数调用。循环展开可能在这里没有帮助,因为它可以由编译器自动完成。但你不应该相信我的话,在你的平台上验证。