简体   繁体   English

确定在何处放置函数实现

[英]Deciding where to place a function implementation

Let me first state that I know that inline does not mean that the compiler will always inline a function... 首先让我声明我知道inline并不意味着编译器将始终内联一个函数...

In C++ there really are two places for a non- template non- constexpr function implementation to go: 在C ++中,非templateconstexpr函数实现确实有两个地方:

  1. A header, definition should be inline 标头,定义应​​为内联
  2. A source file 源文件

There are benefits/negatives to placing the implementation in one or the other: 将实现放在一个或另一个中有优点/缺点:

  1. inline function definition inline函数定义
    • compiler can inline the function 编译器可以内联函数
    • slower compiler times both due to having to parse definitions and include implementation dependencies. 由于必须解析定义并包括实现依赖项,因此编译器时间较慢。
    • multiple copies of a function between multiple users on the same site 同一站点上多个用户之间的功能的多个副本
  2. source file definition 源文件定义
    • compiler can never inline the function (maybe that's not true with LTO?) 编译器永远无法内联函数(也许对LTO而言不是这样吗?)
    • can avoid recompilation if the file hasn't changed 如果文件未更改,可以避免重新编译
    • one copy per site 每个站点一份

I am in the midst of writing a reusable math library where inlining can offer significant speedups. 我正在编写一个可重用的数学库,其中内联可以显着提高速度。 I only have test code and snippets to work with right now, so profiling isn't an option for helping me decide. 我现在只能使用测试代码和代码片段,因此无法进行性能分析。 Are there any rules - or just rules of thumb - on deciding where to define the function? 是否有任何规则(或只是经验法则)来决定在哪里定义函数? Are there certain types of functions, like those with exceptions, which are known to always generate large amounts of code that should be relegated to a source file? 是否存在某些类型的功能(例如带有异常的功能),这些功能通常会生成大量降级到源文件的代码?

If you have no data, keep it simple. 如果没有数据,请保持简单。

Libraries that suck to develop don't get finished, and those that suck to use don't get used. 那些难以发展的图书馆没有完成,而那些难以使用的图书馆则没有被使用。 So split h/cpp by default; 因此,默认情况下拆分h / cpp; that makes build times slower and development faster. 这样可以缩短构建时间并加快开发速度。


Then get data. 然后获取数据。 Write tests and see if you get significant speedups from inlining. 编写测试,看看是否可以从内联中获得显着的加速。 Then go and learn how to profile and realize your speedups where spurious, and write better tests. 然后去学习如何分析并实现杂散的加速,并编写更好的测试。

How to profile and determine what is spurious and what is microbenchmark noise is between a chapter of a book and a book in length. 在书的一章和一本书的长度之间,如何分析和确定什么是伪造的以及什么是微基准噪声。 Read SO questions about performance in C++ and you'll at least learn the 10 most common ways to microbenchmark are not accurate. 阅读有关C ++性能的SO问题,您至少将了解10种最常见的微基准测试方法不准确。


For general rules, smallish bits of code in tight loops benefit from inlining, as do cases where external vectorization is plausible, and where false aliasing could block compiler optimizations. 对于一般规则,紧密循环中的少量代码可以从内联中受益,在这种情况下,外部向量化是可行的,而假混叠可能会阻止编译器优化。

Often you can hoist the benefits of inlining into your library by offering vector operations. 通常,您可以通过提供矢量操作来提升内联到库中的好处。

Generally speaking, if you are statically linking (as opposed to DLL/DSO methods), then the compiler/linker will basically ignore inline and do what's sensible. 一般来说,如果您是静态链接(而不是DLL / DSO方法),则编译器/链接器将基本上忽略内联并执行明智的操作。

The old rule of thumb (which everyone seems to ignore) is that inline should only be used for small functions. 经验法则(每个人似乎都忽略了)是内联仅应用于小型函数。 The one problem with inlining is that all do often I see people doing some timed test, eg 内联的一个问题是,我经常看到所有人都在做一些定时测试,例如

auto startTime = getTime();
for(int i = 0; i < BIG_NUM; ++i)
{
  doThing();
}
auto endTime = getTime();

The immediate conclusion from that test is that inline is good for performance everywhere. 该测试的直接结论是,内联对任何地方的性能都有好处。 But that isn't the case. 但是事实并非如此。

inlining also increases the size of your compiled exe. 内联还会增加已编译exe的大小。 This has a nasty side effect in that it increases the burden placed on the instruction and uop caches, which can cause a performance loss. 这有一个令人讨厌的副作用,因为它增加了指令和uop缓存的负担,这可能会导致性能下降。 So in the case of a large scale app, more often than not you'll find that removing inline from commonly used functions can actually be a performance win. 因此,对于大型应用程序,您常常会发现从常用功能中删除内联实际上可以赢得性能。

One of the nastiest problems with inline is that if it's applied to the wrong method, it's very hard to get a profiler to point out a hot spot - It's just a little warmer than needed in multiple points in the codebase. 内联的最讨厌的问题之一是,如果将它应用于错误的方法,将很难使探查器指出热点-它仅比代码库中多个点所需的温度高一点。

My rule of thumb - if the code for a method can fit on one line, inline it. 我的经验法则-如果一种方法的代码可以放在一行上,请对其内联。 If the code doesn't fit on one line, put it in the cpp file until a profiler indicates moving it to the header would be beneficial. 如果代码不适合一行,则将其放在cpp文件中,直到探查器指示将其移至标头将是有益的。

The rule of thumb I work by is simple: No function definitions in headers, and all function definitions in a source file, unless I have a specific reason to do otherwise. 我的经验法则很简单:标头中没有函数定义,而源文件中没有所有函数定义,除非我有其他特殊原因。

Generally speaking, C++ code (like code in many languages) is easier to maintain if there is a clear separation of interface from implementation. 一般来说,如果接口与实现之间存在明显的分离,则C ++代码(如许多语言的代码)更易于维护。 Maintenance effort is (quite often) a cost driver in non-trivial programs, because it translates into developer time and salary costs. 维护工作(通常)是非平凡程序中的成本驱动因素,因为它会转化为开发人员的时间和薪水成本。 In C++, interface is represented by declarations of functions (without definition), type declarations, struct and class definition, etc ie the things that are typically placed in a header, if the intent is to use them in more than one source file. 在C ++中,接口由功能声明(无定义),类型声明, structclass定义等表示,即,如果要在多个源文件中使用它们,则通常将它们放在标头中。 Changing the interface (eg changing a function's argument types or return type, adding a member to a class , etc) means that everything which depends on that interface must be recompiled. 更改接口(例如,更改函数的参数类型或返回类型,向class添加成员等)意味着必须重新编译依赖该接口的所有内容。 In the long run, it often works out that header files need to change less often than source files - as long as interface is kept separate from implementation. 从长远来看,通常可以弄清楚头文件的更改频率要比源文件少,只要接口与实现保持独立即可。 Whenever a header changes, all source files which use that header (ie that #include it) must be recompiled. 每当头文件更改时,必须重新编译使用该头文件的所有源文件(即, #include它)。 If a header doesn't change, but a function definition changes, then only the source file which contains the changed function definition, needs to be recompiled. 如果头文件没有变化,但是函数定义发生了变化,则只有包含更改后的函数定义的源文件才需要重新编译。

In large projects (say, with hundreds of source and header files) this sort of thing can make the difference between incremental builds taking a few seconds or a few minutes to recompile a few changed source files versus significantly longer to recompile a large number of source files because a header they all depend on has changed. 在大型项目中(例如,具有数百个源文件和头文件),这种事情可以使增量构建之间的区别有所不同,即重新编译一些已更改的源文件需要花费几秒钟或几分钟,而重新编译大量的源则花费更长的时间。文件,因为它们都依赖的标头已更改。

Then the focus can be on getting the code working correctly - in the sense of producing the same observable output given a set of inputs, meeting its functional requirements, and passing suitable test cases. 然后,重点可以放在使代码正确工作上,即在给定一组输入的情况下产生相同的可观察到的输出,满足其功能要求并通过适当的测试用例。

Once the code is working correctly enough, attention can turn to other program characteristics, such as performance. 一旦代码正常运行,注意力就会转向其他程序特征,例如性能。 If profiling shows that a function is called many times and represents a performance hot-spot, then you can look at options for improving performance. 如果性能分析表明某个函数被多次调用并代表一个性能热点,那么您可以查看提高性能的选项。 One option that MIGHT be relevant for improving performance of a program that is otherwise correct is to selectively inline functions. MIGHT与提高程序性能相关的一种选择,否则是正确的,是有选择地内联函数。 But, every time this is done, it amounts to deciding to accept a greater maintenance burden in order to get performance. 但是,每次这样做,就等于决定接受更大的维护负担以获得性能。 But it is necessary to have evidence of the need (eg by profiling). 但是有必要证明需要(例如通过剖析)。

Like most rules of thumb, there are exceptions. 像大多数经验法则一样,也有例外。 For example, templated functions or classes in C++ do generally need to be inlined since, more often than not, the compiler needs to see their definition in order to instantiate the template. 例如,通常确实需要内联C ++中的模板化函数或类,因为(通常)编译器需要查看其定义才能实例化模板。 However, that is not an justification to inlining everything (and it is not a justification for turning every class or function into a template). 但是,这并不是内联所有内容的理由(也不是将每个类或函数转换为模板的理由)。

Without profiling or other evidence, I would rarely bother to inline functions. 没有概要分析或其他证据,我很少会费心内联函数。 Inlining is a hint to the compiler, which the compiler may well ignore, so the effort of inlining may not even be worth it. 内联是对编译器的提示,编译器可能会完全忽略它,因此内联的工作甚至不值得。 Doing such a thing without evidence may achieve nothing - in which case it is simply premature optimisation. 在没有证据的情况下做这样的事情可能无济于事-在这种情况下,这仅仅是过早的优化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在哪里放置2个文件的辅助函数? - Where to place helper function for 2 files? 将gcc函数属性放在自动函数中的位置 - Where to place gcc function attribute in auto function 在代码(头文件和实现文件)中放置#includes和Redundancies的位置 - Where to place #includes and Redundancies in Code (Header and Implementation Files) 在多文件编译中将 function 的变量放在哪里 - Where to place variables of a function in multifile compilation 类和类成员将使用的通用函数放在哪里? - Where to place common function which will be used by class and class member? 我的功能放在哪里? 到我的本地Gui还是我的演示者中? - Where to place my function? Into my native Gui or into my presenter? 在哪里可以找到GCC源代码中strncpy()函数的实现? - Where can I find the implementation of strncpy() function in GCC source code? 有没有办法派生基本函数的实现来访问base是朋友的类? - Is there a way for a derived implementation of a base function to access a class where base is a friend? 确定可变C ++ 11模板化lambda函数的返回类型 - Deciding return type for variadic C++11 templated lambda function 编译器决定不内联函数会导致多个定义吗? - can compiler deciding not to inline a function lead to multiple definitions?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM