clang:强制特定循环展开

10

有没有办法告诉clang展开特定的循环?


搜索答案会给出影响整个编译器而不是单个循环的命令行选项。


对于GCC有一个类似的问题---告诉gcc特别展开一个循环---

那里提供的第一种方法:

#pragma GCC optimize ("unroll-loops")

看起来被悄悄忽略了。事实上

#pragma GCC akjhdfkjahsdkjfhskdfhd

也会被默默地忽略。

选项2:

__attribute__((optimize("unroll-loops")))

结果会提示一个警告:

warning: unknown attribute 'optimize' ignored [-Wattributes]

更新

joshuanapoli提供了一个不需要创建循环即可通过模板元编程和C++11进行迭代的好方法。该结构将在编译时解析,从而产生一个重复内联的主体。虽然它并不完全是问题的答案,但它基本上实现了相同的功能。

这就是为什么我接受这个答案。不过,如果您知道如何使用标准的C循环(for, while)并强制展开它,请与我们分享!


1
通常,编译器非常清楚何时适合展开循环以及不适合展开循环。您试图解决的特殊情况是什么,使得这个规则不适用? - Mats Petersson
它可能不会强制展开,但是尝试使用__attribute__ ((hot))可能值得一试。 - Brett Hale
1
@MatsPetersson 我想明确地衡量循环展开的好处。手写展开实际上可以将代码加速3倍,但编译器无法自动优化。 - CygnusX1
4个回答

9

对于一个C++程序,你可以在语言内部展开循环。你不需要找出编译器特定的选项。例如,

#include <cstddef>
#include <iostream>

template<std::size_t N, typename FunctionType, std::size_t I>
class repeat_t
{
public:
  repeat_t(FunctionType function) : function_(function) {}
  FunctionType operator()()
  {
    function_(I);
    return repeat_t<N,FunctionType,I+1>(function_)();
  }
private:
  FunctionType function_;
};

template<std::size_t N, typename FunctionType>
class repeat_t<N,FunctionType,N>
{
public:
  repeat_t(FunctionType function) : function_(function) {}
  FunctionType operator()() { return function_; }
private:
  FunctionType function_;
};

template<std::size_t N, typename FunctionType>
repeat_t<N,FunctionType,0> repeat(FunctionType function)
{
  return repeat_t<N,FunctionType,0>(function);
}

void loop_function(std::size_t index)
{
  std::cout << index << std::endl;
}

int main(int argc, char** argv)
{
  repeat<10>(loop_function)();
  return 0;
}

使用复杂的循环函数的示例

template<typename T, T V1>
struct sum_t
{
  sum_t(T v2) : v2_(v2) {}
  void operator()(std::size_t) { v2_ += V1; }
  T result() const { return v2_; }
private:
  T v2_;
};

int main(int argc, char* argv[])
{
  typedef sum_t<int,2> add_two;
  std::cout << repeat<4>(add_two(3))().result() << std::endl;
  return 0;
}
// output is 11 (3+2+2+2+2)

使用闭包代替显式函数对象
int main(int argc, char* argv[])
{
  int accumulator{3};
  repeat<4>( [&](std::size_t)
  {
    accumulator += 2;
  })();
  std::cout << accumulator << std::endl;
}

是的,这是我默认的做法。但由于我已经在带有需要进入“loop_function”的参数的模板内部,所以变得非常丑陋...这就是为什么我正在寻找一些更“美观”的解决方案的原因 :) - CygnusX1
如果您可以使用C++11,则可以使用constexpr函数来减少模板语法噪音。 - joshuanapoli
不过,如果只有一些参数是constexpr/template类型,而另一些参数是常规动态参数,那么这个条件还成立吗? - CygnusX1
你应该能够在概念上和语法上将循环的“算法”与被循环的函数分开。我添加了一个具有模板和变量参数的复杂循环函数对象的示例。 - joshuanapoli
这是编译时递归,因此没有函数调用的运行时嵌套。如果除了展开循环之外,您还想影响循环体的内联,则可能需要使用特定于编译器的功能。 - joshuanapoli
显示剩余2条评论

3

3

在C++17及更高版本中,您可以使用if constexpr编写一个更加简单明了(对我来说)的joshuanapoli模板方法的版本:

template<std::size_t N, class F, std::size_t START = 0>
inline void repeat(const F &f) {
  if constexpr (N == 0) {
    return;
  } else {
    f(START);
    repeat<N - 1, F, START + 1>(f);
  }
}

这个版本在调用时不需要额外的 ():

  int accumulator = 3;
  repeat<4>([&](std::size_t x) {
    accumulator += x;
  });

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接