1

考虑以下基于 C++ 11 中 for 循环的范围

for ( T k : j )
{
  ...
}

是否有g++可以clang++加快编译代码的优化标志?

我不是在谈论任何for循环,我只是在考虑这个新的 C++11 构造。

4

2 回答 2

3

优化循环很少涉及优化实际循环迭代代码(for ( T k : j )在这种情况下),而是优化循环中的内容。

现在,由于...在这种情况下,不可能说,例如,展开循环是否会有所帮助,或者声明函数内联[或简单地移动它们以便编译器可以看到它们并将它们内联],使用自动矢量化,或者可能在循环内使用完全不同的算法。

上一段中的例子更详细一点:

  1. 展开循环——本质上是在不回到循环开始的情况下进行多次循环迭代。这在循环内容非常小时非常有用。有自动展开,编译器在其中展开,或者您可以手动展开代码,只需在每个循环迭代中执行四个项目,然后在每个循环变量更新中向前步进四个项目或在期间多次更新迭代器循环本身 [但这当然意味着不使用基于范围的 for 循环]。
  2. 内联函数 - 编译器将采用(通常是小的)函数并将它们放入循环本身,而不是进行调用。这节省了处理器调用代码中另一个位置并返回所需的时间。大多数编译器仅对编译期间对编译器“可见”的函数执行此操作 - 因此源必须位于同一源文件中,或者位于已编译源文件中包含的头文件中。
  3. 自动向量化 - 使用 SSE、MMX 或 AVX 指令在一条指令中处理多个数据项(例如,一条 SSE 指令可以将四个float值添加到另一个四合一float指令)。这比一次操作单个数据项要快(大多数情况下,有时它没有任何好处,因为尝试组合不同的数据项然后在计算完成后整理出哪些内容会带来额外的复杂性)。
  4. 选择不同的算法 - 通常有几种方法可以解决特定问题。根据您要实现的目标,[任何类型的] for 循环可能首先不是正确的解决方案,或者循环内的代码可能会使用更聪明的方式来计算/重新排列/无论如何- 确实可以达到您需要的结果。

但是...很难说上述解决方案中的哪一个(如果有的话)将有助于改进您的代码。

于 2013-05-22T10:52:39.420 回答
3

关于自动矢量化的GCC文档没有提到任何关于基于范围的for循环。此外,它的代码归结为:

{
    auto && __range = range_expression ;
    for (auto __begin = begin_expr,
                __end = end_expr;
            __begin != __end; ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
}

因此,从技术上讲,任何有助于自动矢量化这种常规结构中的结构的标志for都应该自动矢量化类似的基于范围的for循环。我真的这样做编译器只将基于范围的for循环转换为常规for循环,然后让自动矢量化在这些旧循环上完成它的工作。这意味着for在任何情况下都不需要标志来告诉编译器自动矢量化基于范围的循环。


由于要求 GCC 的实现,这里是源代码中的相关注释,描述了基于范围的for循环实际做了什么(如果您想查看代码,可以查看实现文件 parser.c):

/* Converts a range-based for-statement into a normal
   for-statement, as per the definition.

      for (RANGE_DECL : RANGE_EXPR)
    BLOCK

   should be equivalent to:

      {
    auto &&__range = RANGE_EXPR;
    for (auto __begin = BEGIN_EXPR, end = END_EXPR;
          __begin != __end;
          ++__begin)
      {
          RANGE_DECL = *__begin;
          BLOCK
      }
      }

   If RANGE_EXPR is an array:
    BEGIN_EXPR = __range
    END_EXPR = __range + ARRAY_SIZE(__range)
   Else if RANGE_EXPR has a member 'begin' or 'end':
    BEGIN_EXPR = __range.begin()
    END_EXPR = __range.end()
   Else:
    BEGIN_EXPR = begin(__range)
    END_EXPR = end(__range);

   If __range has a member 'begin' but not 'end', or vice versa, we must
   still use the second alternative (it will surely fail, however).
   When calling begin()/end() in the third alternative we must use
   argument dependent lookup, but always considering 'std' as an associated
   namespace.  */

如您所见,它们所做的只是标准实际描述的内容。

于 2013-05-22T09:49:33.830 回答