【问题标题】:How to avoid aliasing and improve performance?如何避免混叠并提高性能?
【发布时间】:2015-07-24 02:17:06
【问题描述】:

在这个Stack Overflow answer 证明了 C++ 中的别名可以减慢您的代码。 C++ 中的别名不仅适用于指针,也适用于引用,更普遍地适用于these types specified by the standard。特别是有

在其成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含联合的成员)

所以根据我的理解,如果我有如下代码,

 class A{
  public:
   int val;
 };

 void foo(vector<A> & array, int & size, A & a0) {
   for(int i=0;i<size;++i) {
    array[i].val = 2*a0.val;
   }
 }

并且a0 可以为array 中的元素之一加上别名,也可能因为前面提到的引用而别名size,因此必须为每次迭代加载a0size在性能下降。

  1. 那么我的问题是我应该对代码做些什么来避免别名,并提高性能?
  2. 传递const &amp; 将无济于事,因为它无法避免标准指定的别名。按值传递a0?但这会复制我不喜欢的a0,因为在实践中,A 类可能非常复杂,并且复制是一个非常昂贵的选择。
  3. 是否有避免在 C++ 中出现别名的通用解决方案?如果是,是什么?

【问题讨论】:

  • 我在此处引用的提案来自 Clark Nelson is relevant,尽管这是一个 C 提案,但他提到它也与 WG21(C++) 相关,尽管他没有时间提供 WG21 措辞。
  • 我对别名行为不太熟悉,但对我正在阅读的内容不太熟悉,只要可以向编译器证明函数需要读取的值不是彼此的潜在别名根据规则,它应该避免每次刷新值的开销。例如,在这个例子中(我可能是错的)不应该有别名,因为intvector&lt;A&gt;A 不是可以相互引用的类型,除了指向int&amp;int&amp; 指向A::val
  • @XerenNarcy a0 可能是对array 的元素之一的引用
  • @MattMcNabb 公平点,完全掩盖了这一点......

标签: c++ c++11


【解决方案1】:

Evolution Working Group issue 72: N4150 Alias-Set Attributes: Toward restrict-like aliasing semantics for C++, N3988 Towards restrict-like aliasing semantics for C++ N3635 Towards restrict-like semantics for C++ 似乎涵盖了在 C++ 中避免别名性能问题的问题,N4150 是该提案的最新版本。 EWG 问题尚未解决,但显然已准备好进行审查。

提案提出了类似 C 的限制限定符,目前许多编译器都支持 C++ 中的扩展,但存在模糊区域,提案中写道:

毫无疑问,restrict 限定符有利于编译器 在许多方面进行优化,特别是允许改进的代码运动和 消除负载和存储。自C99推出以来 限制,它已在许多编译器中作为 C++ 扩展提供。 但是这个特性在 C++ 中很脆弱,没有明确的 C++ 语法规则 和语义。现在随着 C++11 的引入,仿函数正在 替换为 lambdas 并且用户已经开始询问如何使用限制 在 lambdas 存在的情况下。鉴于现有的编译器支持,我们 需要提供具有明确定义的 C++ 语义的解决方案,之前 C99 限制的一些常见子集的使用被广泛用于 结合 C++11 结构。

提案还指出:

没有对现有的 C99 进行标准化和改进 在 C++ 中限制设施,用户通常必须跳过相当大的障碍才能通过 通过临时代码重写代码,或分解和内联函数体以模拟其效果。

因此,目前似乎没有好的解决方案,尽管当前的编译器具有提供 C 限制类似语义的扩展,但有很多灰色区域部分 3。 C++ 和 C 中的限制问题涵盖了一些用于避免别名的方法,但它们都有缺陷。

提案提到了N3538,其中提到了一些技术以及与这些技术相关的缺陷。例如:

克服混叠的最简单技术是复制 潜在的别名参数。

void rf2(type& output, const type& input) {
    type temp = input;
    output += ra1(temp);
    output += ra2(temp);
}

这种技术比简单的技术更复杂,效率更低 按值传递参数。虽然该技术在以下情况下很有用 处理遗留接口,它不应该是主要技术。

这种技术适用于您的情况。

注意关于别名和 C99 限制限定符 On the redundancy of C99's restrict 的有趣观点。

【讨论】:

    【解决方案2】:

    如果您不希望 sizea0.val 在执行 foo 期间发生更改,那么您可以通过将它们设为本地值来明确说明:

    void foo(vector<A> & array, int size, const A & a0) {
        auto new_val = 2*a0.val;
        for(int i=0;i<size;++i) {
            array[i].val = new_val;
        }
    }
    

    现在很明显,您打算将所有元素设置为相同的值。读者和编译器都很清楚。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-13
      • 1970-01-01
      • 1970-01-01
      • 2019-04-28
      相关资源
      最近更新 更多