作者Lipraxde (静夜)
看板CompilerDev
标题[分享] Header Time Optimization (HTO)
时间Sun Sep 20 01:17:43 2020
Cross-Translation Unit Optimization via Annotated Headers
以前在 LLVM youtube 频道上看到的:
https://www.youtube.com/watch?v=elmio6AoyK0
LLVM 在优化程式码的时候,会对 IR 顺便添加一些 attribute。
例如:
define i32 @square(i32) {
%2 = mul nsw i32 %0, %0
ret i32 %2
}
会发现经过 opt 优化後,square 这个 function 被加上了:norecurse、nounwind、
readnone 这三个 attribute。
https://godbolt.org/z/6n47h6
而所谓的 HTO 呢,就是把这些 attribute 放到 header file 里,利用这种方式提供跨
Translation Unit (TU) 的优化能力。
让我们来看看影片里的例子:
double norm(double *A, int n);
void normalize(double *out, double *in, int n) {
for (int i = 0; i < n; ++i) {
out[i] = in[i] / norm(in, n);
}
}
如何才能使 for loop 里的计算被 vectorized?
一般来说,因为 norm 的 definition 在不同的 TU 里,compiler 是没办法主动做
vectorization 的,所以不想改程式的话就只好开启 LTO 了。
然而,若是在无法取得 source code 情况下是无法开 LTO 的 QQ。
这时候就是 HTO 派上用场的时间啦~
HTO 可以把由 LLVM 优化所添加的 attribute 放到 header file 里:
__attribute__((fn_attr("readonly"), fn_attr("argmemonly")))
double norm(double* A, int n);
* fn_attr 是 HTO 提供的特殊 attribute
这样一来,compiler 就能知道 norm 这个 function:
1. 不会透过 pointer 写入
2. 只使用它的 argument
这样 compiler 在优化 normalize 的时候呢,就可以把对 norm 的呼叫移到 for loop
外,对剩下的运算进行 vectorization。
心得:
原则上,任何优化要是在「不改变程式行为的前提下」才能做。
Aggressive 的优化当然也不能无法无天。
事实上所谓 aggressive 的优化,是对程式码的假设变得 aggressive,譬如说假设不会
发生越界存取之类的(-faggressive-loop-optimizations)。
那...为什麽开了优化後程式出问题了?
绝大多数的情况是因为写了 Undefined Behavior,或是说破坏了某些 compiler 做的假
设。
Compiler 是真的不会变魔术,有时候由於提供的资讯不够, 并不能做出想像中的优化。
因此,使用一门程式语言,除了学会语法外,对其 compiler 的熟悉度也是很重要的。
就譬如文中所述的例子,其实写的时候把 norm 移到 for loop 外即可。
了解「什麽情况下 compiler 可以优化这段程式码」才能更加善用程式语言,写出更有效
率的程式。
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 180.177.1.12 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/CompilerDev/M.1600535873.A.C8D.html
※ 编辑: Lipraxde (180.177.1.12 台湾), 09/20/2020 01:27:03
1F:推 mshockwave: 推推 这种方法感觉就像把 ThinLTO 的 summary搬到原始 09/23 04:29
2F:推 mshockwave: 码阶层 毕竟ThinLTO summary也只记载函式介面的属性 09/23 04:30
3F:→ Lipraxde: 不过 HTO 没办法拿来 inline function,而且最後也没被 09/23 18:10
4F:→ Lipraxde: 合并到 LLVM 里QQ 09/23 18:10
5F:推 unimaybe: 实用,推推!! 06/26 04:10