问题 GCC热属性的语义


假设我有一个由三个函数A,B和C组成的编译单元.A从编译单元外部的函数调用一次(例如,它是一个入口点或回调函数); A被A多次调用(例如,它在紧密循环中被调用);每次调用B都会调用一次C(例如,它是一个库函数)。

通过A(通过B和C)的整个路径对性能至关重要,尽管A本身的性能不是关键的(因为大部分时间花在B和C上)。

应该注释的最小函数集是什么 __attribute__ ((hot)) 实现更积极的优化这条路径?假设我们不能使用 -fprofile-generate

同等地:是的 __attribute__ ((hot)) 意思是“优化这个函数的主体”,“优化对这个函数的调用”,“优化这个函数所做的所有后代调用”,或者它们的某种组合?

GCC信息页面没有明确解决这些问题。


1585
2018-02-22 16:39


起源

__attribute__ ((hot)) 可能会获得一些东西,但是你可能会在首次制作B和C时获得更好的效果 static inline 和优化 -O3。 - twalberg
我假设已经采取了这些步骤。 - Chris Pacejo


答案:


官方文件

hot   函数的hot属性用于通知编译器该函数是已编译程序的热点。该功能进行了更积极的优化,并且在许多目标上,它被放置在文本部分的特殊子部分中,因此所有热门功能都紧密结合在一起,从而改善了局部性。   当配置文件反馈可用时,通过-fprofile-use,将自动检测热门功能,并忽略此属性。

功能的热属性未在4.3之前的GCC版本中实现。

标签上的hot属性用于通知编译器标签后面的路径比未注释的路径更可能。此属性用于无法使用__builtin_expect的情况,例如使用计算的goto或asm goto。

标签上的hot属性未在4.8之前的GCC版本中实现。

2007年

 __attribute__((hot))   

提示标记的功能是“热”并且应该更加积极地优化和/或放置在其他“热”功能附近(用于缓存局部性)。

Gilad Ben-Yossef

顾名思义,这些函数属性用于提示编译器在代码中经常调用相应的函数(热)或很少调用(冷)。

然后,编译器可以在分支中对代码(例如if语句)进行排序,以支持调用这些热函数的分支并拒绝函数冷函数,假设更可能是将要采用的分支将调用热函数并且不太可能叫冷。

此外,编译器可以选择将生成的二进制文件中特殊部分标记为热的函数组合在一起,前提是数据和指令缓存基于局部性或相关代码和数据的相对距离工作,所有通常一起调用函数将导致更好地缓存整个应用程序的代码。

热属性的良好候选者是核心函数,在您的代码库中经常调用它们。 cold属性的良好候选者是内部错误处理函数,只有在出现错误时才会调用它们。

所以,根据这些消息来源, __attribute__ ((hot)) 手段:

  • 优化对此功能的调用
  • 优化这个功能的主体
  • 把这个功能的身体 .hot 部分(将所有热门代码分组到一个位置)

在源代码分析之后,我们可以说用“hot”属性进行检查 (lookup_attribute ("hot", DECL_ATTRIBUTES (current_function_decl));当它成立时,功能就是 node->frequency 被设定为 NODE_FREQUENCY_HOT (predict.c,compute_function_frequency())。

如果函数的频率为 NODE_FREQUENCY_HOT

  • 如果没有个人资料信息,则没有 likely/unlikely 在树枝上, maybe_hot_frequency_p  将为函数返回true(==“...频率FREQ被认为是热的。”)。这会改变价值 maybe_hot_bb_p 对于函数中的所有基本块(BB)都为true(“BB可以是CPU密集型,应该针对最大性能进行优化。”)和 maybe_hot_edge_p 对于函数中的所有边都为true。反过来非 -Os - 模拟这些BB和边缘以及环路将针对速度进行优化,而不是针对尺寸进行优化。

  • 对于此函数的所有出站调用边, cgraph_maybe_hot_edge_p 将返回true(“如果呼叫可能很热,则返回true。”)。此标志用于IPA(ipa-inline.c,ipa-cp.c,ipa-inline-analysis.c)并影响内联和克隆决策


15
2018-02-22 23:05



不要这么认为。它不完整,需要进行一些实验。我认为,热也可能会影响代码,从热函数调用。 - osgx
你可以期待gcc现在或将来将函数的热度传播到它的祖先和后代分支以及它们调用gcc可以确定调用图的函数。 - codeshot
@codeshot如果这是盲目地对所有祖先和后代进行的,那么大多数时候整个程序会被标记为热,不是吗?此外,在函数具有多个祖先且仅经常使用一个的情况下,这将是浪费。 - Jeevaka
这取决于你对盲人的定义,但关于多个祖先的好点。我在此暂停我的期望,等待进一步的了解 - codeshot
我想到了这一点,是的,如果“盲目地做”意味着“不考虑分支”,这将是一件荒谬的事情。但我绝对希望gcc实现者现在或将来能够将热度传播到最近的分支。如果一个分支被注释为“可能”,那么我希望它们通过分支传播热度 - 向下和向上传播,因为可能表示在热度被稀释之前需要大约7个左右分支的极端概率。 - codeshot