Depending on your CPU, you might have, say, 32KB of 8-way associative instruction cache per core. Just being shared does not make it fit in the cache.
A shared library would only be there across processes of different executable images if its users primarily, continuously execute the same paths in shared libs rather than anything unique in their own executable image - e.g., they'd more or less need to be stuck in the same processing-intensive shared routine in the lib. There would also have to be no other processing done in between by other processes that would have trashed the cache.
On the other hand, the severe cache penalty of longer code paths for each executable and the larger PLT call overhead will universally lead to a loss in performance for all library usage.
The scenarios you may hit where different processes are actually executing the same shared code paths to the point of benefiting from shared cache utilization would be cases where they share executable image as well. E.g., browser processes, threads, compilers. Electron too if using system-packaged electron binaries.
> A shared library would only be there across processes of different executable images if its users primarily, continuously execute the same paths in shared libs rather than anything unique in their own executable image
Yes, like libc/WinApi, Cairo/whatever graphics library of your OS, Gtk/Qt etc
All of the apps on my desktop (I don't use electron) share vast majority of their code (see above) and spend most of their time drawing UI through shared libs or doing their own business logic but that's part of their code and not subject to shared lib overhead.
It's really important to emphasize how small caches are (especially as even the KBs they have cannot always be fully utilized). When we talk about processor caches, we're trying to make the current routine of one process fit well* - not even the whole thing.
No, you don't have a desktop environment where a majority of your unique executables all spend time at the same time in the same small, compute-heavy libc routine with no other processes to trash the cache in between.
For Gtk for example, the applications are not taking the same inputs and drawing the same GUI at the same time, with no other processes in between to wipe the cache.
Instead they're primarily running their own application logic in their own time, and interaction with Gtk (accepting input, rendering) is on timescales so long that the cache has been wiped out over and over in between (16ms is practically infinity at these scales). In these cases, the cache will be filled at the time of execution with e.g. that apps runtime data, widget tree and what not.
At the same time, remember that a static routine is much smaller and faster. Even if way say you fetch a shared library routine from cache and save some cycles there, every single call to it incurs large performance hits over the static linkage: a few cycles to every call from the PLT, possibly many cycles from poorer optimization (e.g., branches you never need), and cycles from not fitting as well in the tiny instruction cache as the process executes it.
... And if that shared lib routine calls other shared libs, then you get to apply the overheads and lost optimization recursively. This recursion where the static linkage pruning is especially effective: you might have tens or hundreds of megabytes of dependencies dynamically, but single megabyte static output as unused functionality is pruned.
There is no realistic scenario where dynamic linkage wins on performance or cacheability - it just doesn't play well with how caches end up used. Overall system memory utilization can be slightly lower for dynamic linkage in some cases, but not in a notable way.
But this is the most common case for desktops/multipurpose systems.
On my desktop there are tens or hundreds distinct processes sharing most of their code.