Zero-cost interfaces?

Or when compilers fail at devirtualization.

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.