3

我有这两个功能:

template<int N>
void fun()
{
    for(int i = 0; i < N; ++i)
    {
        std::cout<<i<<" ";
    }
}

void gun(int N)
{
    for(int i = 0; i < N; ++i)
    {
        std::cout<<i<<" ";
    }
}

我可以假设在第一个版本中,编译器将为每个小的 N(我的意思是 N = {1, 2, 3, 4})优化循环吗?

4

5 回答 5

3

我可以假设在第一个版本中,编译器将为每个小 N 优化循环

这是一个典型的优化,虽然“假设”是一个强词。如果必须进行优化,您最终会对任何潜在的优化感到失望。

如果编译器能够内联函数,您的第二个版本可能会经历相同的优化。

于 2013-03-29T14:03:49.290 回答
2

您永远无法保证优化会做什么,但如果有合适的优化级别,您通常可以依靠它做出比手动优化更好的选择。

如果您真的想知道生成了什么代码,您可以随时查看生成的程序集。

于 2013-03-29T14:06:43.613 回答
1

这取决于您的优化级别和标志。-O0 -g(无优化,启用调试)、-O3(积极优化速度)和-Os(优化空间)之间存在很大差异。

如今,循环展开不一定是胜利,即使在优化速度时也是如此。太多的代码会导致指令缓存未命中,这将大大超过内联简单循环的加速。像这样的循环中条件分支的成本几乎可以忽略不计,因为分支预测将正确预测除最后一次迭代之外的所有迭代。

于 2013-03-29T14:06:59.287 回答
1

如果编译器可以内联任何一个函数,它也会展开循环,如果它认为这是正确的做法。编译器何时以及如何决定展开循环有好处是一个相当复杂的问题,并且高度取决于其他因素,例如可用寄存器的数量,循环内部发生的情况(我怀疑上面给出的示例,例如, 将从减少循环中涉及的 5 条左右指令中获得很多时间,因为这cout ...可能会消耗数千倍的时间 - 编译器是否能解决这个问题是另一回事,但这并不是完全未知让编译器对函数是否小有一定的了解。

另一方面,如果代码看起来像这样:

int arr[N];  // Global array. 

template<int N>
int fun()
{
    int sum = 0;
    for(int i = 0; i < N; ++i)
    {
        sum += arr[i];
    }
}

然后我希望编译器展开循环是这样的:

    int *tmp = arr;
    sum += *tmp++;
    sum += *tmp++;
    sum += *tmp++;
    sum += *tmp++;
    sum += *tmp++;

假设 N = 5。

这适用于任何对编译器“可见”且 N 在编译时已知的函数。所以,假设gun不在不同的源文件中,那么我希望它被内联和展开完全相同fun(作为模板函数,必须在这个编译单元中可见)

于 2013-03-29T14:13:08.500 回答
0

如果你想更明确一点,你可以使用Duff 的设备,它使用 switch case fallthrough 来展开循环。不过,我不能说它在实践中的效果如何。但是,我想,如果您可以提示编译器将其展开,那会更快。

编译器也非常聪明,虽然它们并非万无一失,但它们的优化选择通常比我们自己的直觉要好。

于 2013-03-29T14:13:15.447 回答