我知道你应该只在必要时优化事物。但是,如果认为有必要,你最喜欢的低级别(与算法级别相比)优化技巧是什么。
例如:loop unrolling。
答案 0 :(得分:23)
gcc -O2
编译器可以做得更好。
答案 1 :(得分:16)
为过滤器,循环缓冲器等选择2的幂
非常,非常方便。
- 亚当
答案 2 :(得分:13)
为什么,bit twiddling hacks,当然!
答案 3 :(得分:12)
科学代码中最有用的一个方法是将pow(x,4)
替换为x*x*x*x
。 Pow几乎总是比乘法更昂贵。接下来是
for(int i = 0; i < N; i++)
{
z += x/y;
}
到
double denom = 1/y;
for(int i = 0; i < N; i++)
{
z += x*denom;
}
但我最喜欢的低级优化是要弄清楚哪些计算可以从循环中删除。它总是更快地进行一次计算而不是N次。根据您的编译器,其中一些可能会自动为您完成。
答案 4 :(得分:9)
检查编译器的输出,然后尝试强制它做更快的事情。
答案 5 :(得分:8)
我不一定称之为低级优化,但通过明智地应用缓存,我通过所有低级技巧的应用程序组合,已经节省了数量级更多的周期。其中许多方法都是特定于应用程序的。
if
或for
语句中找到。CPU和编译器在不断变化。无论什么样的低级代码技巧,3 CPU芯片以前使用不同的编译器实际上可能在当前架构上更慢,并且很可能这个技巧可能会混淆将来维护此代码的人。
答案 6 :(得分:7)
++i
可能比i++
更快,因为它可以避免创建临时。
现在的C / C ++ / Java / C#编译器是否仍然适用,我不知道。对于具有重载运算符的用户定义类型,它可能会有所不同,而在简单整数的情况下,它可能无关紧要。
但我喜欢语法......它读起来像“增量i”这是一个合理的顺序。
答案 7 :(得分:7)
使用模板元编程在编译时而不是在运行时计算事物。
答案 8 :(得分:5)
多年前,在一个不那么聪明的编译器中,我从函数内联,行走指针而不是索引数组获得了很大的里程,并且迭代到零而不是最多。
如果有疑问,对汇编的一点知识将让你看看编译器产生了什么并攻击低效的部分(使用源语言,使用对编译器更友好的结构。)
答案 9 :(得分:5)
不要进行循环展开。不要做Duff的设备。使你的循环尽可能小,其他任何东西都会抑制x86性能和gcc优化器性能。
除掉分支可能很有用 - 所以完全摆脱循环是好的,那些无分支的数学技巧确实有效。除此之外,尽量不要离开L2缓存 - 这意味着如果它浪费缓存空间,也应避免大量的预先计算/缓存。
并且,特别是对于x86,尝试在任何时候保持使用的变量数量。很难说这些编译器会用这种东西做什么,但通常用较少的循环迭代变量/数组索引最终会得到更好的asm输出。
当然,这适用于台式机CPU;具有快速内存访问速度的慢速CPU可以预先计算更多,但在这些日子里,这可能是一个总内存很少的嵌入式系统...
答案 10 :(得分:5)
预先计算值。
例如,如果你的应用不一定需要角度非常精确,而不是sin(a)或cos(a),也许你代表一个圆的1/256的角度,并创建浮动数组正弦[]和余弦[]预先计算这些角度的sin和cos。
并且,如果你经常需要一个给定长度的某个角度的矢量,你可能会预先计算所有已经乘以该长度的正弦和余弦。
或者,换句话说,为了速度而交易记忆。
或者,更一般地说,“所有编程都是缓存练习” - Terje Mathisen
有些事情不太明显。例如,遍历二维数组,您可能会执行类似
的操作for (x=0;x<maxx;x++) for (y=0;y<maxy;y++) do_something(a[x,y]);
如果你这样做,你可能会发现处理器缓存更喜欢它:
for (y=0;y<maxy;y++) for (x=0;x<maxx;x++) do_something(a[x,y]);
反之亦然。
答案 11 :(得分:4)
优化缓存局部性 - 例如,当将两个不适合缓存的矩阵相乘时。
答案 12 :(得分:3)
我发现从指针转换为索引访问可能会有所不同;编译器有不同的指令形式和注册用法可供选择。反之亦然。但这是非常低级别和编译器相关的,并且只有在需要最后几个百分点时才有效。
E.g。
for (i = 0; i < n; ++i)
*p++ = ...; // some complicated expression
VS
for (i = 0; i < n; ++i)
p[i] = ...; // some complicated expression
答案 13 :(得分:3)
使用C ++的placement new在预先分配的缓冲区上分配new。
答案 14 :(得分:3)
倒计时循环。与0比N比较便宜:
for (i = N; --i >= 0; ) ...
以2的幂移位和屏蔽比除法和余数,和/和%
便宜#define WORD_LOG 5
#define SIZE (1 << WORD_LOG)
#define MASK (SIZE - 1)
uint32_t bits[K]
void set_bit(unsigned i)
{
bits[i >> WORD_LOG] |= (1 << (i & MASK))
}
修改强>
(i >> WORD_LOG) == (i / SIZE) and
(i & MASK) == (i % SIZE)
因为SIZE是32或2 ^ 5。
答案 15 :(得分:3)
Jon Bentley的Writing Efficient Programs是低级和高级技术的重要来源 - 如果你能找到副本。
答案 16 :(得分:3)
使用布尔数学消除分支(if / elses):
if(x == 0)
x = 5;
// becomes:
x += (x == 0) * 5;
// if '5' was a base 2 number, let's say 4:
x += (x == 0) << 2;
// divide by 2 if flag is set
sum >>= (blendMode == BLEND);
这真的可以加快速度,特别是当这些ifs处于循环或某个被调用的地方时。
答案 17 :(得分:3)
汇编程序中的那个:
xor ax, ax
而不是:
mov ax, 0
节目大小和性能的经典优化。
答案 18 :(得分:2)
在SQL中,如果您只需要知道是否存在任何数据,请不要理会COUNT(*)
:
SELECT 1 FROM table WHERE some_primary_key = some_value
如果您的WHERE
子句可能会返回多行,请同时添加LIMIT 1
。
(请记住,数据库无法查看代码对其结果的影响,因此他们无法自行优化这些内容!)
答案 19 :(得分:2)
vfork()
fork()
代替exec()
答案 20 :(得分:1)
自由使用__restrict来消除负载命中存储停滞。
答案 21 :(得分:1)
滚动循环。
说真的,上次我需要做这样的事情的时候是一个占用80%运行时间的函数,所以如果我能得到明显的性能提升,那么值得尝试进行微优化。
我做的第一件事就是卷起循环。这给了我一个非常显着的速度提升。我相信这是缓存局部的问题。
我接下来要做的是添加一个间接层,并在循环中添加一些逻辑,这使我只能遍历我需要的东西。这并没有增加速度,但值得做。
如果您要进行微优化,您需要对两件事情有一个合理的认识:您实际使用的架构(这与我长大的系统有很大不同,至少对于微优化而言目的),以及编译器将为您做什么。
许多传统的微观优化会随着时间的推移进行交易。如今,使用更多空间会增加缓存未命中的可能性,并且会影响您的性能。而且,现在很多都是由现代编译器完成的,通常比你可能做得更好。
目前,您应该(a)剖析以查看是否需要微优化,然后(b)尝试交换空间计算,以期尽可能保持缓存。最后,进行一些测试,这样你就知道你是否改进了东西或搞砸了。现代编译器和芯片对于保持良好的心理模型来说太复杂了,而且你知道一些优化是否有效的唯一方法就是测试。
答案 22 :(得分:1)
除了约书亚关于代码生成(大赢)的评论以及其他好的建议,......
我不确定你是否会把它称为“低级”,但是(这是downvote-bait)1)远离使用任何更多级别的抽象而不是绝对必要,2)远离事件如果可能的话,驱动通知式编程。
如果执行程序的计算机就像运行竞赛的汽车那样,方法调用就像绕道而行。这并不一定是坏事,除非有强烈的诱惑来嵌套这些东西,因为一旦你写了一个方法调用,你往往会忘记那个调用会花费你的成本。
如果您依赖事件和通知,那是因为您有多个数据结构需要保持一致。这是昂贵的,只有在你无法避免时才应该这样做。
根据我的经验,最大的性能杀手是过多的数据结构和过多的抽象。
答案 23 :(得分:1)
我对通过替换在结构中添加数字的for循环获得的加速感到惊讶:
const unsigned long SIZE = 100000000;
typedef struct {
int a;
int b;
int result;
} addition;
addition *sum;
void start() {
unsigned int byte_count = SIZE * sizeof(addition);
sum = malloc(byte_count);
unsigned int i = 0;
if (i < SIZE) {
do {
sum[i].a = i;
sum[i].b = i;
i++;
} while (i < SIZE);
}
}
void test_func() {
unsigned int i = 0;
if (i < SIZE) { // this is about 30% faster than the more obvious for loop, even with O3
do {
addition *s1 = &sum[i];
s1->result = s1->b + s1->a;
i++;
} while ( i<SIZE );
}
}
void finish() {
free(sum);
}
为什么gcc不优化for循环呢?还是我错过了什么?一些缓存效应?