【问题标题】:pure/const function attributes in different compilers不同编译器中的纯/常量函数属性
【发布时间】:2010-05-09 15:49:58
【问题描述】:

pure 是函数属性,表示函数不会修改任何全局内存。
const 是函数属性,表示函数不会读取/修改任何全局内存。

根据这些信息,编译器可以进行一些额外的优化。

GCC 示例:

float sigmoid(float x) __attribute__ ((const));

float calculate(float x, unsigned int C) {
    float sum = 0;
    for(unsigned int i = 0; i < C; ++i)
        sum += sigmoid(x);
    return sum;
}

float sigmoid(float x) { return 1.0f / (1.0f - exp(-x)); }

在该示例中,编译器可以将函数 calculate 优化为:

float calculate(float x, unsigned int C) {
    float sum = 0;
    float temp = C ? sigmoid(x) : 0.0f;
    for(unsigned int i = 0; i < C; ++i)
        sum += temp;
    return sum;
}

或者如果你的编译器足够聪明(并且对浮点数不那么严格):

float calculate(float x, unsigned int C) { return C ? sigmoid(x) * C : 0.0f; }

如何为不同的编译器(即 GCC、Clang、ICC、MSVC 或其他)以这种方式标记函数?

【问题讨论】:

  • 对于我们这些不经常使用 gcc 的人,也许你可以发布一个关于什么是 pure/const 属性的解释。如果它是某种类型的优化,那么它可以帮助编译器生成更优化的程序集的 C 或 C++ 代码示例也会很有用。
  • 我不是编译器编写者,但在我看来,如果sigmoid() 函数定义可用于编译器解析@ 987654325@在同一个翻译单元。无论如何,当函数定义在不同的翻译单元中时,这些属性肯定会很有用。
  • 是的,GCC(和其他编译器)在这种情况下会自动在内部添加此属性。然后,基于属性,他们可以做进一步的优化(就像我的例子一样)。而且您恰好遇到了明确指定它的主要原因之一:如果编译器此时没有看到定义,或者如果定义根本不可用,或者如果您认为编译器减少数量是安全的调用函数。

标签: c++ gcc constants function-attributes


【解决方案1】:

一般来说,似乎几乎所有编译器都支持 GCC 属性。 MSVC 是迄今为止唯一不支持它们的编译器(也没有任何替代方案)。

【讨论】:

  • MSVC 也无意添加对这些属性的支持:connect.microsoft.com/VisualStudio/feedback/details/804288/…。他们说他们不需要它,这对应用程序有意义,但对库没有意义。
  • 将 GNU C 视为一种语言方言,或一组语言扩展。您可以使用#if __GNUC__ &gt; 4 || ... 或类似方法在任何编译器中测试特定GNU C 功能的可用性,推荐in the manual。 Clang(和其他编译器)只宣传他们完全支持的__GNUC____GNUC_MINOR__ 版本。实现可能与 gcc 不同(例如,clang 的 __builtin_constant_p() 不会将 const-ness 传播到内联函数的函数 args 中),但您的代码将编译。
  • 换句话说,clang 和 ICC 可能支持 gcc 6.0 的新功能,但在支持 all的版本中只会将 __GNUC__ 定义为 6 和 __GNUC_MINOR__ 定义为 0 > gcc6.0 所做的语言扩展。
【解决方案2】:

首先,请注意“const”是一个更严格的版本 “纯”,所以如果编译器不支持,“纯”可以用作后备 实现“const”。

正如其他人所提到的,MSVC 并没有类似的东西, 但是很多编译器都采用了 GCC 语法,包括很多 没有定义__GNUC__(有些有时会定义和 有时不会,具体取决于标志)。

  • GCC 支持 pure 从 2.96+ 开始,以及 const 从 2.5.0 开始,如果您想查看版本。
  • Clang 支持两者;你可以使用__has_attribute(pure)__has_attribute(const) 来检测它们,但可能没问题 只依赖clang设置__GNUC__。这也包括编译器 基于像 emscripten 和 XL C/C++ 13+ 这样的 clang。
  • 英特尔 C/C++ 编译器支持两者,但它们的文档是 太可怕了,所以我不知道它们是什么时候添加的。 16.0+当然是 安全。
  • Oracle Developer Studio 12.2+ 支持两者。
  • ARM C/C++ 编译器 4.1+(可能更早版本)同时支持 pureconst
  • IBM XL C/C++ 至少从10.1 开始。
  • TI 8.0+
  • TI 7.3+ with --gcc(使用__TI_GNU_ATTRIBUTE_SUPPORT__检测)支持两者。
  • PGI 没有记录它 (AFAICT),但两个属性都有效(或者是 至少默默地忽略)。 17.10+ 是安全的,尽管他们可能已经 可以接受更长的时间。

其中,clang 总是定义__GNUC__ 和朋友(目前 4.2,IIRC)。英特尔默认定义__GNUC__(虽然它可以 用 -no-gcc 抑制),就像 C++ 模式下的 PGI 一样(但不是在 C 中) 模式)。其他的你必须手动检查。

Oracle Developer Studio 也支持 pragma,因为它是已知的 作为Forte Developer 6。他们是 使用有点不同,因为它们需要您指定函数 名称:

/* pure: */
#pragma does_not_write_global_data (funcname)
/* const; SPARC-only until 12.2 */
#pragma no_side_effect (funcname)

TI 6.0+(至少)支持 C++ 中的 #pragma FUNC_IS_PURE; pragma 仅限模式。 C模式下为#pragma FUNC_IS_PURE(funcname);

其中大部分可以隐藏在宏后面,这就是我所做的 Hedley:

#if \
  HEDLEY_GNUC_HAS_ATTRIBUTE(pure,2,96,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
  HEDLEY_PGI_VERSION_CHECK(17,10,0)
#  define HEDLEY_PURE __attribute__((__pure__))
#elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus)
#  define HEDLEY_NO_RETURN _Pragma("FUNC_IS_PURE;")
#else
#  define HEDLEY_PURE
#endif

#if HEDLEY_GNUC_HAS_ATTRIBUTE(const, 2, 5, 0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
  HEDLEY_PGI_VERSION_CHECK(17,10,0)
#  define HEDLEY_CONST __attribute__((__const__))
#else
#  define HEDLEY_CONST HEDLEY_PURE
#endif

这不包括需要该功能的变体 名称作为参数,但它仍然涵盖了大量 用户,并且在任何地方都可以安全使用。

如果您不想使用 Hedley(它是一个公共域/CC0 标头),它 替换内部版本宏应该不会太难。如果 您选择这样做,您可能应该将您的端口基于 Hedley repo 而不是这个答案,因为我更有可能保留它 是最新的。

【讨论】:

    【解决方案3】:

    作为这些年来阅读本文的任何人的更新,从 C++11 开始(随着 C++14、C++17 和 C++20 的不断改进),添加了 constexpr 关键字语言,让您将函数、方法、语句、模板、变量(几乎所有内容)标记为绝对且完全恒定,从而允许编译器进行良好的优化。

    constevelconstinit 也被添加到 C++20 中,以进一步扩展该语言的编译时功能。

    【讨论】:

      猜你喜欢
      • 2012-09-06
      • 1970-01-01
      • 1970-01-01
      • 2013-03-15
      • 2021-04-07
      • 2018-04-07
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      相关资源
      最近更新 更多