@云计算与数字孪生 的回答有一个简单的错误。
首先把局部变量放在for循环外,如果变量在循环中使用,但在循环结束后就不再使用,将这个变量放在for循环外可以避免每次循环都重新分配内存。
这个想法是错误的。在实践中除了C语言的可变长数组(VLA),不管是C还是C++都不会每次循环都重新处理栈内存分配。
对此标准中的说法是:生命期结束。也就是通过其他方式企图访问这些对象是不礼貌的,但有没有释放内存不做要求。常见实现都会在函数return之后才释放内存。
的确存在变量写在外部提升运行效率的情况,比如C++的构造函数和析构函数。如果写在循环内部每次循环会重复运行,写在循环外部可以只运行一次。但这些情况提升运行效率的原因和栈内存分配毫无关系。
只说c/c++的for循环优化,除了一些编译器自动化的完全循环展开之类的已经优化到极限的特殊场景。其它情况下,程序员还是有很多办法来优化,老司机都有很多的优化办法,我随便提几条:
1)如果循环中存在使用 vector1[i], 并且循环期间vector长度固定不变,那么引入一个ptr_t=&vector1[0],循环中 ++ptr_t; 那么性能会高不少。 因为vector1[i]内部的操作比较复杂,典型的场景下涉及一次取指针一次乘法一次加法,开销比较大。std::array 也可以用这个思路优化,也有效果。
2) 如果for循环中的第一次条件比较,肉眼可见的一定是true,那么改写成while( 1 ){.........if( likely( i < n ) ) continue; break; }把比较 if 挪到最后,可以平均减少一次比较,提升效率。
3) 循环 部分展开,将计算部分的一条,手工改成四条或者8条或者更多,改写整个循环,减少总体循环次数。剩下的几条单独处理。需要对比测试,在一部分场景下会有更好的性能。例如:写memcpy时有用这个思路,就是依次处理512字节(16条avx指令每循环)的倍数,剩下128字节(4条avx指令每循环)的倍数,剩下32字节(1条avx指令每循环)的倍数,剩下是32字节以下的处理。这样就比1条avx指令每循环的简单for循环要快很多。
4) intel cpu存在一些常用场景的特殊优化,CPU内部uint整数短循环优化,在现代intel cpu下会有更高的性能。对于intel cpu, 如果循环的i和n是 unsigned int,并且循环内的指令数很少,那么这个循环内的代码就会自动进入一个加速模式中执行。这种模式有专门的设计,是硬件优化,比i和n是64bit size_t的场景下快很多。
5) 循环优化,还有其它黑科技的未公开的优化方案, 例如:指令级if并发。适应现代cpu(支持乱序执行), 可以用于一些for循环,也可以用于类似于红黑树,AVL树之类的 find 循环,有特定写法,大约可以比普通循环写法提升10%--30%,我自己测试AVL树大约提升20%,主要方案是 指令级if并发 等优化方案,目前只在小圈子中流传。这个现代cpu乱序执行if的优化,针对一部分有循环的旧函数代码优化,很多情况下可以实现性能大幅度提升。
- 减少循环次数:在循环中使用 break 语句或者 return 语句可以尽早退出循环,减少循环次数。
- 减少循环内部的计算量:在循环内部使用变量来保存中间结果,避免重复计算,减少计算量。
- 提前计算循环条件:对于需要多次使用的循环条件,可以先将其计算出来,然后在循环中直接使用,避免每次循环都要重新计算。
- 使用合适的数据结构:选择合适的数据结构,可以使循环的执行效率更高。例如,使用数组可以比使用链表更快地访问数据,使用哈希表可以比使用链表更快地查找数据。
- 使用并行化:使用多线程或多核 CPU 可以使循环的执行效率更高。但是,使用并行化需要注意同步问题。
- 使用编译器优化:许多编译器都有循环展开、自动向量化和其他优化选项,可以使循环的执行效率更高。
- 使用循环展开:循环展开是指将循环体中的代码复制到循环外部,从而减少循环次数。
- 使用自动向量化:自动向量化是指使用 CPU 的 SIMD 指令(Single Instruction Multiple Data,单指令多数据)来并行执行向量运算,从而提高循环的执行效率。
- 使用尾递归优化:尾递归优化是指将递归转化为循环的优化技术,可以避免栈的增长,提高递归的执行效率。
- 使用内联函数:内联函数是指将函数体内的代码直接嵌入调用函数中,从而避免函数调用带来的开销。
- 使用缓存友好的代码:计算机的缓存是一种高速存储器,可以加快访问数据的速度。使用缓存友好的代码,可以更有效地利用缓存,提高循环的执行效率。
忘记在哪看过一个文章,复制到自己小本本上了,如下:
In frequency reduction, the amount of code in loop is decreased. A statement or expression, which can be moved outside the loop body without affecting the semantics of the program, is moved outside the loop.
1.降频
使用降频,循环中的代码量被减少。在不影响程序语义的情况下,可以移到循环体之外的语句或表达式被移到循环体之外。
Initial code:
while(i<100)
{
a = Sin(x)/Cos(x) + i;
i++;
}
Optimized code:
t = Sin(x)/Cos(x);
while(i<100)
{
a = t + i;
i++;
}
Loop unrolling is a loop transformation technique that helps to optimize the execution time of a program. We basically remove or reduce iterations. Loop unrolling increases the program’s speed by eliminating loop control instruction and loop test instructions.
2.循环展开
循环展开是一种循环转换技术,有助于优化程序的执行时间。我们通常删除或减少迭代。循环展开通过消除循环控制指令和循环测试指令来提高程序的速度。
Initial code:
for (int i=0; i<5; i++)
printf("Pankaj\
");
Optimized code:
printf("Pankaj\
");
printf("Pankaj\
");
printf("Pankaj\
");
printf("Pankaj\
");
printf("Pankaj\
");
Loop jamming is the combining the two or more loops in a single loop. It reduces the time taken to compile the many number of loops.
3.循环转移
循环转移是将两个或更多的循环合并为一个单一的循环。它减少了编译许多循环所需的时间。
Initial Code:
for(int i=0; i<5; i++)
a = i + 5;
for(int i=0; i<5; i++)
b = i + 10;
Optimized code:
for(int i=0; i<5; i++)
{
a = i + 5;
b = i + 10;
}
其实核心内容就一句话:
减少内部循环中的指令数可以减少程序的运行时间。
另外,看了一部分答案,都挺不“常见”的。
无需优化!
99%的程序员是业务程序员,没有海量的数据需要循环,计算什么的。
很多程序员在写代码的过程中,以为循环多了会影响性能,其实对于编程语言来说,循环100次和循环10000次性能差不多,所以,没必要绞劲脑汁的去优化,尽量做到能不循环就不循环,循环到想要的结果马上退出循环就行,没必要为了那点性能去过度优化。一个优秀的程序员应该优先考虑程序的扩展性,写出好的可维护的代码,而不是把时间浪费在那一点点优化上,系统的瓶颈一般都在并发或数据库上,而不是那点点循环