我对内联函数感到困惑。
人们说内联函数通过用原始代码替换函数来节省CPU时间,但与普通函数相比,它增加了代码的大小。
所以真正的问题是如果我继续在循环中调用内联函数10次,代码大小是否会增加。
假设内联函数大小为2个字节,它会增加20个字节吗?
有人可以向我解释一下吗?
答案 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 代码比执行函数调用时的代码少。
此外,优化器可能能够重新排序/消除现在更接近的指令,从而提供更好的性能和甚至更少的代码。
了解这种情况的唯一方法是反复试验。你用一种方式编写它并测量性能和代码大小,然后用另一种方式编写它并再次测试。