我指的是这个讨论。我从未用 C 或 C++ 编写过任何代码。我没有任何CS背景。然而,我作为 Java 开发人员已经工作了 5 年,现在我决定学习更多关于 CS 的知识并做一些追赶。
10 回答
在执行一段给定的代码时,无论何时调用标准函数,执行时间都比将包含在该函数中的代码转储到那里稍长。每次转储函数中包含的整个代码在另一端无法维护,因为它显然会导致代码重复的混乱。
内联通过让您将函数声明为内联(至少在 C++ 中)解决了性能和可维护性问题,因此当您调用该函数时 - 而不是让您的应用程序在运行时跳转 - 内联函数中的代码在编译时被注入每次调用给定函数的时间。
这样做的缺点是——如果你内联你多次调用的大函数——你的程序的大小可能会显着增加(最佳实践建议确实只在小函数上执行)。
http://en.wikipedia.org/wiki/Inlining
在计算中,内联扩展或内联是一种编译器优化,它用被调用者的主体替换函数调用站点。这种优化可能会提高运行时的时间和空间使用率,但可能会增加最终程序的大小。
作为 Java 开发人员,您通常不必担心方法内联。Java 的即时编译器可以并且将在大多数有意义的地方自动完成。
像eclipse这样的IDE可以有一个特性,允许你在源代码级别内联方法——永远不要为了性能而这样做,只是为了代码的可读性(例如,当你意识到该方法只是调用另一个方法而没有添加任何有用的本身时)。
Norman Maurer在他的博客中解释了类似的 JVM 和 JIT 内联功能
内联是一种基本上只是将一个方法“内联”到另一个方法中的技术,因此摆脱了方法调用。JIT 会自动检测“热”方法并尝试为您内联它们。如果一个方法被执行超过 X 次,则该方法被认为是“热的”,其中 X 是一个阈值,可以在启动 java 时使用 JVM 标志进行配置(默认值为 10000)。这是必需的,因为内联所有方法会比其他任何方法造成更大的伤害,因为生成的字节码很大。除此之外,当优化在以后的状态下被证明是错误的时,JIT 可能会“恢复”以前的内联代码。请记住,JIT 代表 Just in Time,因此在执行代码时会进行优化(包括内联但也包括其他内容)。
还带有警告
但是即使 JVM 认为一个方法是“热的”,它也可能不会内联它。但为什么?最可能的原因之一是内联太大了。
您可以在Eva Andreasson的 Java World Post中找到一个用于内联 Java 代码的非常简单的代码示例。您可以在下面找到帖子的相关部分。
许多优化尝试消除机器级跳转指令(例如,用于 x86 架构的 JMP)。跳转指令改变指令指针寄存器,从而转移执行流程。相对于其他 ASSEMBLY 指令,这是一项昂贵的操作,这就是为什么它是减少或消除的常见目标。针对这一点的一个非常有用且众所周知的优化称为内联。由于跳转代价高昂,因此将许多对具有不同入口地址的小方法的频繁调用内联到调用函数中会很有帮助。清单 3 到 5 中的 Java 代码举例说明了内联的好处。
清单 3. 调用者方法
int whenToEvaluateZing(int y) {
return daysLeft(y) + daysLeft(0) + daysLeft(y+1);
}
清单 4. 被调用的方法
int daysLeft(int x){
if (x == 0)
return 0;
else
return x - 1;
}
清单 5. 内联方法
int whenToEvaluateZing(int y){
int temp = 0;
if(y == 0) temp += 0; else temp += y - 1;
if(0 == 0) temp += 0; else temp += 0 - 1;
if(y+1 == 0) temp += 0; else temp += (y + 1) - 1;
return temp;
}
在清单 3 到 5 中,调用方法对一个小方法进行了 3 次调用,为了这个示例,我们假设内联比跳转到 3 次更有益。
内联一个很少调用的方法可能没有太大区别,但是内联一个经常调用的所谓“热”方法可能意味着性能上的巨大差异。内联还经常为进一步优化让路,如清单 6 所示。
清单 6. 内联后,可以应用更多优化
int whenToEvaluateZing(int y){
if(y == 0) return y;
else if (y == -1) return y - 1;
else return y + y - 1;
}
正如其他答案中已经提到的那样,内联是有代价的。通常这被认为很小,但是在实际测量时,您可能会感到惊讶并得知它可能比您获得的更大(所以其他人说的是真的:除非您测量过,否则不要优化)。
值得注意的是,在 Linux 内核中他们开始 un-inlining 原来内联的函数,因为成本太高(更大的函数消耗更多的 cpu 内存缓存,并且由此产生的缓存未命中比仅仅调用函数更昂贵)那些打算被内联的)。有关详细信息,请参阅doc/Documentation/process/coding-style.rst中的“第 15 章:内联疾病” 。
编译器优化答案是正确的。不过,还有另一种用法——在重构中,内联是指用方法体替换方法调用,然后删除该方法。请参阅内联方法。还有类似的重构,比如Inline Class。
编辑:请注意,重构是手动或使用工具完成的;无论哪种情况,它都涉及更改源代码。
基本上,在 C/C++ 中,编译器可以内联函数,这意味着代码将被添加到调用函数的块中,而不是进行函数调用来执行该操作,因此就好像它从来没有单独函数调用。
这将更详细: http: //www.codersource.net/cpp_tutorial_inline_functions.html
内联是指编译时优化,其中将一小段代码注入到调用函数中,而不需要单独调用。
内联函数通常用于 C++ 头文件而不是 Java。C++ 头文件通常不包含已实现的代码,并且被认为是同名 cpp 文件的接口,该文件通常包含已实现的代码。在头文件中包含内联函数是合法的,通常是一个小的轻量级函数。内联函数确实是有代价的,所以它们不应该是大型内存密集型操作。对于小型例程,性能损失很小,它们更多地用于方便。
在那次讨论中,Jon Skeet 提到了 Client jvm (hotspot) v Server jvm,如果允许 JIT (just-in-time) 编译器带来基于时间的增强,则可以在运行时获得性能改进。这就是Java中的“它是如何完成的”。
最初,没有从很多地方调用的一小段代码将被编译器“内联”,这意味着所谓的单例将直接放在指令指针代码路径中,执行函数分支和返回会花费更多的处理器能力不仅仅是展开循环或函数调用并将指令“放在那里”
今天,Singleton 是多页讨论和循环展开以及内联之类的主题,它们在某种程度上已从其原始上下文中移除。您可以阅读 Dov Bulka 关于此事的非常有见地的工作,以了解 C/C++ 对此事的看法。对于 Java,研究 java.util 中丰富的库比研究内联和深度编译器问题更能满足您的需求 - 您可能会陷入对数据结构的根深蒂固的四面楚歌的内部战争中,这会掩盖对 16 位代码的调用,并且学习曲线没有尽头。
您可以在 Java 中执行 instanceof,它类似于 vf 表(请不要加热),但可以将其视为您一直在使用强类型语言编写的 - 现在将使用一种字符串可以轻松四处乱跑的语言编写它没有业务。我最近尝试用 C 代码编写用 Java 构建图像的代码。我很快发现自己正在查看用于强加密的 oxr 表——这与我正在编写的代码无关。
您将如何在 C/C++ 中编写一个字符串类,该类对 32 字节以下的字符串有一个小缓冲区并捕获指针,以便它们只对字符串进行操作?
不是想取笑你或任何东西,它只是一个非常好的起点,而不是内联和编译器科学。