我想添加一些。
你的助教说:
使用 -O3 可以启用硬件加速指令,例如 SSE。
我认为那并不完全正确。似乎 -f 选项旨在实现机器无关性。编译器会解析源代码,并将其转换成通常称为中间表示(IR)的东西。然后,编译器会对 IR 进行自身所谓的机器无关优化。然后,从优化后的 IR 生成汇编代码。生成汇编后,将应用不同的一组优化。IR 的示例包括 LLVM IR、Sun IR、GCC gimple tree 和/或 RTL 等。我相信所有现代编译器都有 IR。
我认为 GCC 的 -f 选项基本上是为了实现机器无关性。-m 选项用于机器相关的优化。-O2 或 -O3 决定 -f 选项,这些选项理想情况下应该是机器无关的。使用高级指令取决于编译器的机器相关部分。
实际上,机器相关和机器无关之间的边界可能并不是非常清晰。
-O3 打开的向量化是灰色区域中的一种优化示例。某些机器不支持 SIMD。有些机器支持,但是向量大小,例如,从架构到架构可能不同。这是我的示例代码:
long long int inner_product(int* v0, int* v1, int sz) {
long long int res = 0;
for (int i = 0; i < sz; i++) {
res += (v0[i] * v1[i]);
}
return res;
}
如果按照以下方式进行编译:
$ gcc -O3 -S -march=core2 code.c -o code.s -fdump-tree-all
Gcc对其进行向量化,但向量大小为2个长长整数或4个整数:
$ cat code.*.optimized | grep 'vector('
vector(2) long long int vect_res_16.21;
vector(2) long long int vect_res_16.19;
vector(2) long long int vect__8.18;
vector(4) int vect__7.17;
vector(4) int vect__6.16;
vector(4) int * vectp_v1.15;
vector(4) int vect__4.13;
vector(4) int * vectp_v0.12;
另一方面,如果使用 -march=skylake-avx512 进行编译,其向量大小比 core2 大 4 倍,结果将如下所示:
$ gcc -O3 -S -march=skylake-avx512 code.c -o code.s -fdump-tree-all && cat code.*.optimized | grep 'vector('
vector(8) long long int vect_res_16.21;
vector(8) long long int vect_res_16.19;
vector(8) long long int vect__8.18;
vector(16) int vect__7.17;
vector(16) int vect__6.16;
vector(16) int * vectp_v1.15;
vector(16) int vect__4.13;
vector(16) int * vectp_v0.12;
请注意,此时代码生成尚未开始。IR的外观取决于march值。我猜向量化不是唯一展示这种行为的例子。
然而,我认为将这样的优化称为机器相关并不公平。典型的机器相关优化是指令调度。它与中间表示几乎没有任何关系。它更多地涉及机器指令和微架构。对于那些处于灰色地带的机器无关优化来说,在概念上,编译器可以生成统一的向量化IR(比如,始终使向量大小为4),然后让代码生成器处理它(每个代码生成器可能需要合并或拆分向量操作数)。然而,在实现上,在IR级别上为向量操作数提供适当的大小被认为比在代码生成级别上更容易。所以,我猜人们放弃了生活在"理想世界"的想法。尽管如此,我相信机器无关优化仍然可以被称为机器无关。
你可能需要询问TA不想看到哪种花哨的指令。如果只是向量指令,所有与向量相关的标志都应该被禁用,但你可以使用-O3。我的示例代码可以用来查看是否仍然存在SIMD指令。
$ gcc -O3 -S code.c
$ cat code.s | egrep xmm
如果输出为空,则表示一切正常。