在这个网站上已经有很多性能问题了,但是在我看来,几乎所有的问题都是非常具体的,而且相当狭窄。几乎所有人都重复了避免过早优化的建议。
我们假设:
代码已经正常工作了
所选择的算法对于问题的环境已经是最优的
对代码进行了测量,并隔离了有问题的例程
所有优化的尝试也将被衡量,以确保它们不会使事情变得更糟
我在这里寻找的是策略和技巧,在一个关键算法中,当没有其他事情可做,但无论如何都要挤出最后百分之几。
理想情况下,尽量让答案与语言无关,并在适用的情况下指出所建议的策略的任何缺点。
我将添加一个带有我自己最初建议的回复,并期待Stack Overflow社区能想到的任何其他东西。
在带有模板的语言(c++ /D)中,您可以尝试通过模板参数传播常量值。你甚至可以用开关来处理小的非常值集合。
Foo(i, j); // i always in 0-4.
就变成了
switch(i)
{
case 0: Foo<0>(j); break;
case 1: Foo<1>(j); break;
case 2: Foo<2>(j); break;
case 3: Foo<3>(j); break;
case 4: Foo<4>(j); break;
}
缺点是缓存压力,因此这只会在深度或长期运行的调用树中获得,其中值在持续时间内是恒定的。
建议:
Pre-compute rather than re-calculate: any loops or repeated calls that contain calculations that have a relatively limited range of inputs, consider making a lookup (array or dictionary) that contains the result of that calculation for all values in the valid range of inputs. Then use a simple lookup inside the algorithm instead.
Down-sides: if few of the pre-computed values are actually used this may make matters worse, also the lookup may take significant memory.
Don't use library methods: most libraries need to be written to operate correctly under a broad range of scenarios, and perform null checks on parameters, etc. By re-implementing a method you may be able to strip out a lot of logic that does not apply in the exact circumstance you are using it.
Down-sides: writing additional code means more surface area for bugs.
Do use library methods: to contradict myself, language libraries get written by people that are a lot smarter than you or me; odds are they did it better and faster. Do not implement it yourself unless you can actually make it faster (i.e.: always measure!)
Cheat: in some cases although an exact calculation may exist for your problem, you may not need 'exact', sometimes an approximation may be 'good enough' and a lot faster in the deal. Ask yourself, does it really matter if the answer is out by 1%? 5%? even 10%?
Down-sides: Well... the answer won't be exact.
添加这个答案,因为我没有看到它包括在所有其他。
最小化类型和符号之间的隐式转换:
这至少适用于C/ c++,即使你已经认为你已经摆脱了转换——有时测试在需要性能的函数周围添加编译器警告是很好的,特别是注意循环中的转换。
特定于GCC:您可以通过在代码周围添加一些冗长的pragmas来测试这一点,
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic error "-Wsign-conversion"
# pragma GCC diagnostic error "-Wdouble-promotion"
# pragma GCC diagnostic error "-Wsign-compare"
# pragma GCC diagnostic error "-Wconversion"
#endif
/* your code */
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
我曾见过一些案例,你可以通过减少这样的警告所带来的转化率来获得几个百分点的加速。
在某些情况下,我有一个带有严格警告的头,我保留了这些警告,以防止意外转换,然而这是一种权衡,因为您可能最终会为安静的故意转换添加大量强制转换,这可能会使代码更加混乱,而收益却微乎其微。