【问题标题】:__restrict vis-a-vis a function optimization behavior of popular compilers__restrict 相对于流行编译器的函数优化行为
【发布时间】:2019-02-23 22:51:22
【问题描述】:

考虑以下函数:

int bar(const int* __restrict x, void g())
{
    int result = *x;
    g();
    result += *x;
    return result;
}

由于调用g(),我们是否需要从x 读取两次?或者__restriction 是否足以保证g() 的调用不会访问/不会更改地址x 处的值?

在this link,我们看到最流行的编译器不得不说这个(GodBolt;语言标准 C99,平台 AMD64):

  • clang 7.0:遵守限制。
  • GCC 8.3:没有限制。
  • MSVC 19.16:没有限制。

clang 是否正确地优化了第二次读取,不是吗?我在这里询问 C 和 C++,因为行为是相同的(感谢@PSkocik)。

相关信息和一些注释:

【问题讨论】:

  • 为什么不标记它[c]?这是这个问题的合法标签(可能比 C++ 标签更合法),你会得到更多的眼球。
  • @PSkocik:在看到您的 GodBolt 示例后 - 进行相应的编辑。
  • 为了对g() 进行适当的优化,编译器必须确定它会注意到任何可能导致x 的地址在运行期间暴露给外界的操作bar 的执行。在这种情况下,显然没有这样的动作,但是除非编译器能够注意到所有这样的动作,否则最好假设引用可能已经泄漏,而实际上它没有泄漏,而不是假装它不会泄漏。它有。
  • @supercat:如果我们不使用restrict,你说的是真的。如果我们这样做,编译器不必确定这一点,它需要“相信我的话”。
  • @einpoklum:函数g() 将被允许访问与*x 相同的存储,当且仅当它使用基于x 的指针(而不仅仅是@987654343 所在的指针@是基于)这样做。如果编译器不可能合法地接收到这样的指针,则编译器只能假定g() 无法访问*x。在没有__restrict 的情况下,编译器必须考虑到在调用bar 之前地址可能已经暴露给外部代码的可能性。

标签: c gcc visual-c++ clang restrict


【解决方案1】:

我认为这实际上是一个 C 问题,因为 C 实际上是具有 restrict 的语言,并附有正式规范。

管理restrict 使用的C 标准部分是6.7.3.1:

1 令 D 是一个普通标识符的声明,它提供一个 将对象 P 指定为限制限定指针的方法 键入 T。

2 如果 D 出现在块内并且没有存储类 extern, 令 B 表示块。如果 D 出现在参数列表中 函数定义的声明,让 B 表示关联的 堵塞。否则,令 B 表示 main 块(或 在独立的程序启动时调用的任何函数 环境)。

3 在下文中,指针表达式 E 被称为基于 对象 P if (在 B 执行之前的某个序列点 评估 E) 修改 P 以指向数组对象的副本 它以前指向的位置会改变 E.137 的值)注 ''based'' 仅针对具有指针类型的表达式定义。

4 在每次执行 B 期间,令 L 为任何具有 &L 的左值 P. 如果 L 用于访问它的对象 X 的值 指定,并且 X 也被修改(以任何方式),则以下 适用要求: T 不应是 const 限定的。每隔一个左值 用于访问 X 的值的地址也应基于 P。 每个修改 X 的访问都应被认为也修改了 P,因为 本款的目的。如果 P 被赋值为 a 基于另一个受限指针的指针表达式 E 对象 P2,与块 B2 关联,然后执行 B2 应在 B 执行之前开始,或 B2 的执行应 在分配之前结束。如果不满足这些要求,那么 行为未定义。

5 这里 B 的执行是指执行的部分 与标量对象的生命周期相对应的程序 与 B 关联的类型和自动存储时长。

按照我的阅读方式,g()的执行属于bar的块的执行,所以g()不允许修改*x和clang是 优化第二次加载的权利(IOW,如果 *x 指的是非常量全局,g() 不得修改该全局)。

【讨论】:

  • @einpoklum 这里的基于应该是指通过一些(可能没有)指针算法派生但没有取消引用。给定T *restrict P;、P、P+2、P-42 都是基于P。优化应该没问题的原因应该主要在(4.)......不幸的是,这部分C标准是用一种超级神秘的方式编写的,IMO。
  • clang 和 gcc 处理 restrict 的方式,即使是像 p[1] 这样的表达式,在某些指针不基于 p 的上下文中也不会被视为可识别基于 p已知等于p+1。
猜你喜欢
  • 1970-01-01
  • 2018-07-19
  • 2021-07-31
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 2021-02-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多