C 优化提示 int*s; 为s[100]分配内存; 无效函数(int*a,int*b) { int i; 对于(i=0;i
假设这个特定的代码片段被调用了1000次,这是我代码中最耗时的操作。还假设a和b的地址每次都会改变。”s’是一个全局变量,使用不同的a和b值集进行更新 据我所知,主要的性能瓶颈是内存访问,因为唯一的其他操作是XOR,这非常简单 你能建议我如何以最好的方式优化我的代码吗C 优化提示 int*s; 为s[100]分配内存; 无效函数(int*a,int*b) { int i; 对于(i=0;i,c,optimization,C,Optimization,假设这个特定的代码片段被调用了1000次,这是我代码中最耗时的操作。还假设a和b的地址每次都会改变。”s’是一个全局变量,使用不同的a和b值集进行更新 据我所知,主要的性能瓶颈是内存访问,因为唯一的其他操作是XOR,这非常简单 你能建议我如何以最好的方式优化我的代码吗 我真的想问的问题,但我认为它没有得到正确的传达,例如,这个for循环包含10个这样的异或操作,循环计数是100,函数调用了1000次,重点是高内存访问。如果代码要在单核机器上执行,改进的范围是什么?不要使用循环变量进行索引。 展开
我真的想问的问题,但我认为它没有得到正确的传达,例如,这个for循环包含10个这样的异或操作,循环计数是100,函数调用了1000次,重点是高内存访问。如果代码要在单核机器上执行,改进的范围是什么?不要使用循环变量进行索引。 展开循环
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];
}
}
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可能比作为另一个计算的一部分执行这些XOR更昂贵:您必须从a和b中读取并将结果存储在s中-如果再次读取s以进行更多计算,您可以通过在那里执行XOR来节省每次迭代的读取和写入,以及所有函数调用和循环开销;同样,如果a和b是其他一些函数的输出,那么在其中一个函数的末尾执行XOR会更好。int*s;
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;
}
为s[100]分配内存;
无效函数(int*a,int*b)
{
int i;
#pragma omp for
对于(i=0;i<100;i++)
{
s[i]=a[i]^b[i];
}
}
当然,对于100个元素,您可能看不到任何特别的改进:-)这里只是一个猜测。如果这是缓存问题,您可以尝试以下方法:
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];
}
}
int*s;
为s[100]分配内存;
无效函数(int*a,int*b)
{
int i;
memcpy(s、a、100);
对于(i=0;i<100;i++)
{
s[i]=s[i]^b[i];
}
}
memcpy,尽管它是一个函数调用,但如果size参数是常量,它通常会由编译器内联。循环展开在这里可能没有帮助,因为它可以由编译器自动完成。但你不应该相信我的话,在你的平台上进行验证。我已经测试了提议的解决方案,以及其他两个。我无法测试onemasse的提案,因为保存到s[]的结果不正确。我也没能把它修好。我不得不对moonshadow代码做一些修改。测量单位为时钟周期,因此越低越好 原始代码:
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];
}
}
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;
}
register int i, *s, ...
致:
新方案2:无数组表示法
register int i, *s, *a, *b;
致:
克里斯托弗提议的优化:
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];
}
}
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;
}
register int i, *s, ...
还有其他简单的方法来优化生成的二进制文件。将-O2传递给gcc表示您需要优化。要确切了解-O2的作用,请参阅gcc手册页
启用-O2后:
源代码位于:您确定瓶颈不是由于分配内存造成的吗?malloc涉及定期调用操作系统中昂贵的内存分配代码。示例:在unix中,该调用是brk()。此函数是否需要是线程安全的?什么是
s
?为什么是本地的?如果函数只是填充一个本地数组,那么它实际上不会做任何事情,因此优化它的最佳方法是完全删除它。如果函数不返回任何内容,为什么函数int
?你用这个干什么?你需要给我们更多的信息。这个函数很奇怪。它计算s
,然后立即丢弃,分配的内存泄漏。因此,最好的优化可能是int func(int*a,int*b){(void)a;(void)b;}
您必须提供有关调用函数的更多信息。有些a和b数组是重复的吗?当涉及内存瓶颈时,你确实需要考虑整个访问模式,而不仅仅是一个小的100元素循环。将内存访问重新排序到稍微高一点的级别可能会对性能产生显著影响,这取决于正在发生的事情。您认为编译器本身无法做到这一点,因为它具有恒定的上限?!它会,它会插入适当的SIMD指令(如果它们在这里碰巧是有益的,这可能是事实)。@Konrad:我花了一整天看反汇编的编译器输出。大多数时候,在我们支持的三种平台上,编译器都不能很好地完成这类工作。我从未见过编译器在没有显式提示(即使用平台的SIMD类型和内部函数)的情况下生成合理的SIMD代码。@Konrad:haha。理论上,我们的一个平台甚至支持GCC自动矢量化()。实际上,我现在大约每个月只对编译器提交一次bug报告,所以我想情况有了一些改善。章鱼器怎么了?自动矢量化没那么容易。编译器需要能够证明a、b和s可以被16整除,如果不能,则插入适当的填充,这会增加开销。这种开销实际上可能会恶化代码的性能,我注意到当-fno-tree矢量化某些关键循环的性能时,@Konrad:-O3是不够的。例如,除非您明确使用-funroll循环
,否则GCC不会展开循环;除非您使用-march=$SOMETHING
指定代码将在支持循环的处理器上运行,否则GCC不会尝试使用SIMD指令。即使插入您忘记的“并行”命令,事实上,如果只有100个数字,速度会慢得多。啊,你说得对,康德。我将删除我以前的评论。可能是
register int i, *s, ...
#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
Original Code 464.233031
New Proposal 1 452.620255
New proposal 2 454.519383
moonshadow 428.651083
moonshadow2 419.317444
Christoffer 452.079057