Inheritance was a premature optimization for computers with small memory. It did its job. However, once memory got big, people forgot to throw it out.
Composition uses a lot more indirection. That's bad on modern CPUs. Pointer chasing throws out performance, so composition is not always preferred, either.
Simple composition, where an entity just has some components on it, can be completely "unrolled" at compile time, essentially removing all indirection as if the entity had all of the data to begin with. You don't need pointers.
For more complex composition, an ECS runtime would likely group all components of the same kind into the same place so that systems that ran with those components get even better data locality.
In C++ there is extremely little difference between the code granted for inheritance or composition (unless you use virtual inheritance), so I have no idea what overhead you are talking about.
Yeah, but that is a decade later, and also ignores the JIT optimizations like devirtualization, or just like C++ templates, using generics for composition.
JIT devirt can be nitpicky, PGO has improved the situation greatly but until around 6.0 or 7.0 devirt was easy to 'break' in a lot of common scenarios.
Heck, tying into the generic composition bit, ironically static generics still have issues right around devirts. Go figure.
Composition uses a lot more indirection. That's bad on modern CPUs. Pointer chasing throws out performance, so composition is not always preferred, either.