繁体   English   中英

不同编译器中的 pure/const 函数属性

[英]pure/const function attributes in different compilers

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

鉴于这些信息,编译器可以做一些额外的优化。

海湾合作委员会的例子:

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)); }

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

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属性。 到目前为止,MSVC是唯一不支持它们的编译器(也没有任何替代方案)。

首先,注意“const”是“纯”的更严格版本是有用的,因此如果编译器没有实现“const”,那么“pure”可以用作后备。

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

  • GCC支持 2.96+以及const自2.5.0以来,如果你想检查版本。
  • Clang支持两者; 你可以使用__has_attribute(pure)__has_attribute(const)来检测它们,但是依靠clang设置__GNUC__可能很好。 这还包括基于clang的编译器,如emscripten和XL C / C ++ 13+。
  • 英特尔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+与--gcc(与检测到的__TI_GNU_ATTRIBUTE_SUPPORT__ )支持。
  • PGI没有记录它(AFAICT),但这两个属性都有效(或至少被忽略)。 17.10+是安全的,尽管它们可能已经可以接受更长时间了。

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

Oracle Developer Studio也支持编译指示,因为它被称为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+(至少)支持#pragma FUNC_IS_PURE; pragma只在C ++模式下。 在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仓库而不是这个答案上,因为我更有可能让它保持最新状态。

作为这些年来阅读本文的任何人的更新,从 C++11(以及 C++14、C++17 和 C++20 的持续改进)开始,在语言中添加了constexpr关键字,让你将函数、方法、语句、模板、变量(几乎所有的东西)标记为绝对和完全常量,允许编译器进行很好的优化。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM