【问题标题】:How can i unroll the loop using metaprogramming C++?如何使用元编程 C++ 展开循环?
【发布时间】:2011-12-09 17:56:42
【问题描述】:

事实上,我想评估 2 个数组的点积。当我尝试这个时

template <int N, typename ValueType>
struct ScalarProduct {
    static ValueType product (ValueType* first, ValueType* second) {
        return ScalarProduct<N-1, ValueType>::product(first + 1, second + 1) 
            + *first * *second;
    }
};

template <typename ValueType>
struct ScalarProduct<0, ValueType> {
    static ValueType product (ValueType* first, ValueType* second) {
        return 0;
    }

那么在运行时计算的时间少于编译期间的时间

【问题讨论】:

  • 这个问题有点不清楚。您询问有关使用元编程展开循环的问题,但我没有看到您尝试展开的循环。请澄清一下?
  • @JohnDibling:他显然是指递归调用,这是运行时循环的一种替代方法。
  • 你的方法有什么问题吗?你的代码对我来说很好。
  • 我想知道这种方式比运行时循环慢是否正常
  • @reinearthed :是的,这很正常,因为您的函数不是尾递归的。如果将其设为尾递归,它可能与运行时 for 循环相同。

标签: c++ metaprogramming


【解决方案1】:

首先,您正在编写函数。所以无论是否元编程,编译器都会生成函数。而且由于直到运行时才会评估函数,因此您的方法不会减少运行时间。事实上,当您将 for 循环展开到递归函数调用中时,它可能会增加一些开销。

要回答一个更通用的问题,使用模板元编程您只能在编译时计算内容。一种标准方法是预先计算您想要的值并将它们作为成员存储在对象中。而且你只能使用像 enum 这样的类型(不需要构造函数的类型)在编译时计算东西,因为所有构造函数调用都是在运行时执行的。

元编程在大多数情况下是不切实际的。您可以将其用作了解模板的好工具,但它会导致大型二进制文件和不可维护的代码库。所以我建议你不要使用它,除非你已经探索过其他选项,比如查找表。

只有在您的代码中已经定义了任意数组时,您才能使用它们。例如

int a1[] = {1,2,3};
int a2[] = {2,4,5};

template <int N,typename T>
struct foo {
  int product;
  foo<N-1,T> rest;
  foo(const T* array1,const T* array2) : rest(array1+1,array2+1) { product = array1[0] * array2[0] + rest.product; }
};

template <0,typename T>
struct foo {
  int product;
  // These addresses are stale, so don't use them
  foo(cons T* array1, const T* array2) : product(0) {}
};

foo<3,int> myfoo(a1,a2);

您可以让 myfoo.product 获得在编译时计算的 a1 和 a2 的叉积的值。

【讨论】:

  • 您说要预先计算值并将它们作为成员存储在对象中。但是我怎样才能使用任意数组呢?
  • 有趣的是,如果数组的初始值设定项是编译时常量 (int a1[] = {1,2,3}; int a2[] = {2,4,5};),gcc (-O3) 可以直接使用 asker 的代码计算结果。如果 ScalarProduct 将使用 for 循环实现,它将展开循环,但计算将在运行时完成。
【解决方案2】:

编译器可能会为您完成,但可能不会。您必须查看编译器生成的代码才能确定它是否这样做。如果由于某种原因没有,您唯一的选择是不使用递归模板并提出替代解决方案,即查找表、运行时循环等。

【讨论】:

    【解决方案3】:

    如果您想了解为什么两段代码执行不同,您需要一个分析器。

    如果我不得不猜测的话,我会说您聪明的递归模板扩展正在生成的代码对于您的编译器来说太难进行有效优化了。浮点数组上的循环可能是 C++ 中最积极优化的结构。您的编译器甚至可能对标量产品有明确的特殊情况。当然,如果它愿意,它可以展开自己的循环。

    在这些简单的事情中,最好不要试图诱使编译器执行您想要的优化。它比您更了解标量产品,并且更有能力有效地解决您的问题。真的!

    或者也许我完全错了,完全应该归咎于其他事情。也许您的基准有问题,也许您的样本量太小。也许任何数量的东西。您不应该试图猜测为什么会发生这种奇怪的结果;您应该应用分析器并进行调查。

    祝你好运。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-03
      • 1970-01-01
      • 2017-01-29
      • 1970-01-01
      • 2021-10-04
      • 2016-08-03
      • 2011-12-03
      • 1970-01-01
      相关资源
      最近更新 更多