Yep. For hot call sites, these optimizations & inlining opportunities make a massive difference to performance. Static linking also allows for faster application startup time. (Though I don't have an intuition for exactly how slow dynamic linking is).
The only argument for dynamic linking being more efficient is that each dynamic library can be shared between all programs that use it. But not a net win in all cases. When you dynamically link a library, the entire library is loaded into RAM. When you static link, dead code elimination means that only the code you actually run in the library needs to be loaded.
But honestly, none of these arguments are strong. Dyld is fast enough on modern computers that we don't notice it. And RAM is cheap enough these days that sharing libraries between applications for efficiency feels a bit pointless.
The real arguments are these:
- Dynamic linking puts more power in the hands of the distribution (eg Debian) to change the library that a program depends on. This is used for security updates (eg OpenSSL) or for UI changes on Apple platforms. Dynamic linking is also faster than static linking, so compile times are faster for large programs.
- Static libraries put power in the hands of the application developer to control exactly how our software runs. VMs and Docker are essentially wildly complicated ways to force static linking, and the fact that they're so popular is evidence of how much this sort of control is important to software engineers. (And of course, statically linked binaries are usually simpler to deploy because static binaries make fewer assumptions about their runtime environment.)
> When you dynamically link a library, the entire library is loaded into RAM.
It doesn't. When you dynamic link a library, no part of it is loaded into RAM. It is page-faulted in, as it is used. In the end, only parts that were really used were loaded into RAM.
A page will be loaded in if any part of it is useful. Given that functions will be laid out more or less randomly throughout a shared library, and programs use a randomly scattered subset of the functions, I think its safe to say that you'll get a lot of bytes read in to ram that are never used.
Especially when we take the filesystem's read-ahead cache into account - which will optimistically load a lot of bytes near any executed function.
If your program makes use of some arbitrary 10% of the functions in a shared library, how much of the library will be read from disk? How much will end up in RAM? Its going to be much more than 10%. I'd guess that you'll end up with closer to 50% of the library loaded in memory, in one way or another. (Though I could be way off. I suspect most of the time the filesystem cache will end up loading the whole thing.)
If its 50% loaded, a shared library thats used once will waste 90% of its download size & disk space and 50% of its ram usage compared to the equivalent static library. And make the application slower to start because it needs to link at runtime. And make the program slower to run because of missed inlining opportunities.
> A page will be loaded in if any part of it is useful. Given that functions will be laid out more or less randomly throughout a shared library, and programs use a randomly scattered subset of the functions, I think its safe to say that you'll get a lot of bytes read in to ram that are never used.
Dynamic linking also allows for extensible applications that have to otherwise be implemented with slower OS IPC calls, and higher resource costs in process and CPU cores management.
Which also prevents good privilege separation/sandboxing. See PAM vs BSD Auth, where the former cannot be secured with anything like pledge/unveil or Capsicum, but the latter can.
The only argument for dynamic linking being more efficient is that each dynamic library can be shared between all programs that use it. But not a net win in all cases. When you dynamically link a library, the entire library is loaded into RAM. When you static link, dead code elimination means that only the code you actually run in the library needs to be loaded.
But honestly, none of these arguments are strong. Dyld is fast enough on modern computers that we don't notice it. And RAM is cheap enough these days that sharing libraries between applications for efficiency feels a bit pointless.
The real arguments are these:
- Dynamic linking puts more power in the hands of the distribution (eg Debian) to change the library that a program depends on. This is used for security updates (eg OpenSSL) or for UI changes on Apple platforms. Dynamic linking is also faster than static linking, so compile times are faster for large programs.
- Static libraries put power in the hands of the application developer to control exactly how our software runs. VMs and Docker are essentially wildly complicated ways to force static linking, and the fact that they're so popular is evidence of how much this sort of control is important to software engineers. (And of course, statically linked binaries are usually simpler to deploy because static binaries make fewer assumptions about their runtime environment.)