【问题标题】:Can I `__restrict__ this` somehow?我可以以某种方式`__restrict__ this`吗?
【发布时间】:2015-07-05 05:05:52
【问题描述】:

我在 CppCon 2014 上一直在看 Mike Acton's talk on Data-oriented design in C++,他举了这个例子:

int Foo::Bar(int count)
{
    int value = 0;
    for (int i = 0; i < count; i++) {
        if (m_someDataMemberOfFoo) value++
    }
    return value;
}

并解释了一些编译器如何在每次迭代中不断重新读取m_someDataMemberOfFoo,这可能是因为它的值可能会由于并发访问而改变。不管编译器这样做是否合适 - 是否可以告诉编译器在执行某些方法期间专门忽略任何并发访问任何内容的可能性,以便它可以更好地优化?

换句话说,我可以告诉编译器this 是__restrict__ed 吗?

【问题讨论】:

  • 任何名副其实的编译器都会考虑将检查提升到循环之外,或者至少将m_someDataMemberOfFoo 保存在寄存器中。任何尝试与此代码同时修改m_someDataMemberOfFoo 的程序都会表现出未定义的行为。
  • @IgorTandetnik:不一定。这是一个不合理的语义假设。也许m_someDataMemberOfFoo应该通过并发修改来改变?
  • 我觉得这个假设在上下文中是合理的。如果m_someDataMemberOfFoo 是线程安全的(比如atomic),并且预计它会被同时修改,那么整个问题就没有实际意义了。 “some 编译器在每次迭代中不断重新读取m_someDataMemberOfFoo”并不是这种情况——所有编译器都会这样做,这将是完全有效和合理的——实际上,是必需的——他们这样做。

标签: c++ optimization concurrency restrict restrict-qualifier


【解决方案1】:
  1. __restrict__在C++中没有标准化,所以这个问题只能在特定平台上回答。对于 GCC,您可以将 __restrict__ 应用到 this in the same way 为 const:

    void T::fn () __restrict__
    
  2. 您的示例中没有潜在的别名。 C++ 为数据竞争指定了未定义的行为。

  3. 正在开发用于 C++ 受限指针的new system。它可能会在 C++17 中标准化。支持this 是既定的设计目标之一。

【讨论】:

  • 别名不是我担心的,它是并发访问。假设我的循环是 while (! m_YouRangSir) { mySleep(Miliseconds(1000)); } ...
  • 另外,该提案似乎非常冗长且难以使用,我希望他们做一些简单的事情,例如__restrict__,并使用额外的措辞来避免歧义。
  • @einpoklum 在这种情况下,别名包括并发访问。如果循环中有关键部分,则必须重新读取该成员。如果你想要restrict 关键字,你可以使用#define restrict [[alias_set()]]。属性可以放置在声明的开头或声明符 id 之前。我不确定该提案如何适用于this,现在我查看了它;这些示例仅显示 allowing 使用 this 的别名。
【解决方案2】:

对于您发布的代码,使用任何优化编译的代码都不应该重新读取该类成员。但是,请使用类似的代码:

void Foo::Bar(int count, int* result)
{
    *result = 0;
    for (int i = 0; i < count; i++) {
        if (m_someDataMemberOfFoo) (*result)++;
    }
}

在这种情况下,编译器必须假设 result == &m_someDataMemberOfFoo 是可能的,如果该成员具有 int 类型。显然,任何以这种方式称呼它的开发人员都应该吊销他的编程许可证,但这是合法的,编译器必须正确处理。

如果您将方法标记为“const”,情况也是如此。 const 方法不允许使用 this 指针修改 *this 的任何部分(有一些例外)。但是,如果实际对象不是 const,*this 的成员可以以其他方式合法修改。

C 中的“restrict”旨在解决该问题,希望 C++ 中的相同功能可以解决这种情况。

【讨论】:

  • 编译器不能 - 至少在理论上 - 仍然优化您提供的示例代码,因为即使它假设 result==&m_someDataMemberOfFoo,该值设置为 0 并且永远不会达到增量。如果达到增量,则结果不能别名 m_someDataMemberOfFoo。 (这绝不会使您提出的观点无效,只是询问)
  • 在我链接到的演讲中,MSVC 确实在循环条件中使用 m_someDataMemberOfFoo 编译代码以重复重新读取成员...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-14
  • 2023-03-18
  • 1970-01-01
  • 2012-06-14
  • 1970-01-01
  • 2020-02-22
  • 2021-09-03
相关资源
最近更新 更多