【问题标题】:What are the possible side-effects of using gcc's -fno-math-errno?使用 gcc 的 -fno-math-errno 可能有哪些副作用?
【发布时间】:2019-06-11 19:20:29
【问题描述】:

在编译我的代码时,我通过使用此选项获得了巨大的加速。从手册页上说这个选项:

在调用执行的数学函数后不要设置“errno” 使用单个指令,例如“sqrt”

我不清楚 'errno' 是什么,以及它如何在其他地方使用(这与程序的退出代码相同吗?)此外,手册页说:

依赖 IEEE 异常来处理数学错误的程序可能 希望在保持 IEEE 算法的同时使用此标志来提高速度 兼容性。

我不清楚这意味着什么,是 c++ 的 IEEE 异常标准还是 c++ 的通用库(例如 Eigen、Boost 等)

基本上,我正在尝试确定这是否是在我的代码中使用的“安全”选项,或者我应该从它的使用中注意哪些副作用。 previous answer 说这可能会影响“线程局部变量”,但我不知道这意味着什么。

编辑:我的代码是一个简单的一次性程序代码,用于处理科学问题。它不会成为需要复杂错误处理的深度嵌入式系统的一部分。但是,代码不能以微妙、无声的方式失败。

【问题讨论】:

  • 许多标准数学函数在错误上设置errno。禁用它允许更简单、更优化的代码路径,但代价是更难检测错误。
  • @Shawn 看起来不错的答案
  • 使用-fno-math-errno 的数学函数等效于使用[[gnu::const]] 声明的函数function attribute。这大大改善了优化。
  • @LightnessRacesinOrbit 也许你高估了优化器:demo.

标签: c++ gcc g++


【解决方案1】:

C 引入了单个 errno 或“错误号”的概念,可用于确定某些标准函数(包括数学函数)无法履行其职责的确切原因。 POSIX 扩展了errno,C++11 也采用了其中的一些扩展。

当这些功能之一失败时,the global integer errno is set to a value that tells you why。显然,这会导致性能损失。

GCC 表示此标志禁用某些数学函数的这种行为,以牺牲诊断实用程序和标准合规性为代价来加速您的程序。

如果您在任何受影响的功能失败时已经没有观察errno,并且您可能不是因为您从未听说过它,那么您不会失去任何功能。如果您正在编写一个真正时间受限的程序,或者当失败的特定原因并未决定您的代码接下来要做什么时,可能会出现这种情况。 (但我建议更广泛地研究错误检查,以确保您的代码具有应有的稳健性。)


但是,如果您有错误,您可能会意外观察到副作用。想象一下,如果您正在执行以下操作:

std::some_maths_function(42);
if (errno == ERANGE)
   exit(-1);

哎呀!即使some_maths_function(42) 成功,我们也会检查errno。假设上面这段代码是对std::some_other_maths_function(999) 的调用,它通常会失败并导致errno 被设置为EDOM。这掩盖了上面代码中的错误。

现在打开编译器标志。如果some_other_maths_function 是不再设置errno 的函数之一,那么您的错误将被揭露,您可能需要一些时间才能意识到您应该编写类似以下的内容:

if (std::some_maths_function(42) == -1)
{
   if (errno == RANGE)
      exit(-1);
}

当然,你有一个错误几乎不是 GCC 的错,但这确实显示了在启用这个“优化”标志后你的程序的行为会如何不同。确实,the documentation 说:

任何 -O 选项都不会打开此选项,因为它可能导致依赖于数学函数的 IEEE 或 ISO 规则/规范的精确实现的程序的错误输出。但是,对于不需要这些规范保证的程序,它可能会产生更快的代码。

换句话说,这个标志会让你离开书本。但这可能是值得的。


变量errno 必须是线程本地的(也就是说,程序中的每个线程实际上都有一个变量),否则它在多线程程序中将毫无用处。您可以研究术语“线程本地”以获取有关此主题的更多信息。

【讨论】:

  • 如果我正确阅读了en.cppreference.com/w/cpp/numeric/math/math_errhandling,只要您的 libc 通过将math_errhandling 设置为适当的值 (2) 来很好地使用该选项,使用它仍然可以合规。跨度>
  • @Shawn 仅当使用浮点异常时!我只是在猜测,但我想这个标志不会影响 math_errhandling 的值,而是如果它的值被 MATH_ERRNO 掩蔽了,就会使实现不兼容。
  • @Shawn 实际上,文档确实说:“此选项未被任何 -O 选项打开,因为它可能导致依赖于 IEEE 或数学函数的 ISO 规则/规范。但是,对于不需要这些规范保证的程序,它可能会产生更快的代码。” 换句话说,这个标志会让你离开书本。
  • 除非你使用一些时髦的硬件,否则你应该有 fp 异常,因为它们是 IEEE 浮点所必需的。
  • glibc 至少看起来正确定义了math_errhandling。在假设数学函数设置 errno 之前检查它的程序数量,现在......这可能是一个非常小的数字。而且 C99/C++11 之前的标准根本没有这个特性。它也会破坏旧代码。
【解决方案2】:

请注意,当使用 -fno-math-errno 时,GCC 也可能会禁用非数学函数的 errno,例如 malloc。 [0]

[0]-fno-math-errno causes GCC to consider that malloc does not set errno

【讨论】:

    猜你喜欢
    • 2019-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-12
    相关资源
    最近更新 更多