编译器优化可以消除在 for 循环的条件中重复调用的函数吗?
Posted
技术标签:
【中文标题】编译器优化可以消除在 for 循环的条件中重复调用的函数吗?【英文标题】:Can compiler optimization elminate a function repeatedly called in a for-loop's conditional? 【发布时间】:2013-11-25 18:08:36 【问题描述】:我正在阅读有关哈希函数的内容(我是一名中级 CS 学生)并遇到了这个问题:
int hash (const string & key, int tableSize)
int hasVal = 0;
for (int i = 0; i < key.length(); i++)
hashVal = 37 * hashVal + key[i];
.....
return hashVal;
我正在查看这段代码,并注意到如果在 for 循环中而不是每次我们这样做时调用 key.length() 会更快:
int n = key.length();
for (int i = 0; i < n; i++)
我的问题是,既然这是一种稍微提高性能的明显方法,编译器会自动为我们做这件事吗?我对编译器还不太了解,但我很好奇这个问题的答案。在编写代码以使用较少的操作时,人们经常指出,我所做的事情通常已经由编译器为我完成,所以我在浪费时间,而是在做诸如内联函数之类的事情。我关心这一点,因为我正在编写一个游戏,其中物理处理需要高效,这样事情就不会显得笨重。
【问题讨论】:
【参考方案1】:简短的回答:它有时可以...
长答案:
如果编译器可以根据循环本身确定 key.length() 是一个“常量”值,那么它将能够优化调用。这又取决于所使用的类的定义(在本例中为string
,我们可以预期它是“写得很好”)。它还依赖于编译器理解循环不会以改变key.length()
的方式改变key
。
此功能的关键要素是该函数是 inline
(或者是模板函数,需要 inline
以允许它在不同的编译单元中多次包含 - 或在同一源文件中可用) 并且源代码在编译单元包含的头文件中。
当然,C++ 标准中并没有要求编译器执行此操作。每次调用该函数都完全符合标准。
【讨论】:
从技术上讲,该函数不必被标记为内联即可内联,并且链接器可以跨编译单元进行更多优化。但只有使用正确的标志和编译器版本,仍然不允许这样做。 C++ 函数不需要标记inline
以便编译器内联它;尽管有关键字名称,但它实际上意味着不同的东西(允许多个定义)。
我已经澄清了“内联”部分。
内联关键字或头文件的使用不再相关 - 删除该段落可能更有意义 - 它减少的比增加的多
供应商也可以在不知道代码的情况下添加允许优化的属性。比如GCC有函数属性pure
和const
。【参考方案2】:
只有当key.length()
是一个纯函数——即没有副作用的函数时,才能安全地将其移出循环。如果编译器可以检测到是这样,那么我会说它很可能会执行优化。
不幸的是,与 Fortran 不同,C++ 没有标准的方法来将某些东西标记为纯。如果函数是inline
(或可内联的),那么编译器就有了定义,并且可以自行解决,或者至少消除函数调用。
将成员函数标记为const
保证它不会影响当前实例,但原则上没有理由key.length()
不能更改某些全局变量,所以我怀疑这本身就足够了。
(有多种编译器特定的方式来声明纯函数——比如 GCC 中的__attribute__((pure))
和兼容的编译器(Clang、Intel)。也许可以尝试这些方法,看看它们是否有什么不同?)
【讨论】:
【参考方案3】:这是一个非常依赖编译器的答案。唯一可以确定的方法是生成汇编程序并对其创建的内容有所了解(我建议学习如何使用您的编译器执行此操作并尝试它,这非常有启发性*)。
优化不是那么明显——编译器可以执行优化,但前提是确定.length()
不会改变——即没有其他线程可以访问数据并且它能够确定什么都没有循环内部将改变 .length()
后者很容易检查,因为它是一个 const 字符串,但前者不太容易。话虽如此,我希望编译器在中等优化水平下假定它在任何地方都是 const。
*双关语是故意的;对不起。
【讨论】:
【参考方案4】:有两个地方需要优化。
更智能的编译器将内省短函数以查看它们是否对if
子句产生影响。可能大多数编译器都会inline
调用,使其等同于您编写的内容。
与更“随机”的if
相比,在 cpu 级别的分支预测也会使其(显着)更快。 SO
上的最热门问题之一展示了一个很好的核心示例。
编辑:
我之前假设声明方法length() const
就足够了。但它太弱了。方法仍然可以在不改变对象状态的情况下抛出随机的东西。不过我 100% 肯定,体面的编译器会像我描述的那样内省函数和方法。
【讨论】:
以上是关于编译器优化可以消除在 for 循环的条件中重复调用的函数吗?的主要内容,如果未能解决你的问题,请参考以下文章