Live data from Hacker News

Inheritance was invented as a performance hack (2021)

catern.com

41–50 of 252 posts

Re: Inheritance was invented as a performance hack (2021)

#41
post #35

Earlier quoted context omitted.

> "Trait/Typeclass"-style compositional inheritance as in Rust and Haskell is sublime. It's similar to Java interfaces in terms of flexibility, and it doesn't enforce hierarchical rules. Yes-and-no. Interfaces still participate in inheritance hierarchies (`interface Bar extends Foo`), and that's in a way that prohibits removing/subtracting type members (so interfaces are not in any way a substitute for mixins). Compo…

How are interfaces with ability to provide default implementations for members (which both C# and Java allow today) not a substitute for mixins? "Only reference types can implement interfaces" is simply not true in C#. Not only can structs implement them, but they can also be used through the interface without boxing (via generics).

They just cannot access any field in the class itself. Which means... the default implementations fail to actually implement anything but the simplest methods or you need to expose API-private things via getters violating open-closed principle.

(If you merge multiple interfaces, the implementations of the methods have to match. You end up with even more special getters for each one sometimes.)

Re: Inheritance was invented as a performance hack (2021)

#42
post #12
post #2

Huh, I was always told that inheritance hurt performance as it requires additional address lookups. Thats why many game engines are moving away from it. I guess it could simplify the GC but modern garbage collectors have come a long way.

No, inheritance does not require additional address lookups. Single inheritance as discussed here doesn't even require additional address arithmetic; the address of the subclass instance is the same as the address of the superclass instance. Yes, current GCs are very fast and do not suffer from the problems Simula's GC suffered from. Nevertheless, they do still have an easier time when you embed record A as a field o…

> No, inheritance does not require additional address lookups. Single inheritance as discussed here doesn't even require additional address arithmetic; the address of the subclass instance is the same as the address of the superclass instance.

Yes it does! Inheritance itself is fine, but inheritance almost always means virtual functions - which can have a significant performance cost because of vtable lookups. Using virtual functions also prevents inlining - which can have a big performance cost in critical code.

> Nevertheless, they do still have an easier time when you embed record A as a field of record B (roughly what inheritance achieves in this case) rather than putting a pointer to record A in record B.

Huh? No - if you put A and B in separate allocations, you get worse performance. Both because of pointer chasing (which matters a great deal for performance). And also because you're putting more pressure on the allocator / garbage collector. The best way to combine A and B is via simple composition:

    struct C { a: A, b: B }
In this case, there's a single allocation. (At least in languages with value types - like C, C++, C#, Rust, Swift, Zig, etc). In C++, the bytes in memory are actually identical to the case where B inherits from A. But you don't get any class entanglement, or any of the bugs that come along with that.

> I think the reason many game engines are moving away from inheritance is that they're moving away from OO in general

Games are moving away from OO because C++ style OO is a fundamentally bad way to structure software. Even if it wasn't, struct-of-arrays usually performs better than arrays-of-structs because of how caching works. And modern ECS (entity component systems) can take good advantage of SoA style memory layouts.

The performance gap between CPU cache and memory speed has been steadily growing over the last few decades. This means, relatively speaking, pointers are getting slower and big arrays are getting faster on modern computers.

Re: Inheritance was invented as a performance hack (2021)

#43
post #36

Earlier quoted context omitted.

Yeah ya... everyone likes to go on and on about how inheritance is the root of all evil and if you just don't use it, everything will be fine. Sorry, it won't be fine. Your software will still be a mess unless it is small and written three times by the same person who knows what they are doing. The bottom line is, no one ever really used inheritance that much anyway (other than smart people trying to outsmart themsel…

> The bottom line is, no one ever really used inheritance that much anyway If you think that, you have no idea how much horrible code is out there. Especially in enterprise land, where deadlines are set by people who get paid by the hour. I once worked on a java project which had a method - call a method - call a method - call a method and so on. Usually, the calls were via some abstract interface with a single imple…

I don't think Inheritance is always bad - sometimes it's a useful tool. But it was definitely overused and composition, interfaces work much better for most problems.

Inheritance really shines when you want to encapsulate behaviour behind a common interface and also provide a standard implementation. I.e: I once wrote a RN app which talked to ~10 vacuum robots. All of these robots behaved mostly the same, but each was different in a unique way. E.g. 9 robots returned to station when the command "STOP" was send, one would just stop in place. Or some robots would rotate 90 degrees when a "LEFT" command was send, others only 30 degrees. We wrote a base class which exposed all needed commands and each robot had an inherited class which overwrote the parts which needed adjustment (e.g. sending left three times so it's also 90 degrees or send "MOVE TO STATION" instead of "STOP").

Re: Inheritance was invented as a performance hack (2021)

#44
post #2

Huh, I was always told that inheritance hurt performance as it requires additional address lookups. Thats why many game engines are moving away from it. I guess it could simplify the GC but modern garbage collectors have come a long way.

As I understand it, back when Simula and LISP were invented it was generally the case that loads and stores took 1 cycle and there were no CPU caches. These pointer-chasing languages and techniques really weren't technically bad for the computers of the time - it's just that we have a larger relative penalty for randomly accessing our Random Access Memory these days so locallity is important (hence data-oriented desi…

Function arguments weren't actually first-class to begin with. In Algol 60 (of which Simula started as a superset), you could pass functions as arguments to other functions, but that's it - it wasn't a proper type so you couldn't return it, shove it into a variable, have an array of functions etc. Basically, it had just enough restrictions that you would never get up in a situation where you could possibly call a function for which the corresponding activation frame (i.e. locals) could be gone. But when Simula added classes and objects, now you could suddenly capture arguments in a way that allows them to outlive the callee.

Re: Inheritance was invented as a performance hack (2021)

#45
post #21
post #16

Earlier quoted context omitted.

This assumes that the vtables stay in at least L2 cache, which may be a correct assumption for the few hot-path classes. In this regard, I remember how Facebook's android app once failed to build when the codebase exceeded the limit of 64k classes.

No, Java does Class hierarchy analysis and has multiple way not to use v-table calls. Single site (no class found overriding a method) are static and can be inlined directly. Dual call sites use a class check (which is a simple equality), can be inlined, no v-table. 3-5 call sites use inline caches (e.g. the compiler records what class have been used) that are similar and some can be inlined, usually plus a guard che…

Thats very cool.

Does C++ have any of these optimisations?

Re: Inheritance was invented as a performance hack (2021)

#46
post #36

Earlier quoted context omitted.

Yeah ya... everyone likes to go on and on about how inheritance is the root of all evil and if you just don't use it, everything will be fine. Sorry, it won't be fine. Your software will still be a mess unless it is small and written three times by the same person who knows what they are doing. The bottom line is, no one ever really used inheritance that much anyway (other than smart people trying to outsmart themsel…

> The bottom line is, no one ever really used inheritance that much anyway If you think that, you have no idea how much horrible code is out there. Especially in enterprise land, where deadlines are set by people who get paid by the hour. I once worked on a java project which had a method - call a method - call a method - call a method and so on. Usually, the calls were via some abstract interface with a single imple…

I think that's part of the charm of Go, as a language/community.

I've worked with countless people who came from Java, who try to create the same abstractions and factories and layers.

When I chide them, it's like realizing the shackles are off, and they have fun again with the basics. It leads to much more readable, simple code.

This isn't to say Java is bad and Go is good, they're just languages. It's just how they're typically (ab)used in enterprises.

Re: Inheritance was invented as a performance hack (2021)

#48
post #14
post #5

Earlier quoted context omitted.

Simple inheritance makes the class hierarchy complicated through issues like the diamond inheritance problem, which C++ resolves in typical C++ fashion: attempt to satisfy everybody, actually satisfy nobody. The designers of StarCraft ran into the pitfalls of designing a sensible inheritance hierarchy, as described here (C-f "Game engine architecture"): https://www.codeofhonor.com/blog/tough-times-on-the-road-to-...

Simple inheritance doesn't have the diamond problem, because that requires multiple inheritance, which isn't simple. Smalltalk doesn't have multiple inheritance; I don't think SIMULA did either.

Simula is strictly single inheritance (and no interfaces).

Re: Inheritance was invented as a performance hack (2021)

#49
post #39

Earlier quoted context omitted.

It is a good idea because it's the most fundamental idea. You have two objects. A and B. How do you merge the two objects? A + B? The most straight forward way is inheritance. The idea is fundamental. The reason why it's not practical has more to do with human nature and the limitations of our capabilities in handling complexity then it has to do with the concept of inheritance itself. Literally think about it. How e…

> Literally think about it. How else do you merge two structs if not using inheritance? What? Using multiple inheritence? That's one of the worst ideas I've ever seen in all of computer science. You can't just glue two arbitrary classes together and expect their invariants to somehow hold true. Even if they do, what happens when both classes implement a method or field with the same name? Bugs. You get bugs. I've bee…

>What? Using multiple inheritence?

You just threw this in out of nowhere. I didn't mention anything about "multiple" inheritance. Just inheritance which by default people usually mean single inheritance.

That being said multiple inheritance is equivalent to single inheritance of 3 objects. The only problem is because two objects are on the same level it's hard to know which property overrides which. With a single chain of inheritance the parent always overrides the child. But with two parents, we don't know which parent overrides which parent. That's it. But assume there are 3 objects with distinct properties.

   A -> B -> C
would be equivalent to

   A -> C 
They are isomorphic. Merging distinct objects with distinct properties is commutative which makes inheritance of distinct objects commutative.

   C -> B -> A == A -> B -> C
>I've been programming for 30 years and I've still never seen an example of multiple inheritance that hasn't eventually become a source of regret.

Don't ever tell me that programming for 30 years is a reason for being correct. It's not. In fact you can be doing it for 30 years and be completely and utterly wrong. Then the 30 years of experience is more of a marker of your intelligence.

The point is YOU are NOT understanding WHAT i am saying. Read what I wrote. The problem with inheritance has to do with human capability. We can't handle the complexity that arises from using it extensively.

But fundamentally there's no OTHER SIMPLER way to merge two objects without resorting to complex nesting.

Think about it. You have two classes A and B and both classes have 90% of their properties shared. What is the most fundamental way of minimizing code reuse? Inheritance. That's it.

Say you have two structs. The structs contain redundant properties. HOW do you define one struct in terms of the other? There's no simpler way then inheritance.

>> Composition is often more appropriate than inheritance.

You can use composition but that's literally the same thing but wierder, where instead of identical properties overriding other properties you duplicate the properties via nesting.

So inheritance

   A = {a, b}, C = {a1}, A -> C = {a1, b}
Composition:

   A = {a, b}, C = {a1}, C(A) = {a1, {a, b}}
That's it. It's just two arbitrary rules for merging data.

If you have been programming for 30 years you tell me how to fit this requirement with the most minimal code:

given this:

   A = {a, b, c, d}
I want to create this:

   B = {a, b, c, d, e} 
But I don't want to rewrite a, b, c, d multiple times. What's the best way to define B while reusing code? Inheritance.

Like I said the problem with inheritance is not the concept itself. It is human nature or our incapability of DEALING with the complexity that arises from it. The issue is the coupling is two tight so you make changes in one place it creates an unexpected change in another place. Our brains cannot handle the complexity. The idea itself is fundamental not stupid. It's the human brain that is too stupid to handle the emergent complexity.

Also I don't give two flying shits about google style guides after the fiasco with golang error handling. They could've done a better job.

Re: Inheritance was invented as a performance hack (2021)

#50
post #39

Earlier quoted context omitted.

It is a good idea because it's the most fundamental idea. You have two objects. A and B. How do you merge the two objects? A + B? The most straight forward way is inheritance. The idea is fundamental. The reason why it's not practical has more to do with human nature and the limitations of our capabilities in handling complexity then it has to do with the concept of inheritance itself. Literally think about it. How e…

> Literally think about it. How else do you merge two structs if not using inheritance? What? Using multiple inheritence? That's one of the worst ideas I've ever seen in all of computer science. You can't just glue two arbitrary classes together and expect their invariants to somehow hold true. Even if they do, what happens when both classes implement a method or field with the same name? Bugs. You get bugs. I've bee…

I guess the point was never how to do thing properly, but:

"how to join two struts with least amount of work and thinking so my manager can tick off a box in excel"

in such case inheritance is a nice temporary crutch

Post reply on HN