link-time optimization versus. project inlining; limitations on each approach [closed]
usually people when designing proper software architectures on c++ that also need to have great performance, enter into the dangerous game of premature optimization, but rather that doing optimization at the architecture level (which is a perfectly good and encouraged form of premature optimization) they do compromises at the code level, like avoiding virtual methods and interfaces altogether, low level hacks, etc.
some people avoids this by doing a practice called usually application inlining or unity builds which is basically generating one or two really big .cpp with all the headers and .cpp from the whole project included, and then compile it as a single translation unit. This approach is very reliable when it comes to inlining virtual methods (devirtualization) since the compiler does have everything to make the required optimizations
Question what drawbacks does have this approach regarding more "elegant & modern" methods like link-time 开发者_如何学编程optimization?
The technical name, approaching minor buzzword status, for that approach is unity build.
See for example:
The benefits / disadvantages of unity builds?
The downside is best described here:
http://leewinder.co.uk/blog/?p=394
The short version is it is more or less a choice of languages: you either write in regular-C++ or Unified-build-C++. The 'correct' way of writing virtually any code will differ between the two.
- For a large project, the technique of having "all files in one" potentially increases the build time, though that only matters to the developers. With multiple smaller files, one code change usually causes less code to compile, therefore it should be a faster incremental build. If changes are more often to header files or other components which many files are dependent upon, then the single file approach saves build time since header files need not be processed by multiple dependents when changed.
- There are a surprising number of compilers which cannot process very large source files, even those which are widely used and have popularity far in disproportion to their quality. Such compilers tend to choke at random, report errors for syntactically correct code, or generate incorrect instructions.
- A project with multiple developers and old version control tools might have trouble coordinating changes to the limited number of modules.
Just think about it for a minute. There's some part of your program, call it A, and without any optimization it takes time T, but with optimization it takes, say, T/2.
Now there are other parts of your program also, so let's think about what effect A's optimization has on the whole program.
If the time originally spent in A was none (0%) then what is the effect of A's optimization? Zero.
If the time originally spent in A was all (100%) then what is the effect of A's optimization? T/2
If the time originally spent in A was half (50%) then the effect of A's optimization is T/4.
So the benefit of optimizing a piece of code is proportional to the amount of time originally spent in that code.
So if one wants inlining, avoidance of virtual functions, etc. to have significant benefit, what kind of code does it have to be?
It has to be code that, before optimization, contained the program counter (exclusive time) a significant fraction of the time.
In significant applications, containing many layers of function/method calls, where the call tree nearly always dips down into new
, or I/O, or string libraries, or data structure libraries, or database libraries, that are outside the application's source code, what percent of total time is exclusive time in the code compiled as part of the application?
Often (not always) from little to very little. And the potential benefit from inlining or other compiler/linker optimization is proportional to that.
One obvious drawback is potential clash between static or local symbols:
// File a.cpp
namespace
{
void f() { make_peace(); }
}
void action() { f(); }
// file b.cpp
void f() { launch_missiles(); }
void action2() { f(); }
If b.cpp
is included before a.cpp
, Bad Things happen.
Another drawback is that compilers (MSVC ?) may not cope very well with large files.
Also, every time you change a tiny bit of your code, you're going for full compilation.
In conclusion, I'd never do that. It is not worth the extra bucks for a SSD* or a more powerful build machine. And Link time code generation is usually good enough.
The rare benefits I see is enhanced virtual function analysis, and better alias analysis, but I really don't think it is worth it. You can use restrict
or its variants for the latter, and the former is very rare to have a real impact on your program's performance.
* If compilation times really bother you, try to replace hard disks with SSDs on your build machines. Magic.
精彩评论