我正在为图形编写一个专门的小型C99库,我经常得到这种形式的循环:
for(int i = 0; i < graph->nvertices; ++i) {
// ...
}
我想知道这是不是一个好习惯,特别是在Tigh loop的情况下。起初我认为编译器足够聪明,只能看一次'graph-&gt; nvertices',而不是在每次迭代时查看它,但似乎不可能因为graph-&gt; nvertices可能在循环内部发生变化。写作更聪明,更快:
const int N = graph->nvertices;
for(int i = 0; i < N; ++i) {
// ...
}
它似乎更快,因为它不需要多次查看指针,但它确实需要创建一个新变量。
注意:我想这是一种情况,能够阅读一些汇编代码以查看编译器实际正在做什么,如果有人有一个很好的参考我很乐意接受建议。
答案 0 :(得分:3)
我倾向于自己做这些优化。有时编译器可以推断nvertices
在整个循环中没有变化,但是如果你调用可能更改值的其他函数怎么办?编译器无法推断它,也可能无法优化代码。
此外,最好的方法是分析您的代码以查看两种方法之间的比较。
答案 1 :(得分:3)
尝试使用更高的优化设置,一些编译器应该能够为您优化。您也可以向后迭代,只用表达式初始化计数器:
for (int i = graph->nvertices; i >= 0; --i)
..
然而,您将对缓存性能造成严重破坏。我认为你建议的方法是最直接的,这有助于编译器和下一个阅读代码的人清楚。
答案 2 :(得分:1)
我经常使用它。
const int N = graph->nvertices;
int i = 0;
for(; i < N; ++i) {
// ...
}
答案 3 :(得分:1)
答案可能取决于您的编译器,但大多数编译器会为您生成可以学习的汇编列表。只需确保列出发布版本。
但是,如果我真的关心每一点性能,我可能会按照你的建议创建单独的计数变量。
最后,我怀疑它会产生明显的差异。
答案 4 :(得分:1)
您想方设法查看汇编程序代码。为此,您可以使用objdump
程序,如下所示:
objdump -d executable
要过滤掉主要功能,请使用:
objdump -d executable | sed -n '/<main>/,/^$/p'
答案 5 :(得分:0)
这似乎是一个相当简单的测试方法,只需查看编译器的输出即可。整个程序优化通常会捕获这些低级优化。
另一方面,即使速度略有提高,我也不会进行这些类型的优化,因为我发现第一种形式更容易阅读和维护。
答案 6 :(得分:0)
我要去本地化变量的范围。然后,优化器将自行解决所有问题:
for(size_t i = 0, n = graph->nvertices; i < n; ++i) {
// ...
}