I work for a company that has a "Middleware" department of its own to maintain a few hundreds of libraries that are commonly used by a great many teams.
Despite being in the same company, we shy from header only approach and prefer to favor binary compability over performance because of the ease of maintenance.
The general consensus is that the performance gain (if any) would not be worth the trouble.
Furthermore, the so called "code-bloat" may have a negative impact on performance as more code to be loaded in the cache implies more cache miss, and those are the performance killers.
In an ideal world I suppose that the compiler and the linker could be intelligent enough NOT to generate those "multiple definitions" rules, but as long as it is not the case, I will (personally) favor:
- binary compatibility
- non-inlining (for methods that are more than a couple of lines)
Why don't you test ? Prepare the two libraries (one header only and the other without inlining methods over a couple of lines) and check their respective performance in YOUR case.
EDIT:
It's been pointed out by 'jalf' (thanks) that I should precise what I meant exactly by binary compatibility.
2 versions of a given library are said binary compatible if you can (usually) link against one or the other without any change of your own library.
Because you can only link with one version of a given library Target
, all the libraries loaded that use Target
will effectively use the same version... and here is the cause of the transitivity of this property.
MyLib --> Lib1 (v1), Lib2 (v1)
Lib1 (v1) --> Target (v1)
Lib2 (v1) --> Target (v1)
Now, say that we need a fix in Target
for a feature only used by Lib2
, we deliver a new version (v2)
. If (v2)
is binary compatible with (v1)
, then we can do:
Lib1 (v1) --> Target (v2)
Lib2 (v1) --> Target (v2)
However if it's not the case, then we will have:
Lib1 (v2) --> Target (v2)
Lib2 (v2) --> Target (v2)
Yep, you read it right, even though Lib1
did not required the fix, you head to rebuild it against a new version of Target
because this version is mandatory for the updated Lib2
and Executable
can only link against one version of Target
.
With a header-only library, since you don't have a library, you are effectively not binary compatible. Therefore each time you make some fix (security, critical bug, etc...) you need to deliver a new version, and all the libraries that depend on you (even indirectly) will have to be rebuilt against this new version!