C23 中的 [[reproducible]] 和 [[unsequenced]] 属性是什么,什么时候应该使用它们?

问题描述 投票:0回答:1

C23 引入了属性

[[reproducible]]
[[unsequenced]]

  • 他们背后的动机是什么?
  • 它们是如何定义的,它们对函数有什么影响?
  • 我应该将它们应用于什么样的功能?
c attributes c23
1个回答
4
投票

引发问题的是,当没有可用的函数定义时,编译器无法洞察函数。这会阻止几乎所有编译器优化(除非使用 LTO)。

考虑以下示例:

// note: this is redundant, because [[unsequenced]] implies [[reproducible]]
int square(int x) [[reproducible]] [[unsequenced]];

int arr[] = {
    square(2),
    square(2),
    square(3),
    square(3),
};

即使编译器没有

square
的定义,也允许执行两种优化:

  • 因为该函数是
    [[reproducible]]
    ,连续调用两次
    square(2)
    会产生相同的结果,并且编译器可以决定仅调用
    square(2)
    一次
  • 因为函数是
    [[unsequenced]]
    ,所以可以按任意顺序调用
    square
    ,编译器甚至可以决定在程序启动时只计算一次
    square(2)
    。它还可以决定在
    square(3)
    之前评估
    square(2)
    ,如果这样更有效的话。

此类优化也可以通过在标头中定义函数

inline
来实现,并让编译器可以自行推断这些属性。然而,对于复杂的函数,由于增加了编译速度,使所有内容都
inline
是不可行的。

半正式定义

更严格的解释,请参阅 C23 标准工作草案 N3096 §6.7.12.7 函数类型的标准属性。

[[reproducible]]

此属性断言函数是可重现函数,这意味着

  • 没有效果,而且
  • 它是幂等

Effectless 限制函数可以修改的状态。如果修改任何非本地状态,则只能通过传递给它的指针来发生。例如,如果

void to_upper_case(char* str)
函数仅修改局部变量和 str 的内容,则它是
effectless
。 (直观上,该函数没有可观察到的副作用。)

幂等意味着多次调用该函数与调用一次具有相同的效果。例如,我们可以调用

to_upper_case(s); to_upper_case(s);
,它与只调用一次具有相同的效果。

[[unsequenced]]

此属性断言函数是一个 无序函数,这意味着

  • 它是无效果幂等(这也使得它可重现
  • 它是无状态
  • 独立

无状态意味着

static
thread_local
局部变量不能是非
const
,也不能是
volatile

独立意味着函数的所有调用都会看到相同的全局变量值,不会改变全局状态,也不会通过指针参数改变任何状态。

to_upper_case
不是独立的,但像
strlen
这样的函数可以是独立的。

直观地说,未排序函数可以任意排序,甚至可以在其观察到的状态变化之间并行排序:(另请参阅标准中的脚注 196)

char *str = /* ... */;  // A
  strlen(str);
  global = 123;
  strlen(str);
strcpy(str, /* ... */); // B

在此示例中,点

strlen
A
之间可以有一次、两次或无限多次对
B
的调用。这些可以顺序发生,也可以并行发生。无论如何,“未排序”函数的结果必须相同。 global的突变不允许改变
strlen
的结果。
GCC 属性注意事项

GCC 属性

pure

const
 是这些标准属性的灵感来源,并且行为类似。请参阅
N2956 5.8 与 GCC const 和 pure
的一些差异进行比较。简而言之:

    pure
  • [[independent]]
    更轻松
    
  • const
  • [[unsequenced]]
    更严格
    
  • 何时使用这些属性

这些属性适用于想要利用编译器优化的高级用户。

一般来说,应用它们时必须非常小心。该程序格式不正确,如果将它们应用于不具有断言属性的函数,则无需进行诊断。我们鼓励编译器检测这些属性的此类滥用,但这不是必需的。

常见示例(和惊喜)

    printf
  • 显然两者都不是
  • strlen
  • memcmp
    可以是
    [[unsequenced]]
  • memcpy
  • 可以是
    [[reproducible]]
  • memmove
  • 也不可能,因为对于重叠的内存区域来说它不是
    幂等
  • fabs
  • 可以是
    [[unsequenced]]
  • sqrt
  • 也不可能,因为它会修改浮点环境并可能设置
    errno
    
    
  • 另请参阅

GNU C 中的

    __attribute__((const)) 与 __attribute__((pure))
  • C++11 的 constexpr 和 C23 的 [[reproducible]] 有什么区别?
  • 如何使用GCC的pure属性?
© www.soinside.com 2019 - 2024. All rights reserved.