【问题标题】:Prevent two object internals from aliasing防止两个对象内部出现混叠
【发布时间】:2011-06-09 02:18:27
【问题描述】:

我有一个类似的函数签名

void Mutliply(const MatrixMN& a, const MatrixMN& b, MatrixMN& out);

矩阵类内部有一个float* data;,代表m x n 组件。我想告诉编译器 ab 不给输出矩阵加上别名,所以它不会做大量的加载存储。

我该怎么做呢?我知道我可以传入指向函数签名的指针并用__restrict(在MSVC中)标记指针,但我想保留通过引用传递的对象的习语,其中对象包含指向内存的指针。

我也知道__restrict 不适用于对象引用。

【问题讨论】:

  • 我不认为将__declspec(noalias) 应用于函数具有您想要的语义?
  • @ildjarn:可能是 - 我必须检查组件以确保

标签: c++ pointer-aliasing restrict-qualifier


【解决方案1】:

根据优化器的工作方式,顶部的assert(&in1 != &out && &in2 != &out) 可能会起到作用。你也可以去掉 out 参数,相信优化器会去掉多余的副本(当然,假设它是一个纯 out 参数)。如果代码是内联的候选代码,编译器可能会看到它自己没有任何别名。如果restrict 确实不适用于引用参数,则可以对函数调用进行额外的调用,并将所有三个传递给第二个函数,该函数接受适当限制的指针。希望那个会为你内联。

【讨论】:

  • 我不太担心它会生成不正确的代码,因为编译器会这样做。我担心编译器无法生成高效的代码。我们需要给编译器提示以允许它这样做。
  • @coderdave:我不确定你的意思,但这些都是帮助它生成高效代码的提示。显然它不会生成错误的代码。例如,第一个告诉编译器它可以安全地将这三个视为唯一的,否则程序将提前中止。如果是内联的,可以根据周围的代码做同样的假设。
  • 问题是我不关心传入的对象,我关心的是被别名的对象内部指向的数据。每个对象都有一个指向浮点数组的指针,编译器不能假设它们别名,因为指针是动态的,不知道它们不指向彼此。我们必须告诉编译器,对象内部的 float* 永远不会别名。
【解决方案2】:

编写一个非导出的(文件-staticprivate)乘法函数,它接受float* 参数,用restrict 标记参数。让Multiply调用这个函数。

【讨论】:

  • 这需要 Multiply 成为朋友,如果需要,这很好,但(特别是如果 MatrixMN 不是他的班级)可能不是。
  • @Fred 如果MatrixMN 不是他的班级,他可能会使用写时复制。
  • 除非有文件证明它没有。例如,std::string 在大多数实现中都没有,所以你可以依赖它(使用那些实现),但你不能让你的函数成为它的朋友。
【解决方案3】:

由于您似乎对 __restrict 指针感到满意,所以我会使用您所知道的,但您仍然可以包装它并使用引用提供接口:

void Multiply(const MatrixMN& a, const MatrixMN& b, MatrixMN& out) {
  if (&a == &b || &a == &out || &b == &out) {
    // indicate precondition violation however you like
    assert(!"precondition violated");
    abort();  // assert isn't always executed
  }
  else {
    DoMultiply(&a, &b, &out);
  }
}

void DoMultiply(MatrixMN const * __restrict a, MatrixMN const * __restrict b,
              MatrixMN * __restrict out)
{
  //...
}

将指针版本设为“非公开”,例如将其放置在“详细信息”命名空间中,为其提供内部链接(在这种情况下不适用),或为其指定一个特殊名称。你甚至可以使用局部变量而不是参数并将函数体放在“else”中,但我发现上面的更干净。

【讨论】:

    【解决方案4】:

    如何让宏包装器在编译时本身具有__restrict 效果:(以下是伪代码,未检查):

    #define Multiply(A,B,C) Multiply_restrict(&A, &B, &C)
    

    现在中间方法定义为,

    inline void Multiply_restrict(const MatrixMN* __restrict pA,
                const MatrixMN* __restrict pB, MatrixMN* __restrict pC)
    {
      Multiply_(*pA, *pB, *pC);
    }
    

    最后只需在原来的Multiply 之后添加一个_

    void Mutliply_(const MatrixMN& a, const MatrixMN& b, MatrixMN& out);
    

    所以最终效果将与您调用的完全相同:

    Multiply(x, y, answer);
    

    【讨论】:

    • 为什么人们仍然在没有保证的地方使用宏?
    • 宏需要用于类似于注入 __FILE__ 或基本代码生成的事情。我经常使用宏,但仅仅为了避免内联函数而使用宏是不可原谅的。
    • 希望没有人写过 Multiply(string,int) 函数——或者任何使用“Multiply”的函数——只要你有 Multiply 宏。
    • @Fred,该宏将语法从指针传递更改为看起来像值传递。这不仅仅是为了避免内联函数。我不会对此投反对票。当然,如果有人确实使用 Multiply 之类的语法,最好为宏提供自己的命名约定,以避免发生您建议的命名冲突。无论如何,这个答案仍然无助于类中的指针别名问题。
    • @coderdave:这里的宏是没有根据的,因为它可以完全用内联函数替换,避免令人讨厌的宏并发症。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    • 2018-12-03
    • 2017-07-08
    • 1970-01-01
    • 2016-12-27
    • 1970-01-01
    • 2011-03-16
    相关资源
    最近更新 更多