Mainstream programming paradigm OOP promotes usage of interfaces for seams introduction that are useful for testing and design flexibility. Being spoiled by zero-cost abstraction principle embraced by languages like C++, it does not come as a surprise that for a trivial class hierarchy like
optimizing compilers like clang and gcc can easily deal with simple cases where concrete type is known with certainty at compile time. For example, both functions below
are both translated into
As soon as any dataflow analysis complications are introduced, clang starts having troubles and for a still fairly easy case like
is no longer able to reason about concrete type and as such produces
whereas GCC is still able to produce optimal assembly
but starts having troubles starting with
that results in
Obviously it’s not ideal but if you think that it’s limiting, let’s take a look at another popular programming language - Go. As a test victim we’ll use a trivial adder struct
and 2 benchmarks
that do the exact same thing, but BenchmarkAddConcrete
is invoking add
function on a concrete_adder struct type, which Go’s compiler is able to inline
and BenchmarkAddInterface
performs invocation through adder
interface which is obviously backed by concrete_adder struct which is apparently not obvious to Go’s compiler
This compiler limitation results in an expected almost 50% performance regression
which is particularly unfortunate given that Go does not support generics and as such encourages developers to rely on interfaces.