gcc的哪些-O3优化标志启用“硬件加速指令”?

3
基本上,对于我的项目,有一个限制,就是不能使用-O3标志(我们必须只使用-O2)。其原因是,-O3标志显然引入了“硬件加速指令”。
gcc版本为5.4,此版本优化标志的手册页面为:这里 我想尽可能包含-O3的标志。-O3引入的标志列表如下:
-finline-functions, -funswitch-loops, -fpredictive-commoning, -fgcse-after-reload, -ftree-loop-vectorize, -ftree-loop-distribute-patterns, -ftree-slp-vectorize, -fvect-cost-model, -ftree-partial-pre and -fipa-cp-clone

我计划使用-O2并手动包含尽可能多的上述标志。

以上哪些标志启用了“硬件加速指令”优化?通过阅读描述,我如何确定标志是否启用了“硬件加速指令”优化?这是什么构成的?


1
基本上,任何使用向量化的东西都很可能利用了特定的硬件支持,以比“简单”的逐元素指令更快地工作,但是我对这个问题还有点不清楚。 "硬件启用的指令" 字面上描述了每个指令;它必须在实际芯片上运行(或者在另一块芯片上模拟这样的芯片的软件中),它并非魔法。 - ShadowRanger
3
所有指令都是“硬件加速”的。它们在硬件上运行。 - Nikos C.
1
你真的应该告诉我们更多关于为什么你不能使用那些不应该使用的指令来得到一个正确的答案。 - user1143634
1
我不确定这可以是什么样的任务——我不明白为什么需要这样的限制。但编译器可以自由使用任何指令,即使是 -O2,它肯定会使用它们。你应该查看约翰的答案,它解释了你的情况。 - user1143634
1
@Bob 没有一个。编译器已经在 -O2 级别上使用了“硬件加速指令”。我认为下面的答案很好地解释了这一点。请查看此代码:https://ideone.com/UWyQtC,并尝试在 https://godbolt.org/ 上使用 GCC 在 -O2 级别进行构建。你会发现它即使在 -O2 级别下也生成了 SSE 指令。 - user1143634
显示剩余8条评论
3个回答

4

所使用的指令集受-march控制,而不受-O3控制。虽然-O3会更多地利用SIMD指令进行矢量化,但-O3在代码生成期间并没有特别添加或删除指令。

如果您想只使用最简单的指令编译代码,请为您的平台选择最简单的march。例如,-march=core2是x86-64的保守选择,因为它指的是相当旧的Intel Core 2处理器系列。

尽管如此,Core 2支持MMX和SSE到SSE3和SSSE3。要禁用这些功能,添加:

-mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-ssse3

我的助教说:“使用 -O3 可以启用硬件加速指令,如 SSE”。由于实验手册上说我们不能使用硬件加速指令,所以我们不能使用 -O3。我想知道我可以/不能使用上面列表中的哪些优化选项。 - Fuad
5
@Bob:你的助教是错误的。在-O3以下的编译选项中,SSE被启用,因为为64位x86-64 CPU编译已经保证至少能使用一些SSE指令(据我所知,最旧的x86-64 CPU保证可用到SSE2)。你需要像John建议的那样明确地禁用它们,否则编译器将在-O2自由地使用它们。 - ShadowRanger
2
我认为大部分代码在x86-64上使用-mno-sse参数编译时都无法通过编译,因为ABI要求使用SSE寄存器。 - keltar

3
我感觉John已经回答了这个问题,我会尝试提供一些例子。
考虑以下的最小程序:
#include <cstring>

void copy(long *dst , const long *src)
{
    std::memcpy(dst, src, sizeof(long) * 4);
}

使用GCC 7.2编译,x86_64平台下g++ -O2得到以下输出:

copy(long*, long const*):
  movdqu (%rsi), %xmm0
  movups %xmm0, (%rdi)
  movdqu 16(%rsi), %xmm0
  movups %xmm0, 16(%rdi)
  ret

使用GCC 7.2编译,g++ -O2 -mno-sse在x86_64上运行,输出如下:

copy(long*, long const*):
  movq (%rsi), %rax
  movq %rax, (%rdi)
  movq 8(%rsi), %rax
  movq %rax, 8(%rdi)
  movq 16(%rsi), %rax
  movq %rax, 16(%rdi)
  movq 24(%rsi), %rax
  movq %rax, 24(%rdi)
  ret

正如您所看到的,即使在-O2级别下,GCC也能够生成SSE指令。需要单独的标志来抑制生成这些指令。
同时,GCC 5.4生成相同的代码,无论是否使用-mno-sse标志,但它也为-O3优化级别执行相同的操作。
因此,您的目标有点误导人。在某些情况下,使用-O2标志的超集可能会抑制生成SSE和类似指令,但这并不保证优化级别与生成哪些指令只间接相关。如果您真的想抑制这些指令,可以使用-mno-sse标志,但这可能会使您处于不利地位。只需坚持使用-O2 - 这样每个人都将处于平等的条件下。
我使用https://godbolt.org/进行演示。

0

我想添加一些。

你的助教说:

使用 -O3 可以启用硬件加速指令,例如 SSE。

我认为那并不完全正确。似乎 -f 选项旨在实现机器无关性。编译器会解析源代码,并将其转换成通常称为中间表示(IR)的东西。然后,编译器会对 IR 进行自身所谓的机器无关优化。然后,从优化后的 IR 生成汇编代码。生成汇编后,将应用不同的一组优化。IR 的示例包括 LLVM IR、Sun IR、GCC gimple tree 和/或 RTL 等。我相信所有现代编译器都有 IR。

我认为 GCC 的 -f 选项基本上是为了实现机器无关性。-m 选项用于机器相关的优化。-O2 或 -O3 决定 -f 选项,这些选项理想情况下应该是机器无关的。使用高级指令取决于编译器的机器相关部分。

实际上,机器相关和机器无关之间的边界可能并不是非常清晰。

-O3 打开的向量化是灰色区域中的一种优化示例。某些机器不支持 SIMD。有些机器支持,但是向量大小,例如,从架构到架构可能不同。这是我的示例代码:

// code.c
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

如果输出为空,则表示一切正常。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接