【问题标题】:How to change optimization level of one function?如何更改一项功能的优化级别?
【发布时间】:2015-10-01 03:47:01
【问题描述】:

这与 Determine cause of segfault when using -O3? 有关,在问题中,当使用特定版本的 GCC 使用 -O3 编译时,我在特定函数中发现了段错误。在-O3,使用了向量化指令(在-O2,它们没有使用)。

我想将单个函数包装在较低的优化级别。根据Switching off optimization for a specific function in GCC 4.2.2,我可以做到。但是,按照问题和答案中的各种链接,我没有找到“确切地说,如何去做”的答案。

如何标记单个函数以使用不同的优化级别?


相关,我不想将此函数移动到单独的文件中,然后为其提供不同的 makefile 配方。这样做会打开另一个蠕虫罐,例如仅在某些平台上将其应用于 GCC 4.9。

【问题讨论】:

  • 只是备份你真的不想做单独的文件选项,就好像你用不同的选项编译不同的单元,你是在咕咕咕。跨度>
  • 对我来说听起来像是XY-problem。如果您的代码在优化方面表现不佳,它很可能表现出未定义的行为。如果没有优化就没有出现实际上是一个不好的迹象。与其试图摆弄优化,不如明确地寻找原因。当然,除非您能证明这是由于编译器中的错误造成的。在这种情况下,问题就出现了,您如何保证此错误不会出现在其他地方。在这里,使用补丁或更新的编译器是正确的方法。
  • @Olaf - 问题出现在-O3,但不在-O2。在-O3,GCC 使用 SSE 指令。我能说的最好的,vmovdqa 需要 128 位对齐的字,但代码不能保证它。当使用这个特定的代码路径时,代码保证数组是 64 位对齐的。使用 Clang 及其未定义的行为清理程序,代码也很干净。我想我正在解决编译器错误。
  • @jww 你是在说 mingw-w64 中的this bug 吗?
  • @Matt - 可能是一样的,但我不确定。我没有 GCC 经验/专业知识可言。 (我已经习惯了只使用 GCC 的事情。当我遇到不是我的错的问题时,这种情况很少见)。

标签: c gcc optimization pragma


【解决方案1】:

我知道这个问题被标记为 GCC,但我只是 looking into doing this portably 并认为结果可能对某人有用,所以:

  • GCC 有一个optimize(X) 函数属性
  • Clang 具有optnone 和minsize 函数属性(使用__has_attribute 来测试支持)。因为我相信 3.5 它也有#pragma clang optimize on|off。
  • 英特尔 C/C++ 编译器具有 #pragma intel optimization_level 0,适用于编译指示之后的下一个函数
  • MSVC有#pragma optimize,适用于pragma之后的第一个函数
  • IBM XL 有 #pragma option_override(funcname, "opt(level,X)")。请注意,13.1.6(至少)为 __has_attribute(optnone) 返回 true,但实际上并不支持它。
  • ARM有#pragma Onum,可以加上#pragma push/pop
  • ODS 有 #pragma opt X (funcname)
  • Cray 有 #pragma _CRI [no]opt
  • TI 有 #pragma FUNCTION_OPTIONS(func,"…") (C) 和 #pragma FUNCTION_OPTIONS("…") (C++)
  • IAR 有 #pragma optimize=...
  • Pelles C 拥有#pragma optimize time/size/none

因此,对于 GCC/ICC/MSVC/clang/IAR/Pelles 和 TI C++,您可以定义一个刚刚放在函数前面的宏。如果您想支持 XL、ODS 和 TI C,您可以添加函数名称作为参数。 ARM 需要在函数之后使用另一个宏来弹出设置。对于 Cray AFAIK,您无法恢复之前的值,只能关闭再打开优化。

我认为这样做的主要原因是禁用对有问题的编译器(或暴露代码中的错误的编译器)的优化,因此统一的可移植体验可能并不重要,但希望此列表可以帮助某人找到合适的他们的编译器的解决方案。

编辑:同样值得注意的是,禁用优化是相对常见的,因为之前工作的代码不再工作了。虽然编译器中可能存在错误,但您的代码更有可能依赖于未定义的行为,并且更新、更智能的编译器可以并且将会忽略未定义的情况。在这种情况下,正确的答案不是禁用优化,而是修复您的代码。 clang 和 gcc 上的 UBsan 在这里可以提供很多帮助;使用-fsanitize=undefined 编译,许多未定义的行为将在运行时开始发出警告。另外,尝试使用所有可以启用的警告选项进行编译;对于 GCC,这意味着 -Wall -Wextra,对于铿锵声输入 -Weverything。

【讨论】:

【解决方案2】:

在https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes中有描述

您可以通过这样声明函数来更改级别:

void some_func() __attribute__ ((optimize(1))) {
    ....
}

为其强制优化级别 1。

【讨论】:

  • 有便携式解决方案吗?这可以移植到clang吗?
  • 不,它是 GCC 特定的。 Clang 有一个更严厉的optnone,它会扼杀所有优化。但两者都只是自定义扩展。函数级优化没有标准。
  • 感谢病毒。我希望有一个可移植的解决方案,因为这是一个跨平台库。在 Android、BSD、iOS、Linux、OS X、Solaris 和 Windows 上使用相同的源代码集。
  • 属性必须在函数签名之前,而不是在我的例子之后。
【解决方案3】:

下面是如何使用编译指示:

#pragma GCC push_options
#pragma GCC optimize ("-O2")
void xorbuf(byte *buf, const byte *mask, size_t count)
{
   ...
}
#pragma GCC pop_options

为了使其可移植,如下所示。

#define GCC_OPTIMIZE_AWARE (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) || defined(__clang__)

#if GCC_OPTIMIZE_AWARE
# pragma GCC push_options
# pragma GCC optimize ("-O2")
#endif

它需要被包装因为-Wall,旧版本的 GCC 不理解-Wno-unknown-pragma,它们会导致编译噪音。在现场会遇到旧版本,例如 OpenBSD 上的 GCC 4.2.1。

但根据来自 GCC 邮件列表的 When did 'pragma optimize' become available? 上的 Markus Trippelsdorf:

这通常是个坏主意,因为“pragma GCC optimize”的意思是 仅编译器调试帮助。它不应该在生产中使用 代码。

【讨论】:

    猜你喜欢
    • 2018-08-01
    • 2019-08-11
    • 2017-11-18
    • 2014-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多