内联函数

时间:2011-06-24 12:11:25

标签: c++ c

  

可能重复:
  Benefits of inline functions in C++?

我对内联函数感到困惑。

人们说内联函数通过用原始代码替换函数来节省CPU时间,但与普通函数相比,它增加了代码的大小。

所以真正的问题是如果我继续在循环中调用内联函数10次,代码大小是否会增加。

假设内联函数大小为2个字节,它会增加20个字节吗?

有人可以向我解释一下吗?

5 个答案:

答案 0 :(得分:9)

相同的代码将被执行10次。但仍然在一个循环中,因此代码不会连续复制10次。因此,大小不会随着执行次数而增长。

答案 1 :(得分:3)

我不知道你为什么认为循环迭代次数很重要。让我们来看看。假设你写这个:

inline int foo() { return 5 * gargle(); }

/* later... */

for (size_t i = 0; i < 100; ++i)
{
  const int x = i * foo();
  baz(x + lookup[i]);
}

如果foo被内联,那么基本上编译器就像你写的一样对待你的代码:

for (size_t i = 0; i < 100; ++i)
{
  baz(i * (5 * gargle()) + lookup[i]);
}

因此,代码只会在调用网站上被替换一次。

(完全是循环展开正在发生的事情。)

答案 2 :(得分:1)

这完全取决于您,您的代码和编译器。想象一下你有:

#include <vector>

int frob (int a, int b) {
    return a + b;
}

int main () {
    std::vector<int> results(20), lhs(20), rhs(20);
    for (int i=0; i<20; ++i) {
        results[i] = frob(lhs[i], rhs[i]);
    }
}

现在,如果您的编译器优化了大小,它可能会保留原样。但如果它 优化性能,可能(或可能不是)某些编译器使用粗略的启发式度量 确定)将其转换为:

int main () {
    std::vector<int> results(20), lhs(20), rhs(20);
    for (int i=0; i<20; ++i) {
        results[i] = lhs[i] + rhs[i];
    }
}

如果它进一步优化,它可能会展开循环

int main () {
    std::vector<int> results(20), lhs(20), rhs(20);
    for (int i=0; i<20; i+=4) {
        results[i] = lhs[i] + rhs[i];
        results[i+1] = lhs[i+1] + rhs[i+1];
        results[i+2] = lhs[i+2] + rhs[i+2];
        results[i+3] = lhs[i+3] + rhs[i+3];
    }
}

尺寸增加。但是如果编译器现在决定也要进行一些自动矢量化, 它可能会再次变成与此不相似的东西:

int main () {
    std::vector<int> results(20), lhs(20), rhs(20);
    for (int i=0; i<20; i+=4) {
        vec4_add (&results[i], &lhs[i], &rhs[i]);            
    }
}

尺寸减小。

接下来,编译器,一如既往地智能,再次展开并完全杀死循环:

int main () {
    std::vector<int> results(20), lhs(20), rhs(20);

    vec4_add (&results[i], &lhs[i], &rhs[i]);            
    vec4_add (&results[i+4], &lhs[i+4], &rhs[i+4]);
    vec4_add (&results[i+8], &lhs[i+8], &rhs[i+8]);
    vec4_add (&results[i+12], &lhs[i+12], &rhs[i+12]);
    vec4_add (&results[i+16], &lhs[i+16], &rhs[i+16]);
}

如果可以得出足够的结论是替换向量,那么优化g ++将会运用 使用普通数组

int main () {
    int results[20] = {0}, lhs[20] = {0}, rhs[20] = {0};

    vec4_add (&results[i], &lhs[i], &rhs[i]);            
    vec4_add (&results[i+4], &lhs[i+4], &rhs[i+4]);
    vec4_add (&results[i+8], &lhs[i+8], &rhs[i+8]);
    vec4_add (&results[i+12], &lhs[i+12], &rhs[i+12]);
    vec4_add (&results[i+16], &lhs[i+16], &rhs[i+16]);
}

它可以看到一切都是恒定的,并且折叠

int main () {
    int results[20] = {0}; // because every lhs[0]+rhs[0] == 0
}

结论是结果实际上未被使用,最后吐出:

int main() {
}

答案 3 :(得分:0)

当您使用内联时,您告诉编译器使用该方法中的代码替换对内联方法的任何调用。例如:

inline int min(int a, int b)
{
    return (a < b) ? a : b;
}

void some_method()
{
    int x = min(20, 30);
}

将由编译器更改为:

void some_method()
{
    int x = (20 < 30) ? 20 : 30;
}

如果这是一个循环,它仍然只是一个替换,所以它不会增加在特定情况下的代码大小。

尽管如此,应该考虑Problems With Inline Functions。通常,让编译器决定内联的内容比自己做的更有效。

答案 4 :(得分:0)

使用inline关键字赋予编译器内联函数调用的权限,这可能会占用也可能不占用。

这可能使程序更快的原因是CPU不必进行函数调用,也不必将参数压入堆栈,因此实际上编译器可能会产生很多<调用网站上的em> less 代码比执行函数调用时的代码少。

此外,优化器可能能够重新排序/消除现在更接近的指令,从而提供更好的性能甚至更少的代码。

了解这种情况的唯一方法是反复试验。你用一种方式编写它并测量性能和代码大小,然后用另一种方式编写它并再次测试。