如何使用c++元编程展开循环?

How can i unroll the loop using metaprogramming C++?

本文关键字:循环 编程 何使用 c++      更新时间:2023-10-16

实际上,我想计算两个数组的点积。当我尝试这个

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;
    }

则运行时计算时间小于编译时

首先,编写函数。不管是不是元编程,编译器都会生成函数。因为函数直到运行时才会被求值你的方法不会减少运行时间。实际上,当您将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。

获取编译时计算的a1和a2的外积的值。

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

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

如果让我猜的话,我会说你聪明的递归模板展开产生的代码对于你的编译器来说太难优化了。浮点数组上的循环可能是c++中最积极优化的结构;您的编译器甚至可能对标量积显式地有特殊情况。当然,如果它愿意的话,它可以展开自己的循环。

在这些简单的问题中,最好不要试图欺骗编译器来执行您想要的优化。它比你更能理解标量积,更能有效地解决你的问题。真的!

或者我完全错了,而其他的事情完全是罪魁祸首。也许你的基准有问题,也许你的样本量太小。也许有很多事情。你不应该试图猜测为什么会发生这种奇怪的结果;您应该应用一个分析器,并进行调查。

好运。