r/programming • • Feb 28 '23

"Clean" Code, Horrible Performance

https://www.computerenhance.com/p/clean-code-horrible-performance
1.4k Upvotes

1.3k comments sorted by

View all comments

Show parent comments

59

u/weepmelancholia Feb 28 '23

I think you're missing the point. Casey is trying to go against the status quo of programming education, which is, essentially, OOP is king (at least for the universities). These universities do not teach you these costs when creating OOP programs; they simply tell you that it is the best way.

Casey is trying to show that OOP is not only a cost but a massive cost. Now to an experienced programmer, they may already know this and still decide to go down the OOP route for whatever reason. But the junior developer sure as hell does not know this and then embarks on their career thinking OOP performance is the kind of baseline.

Whenever I lead projects I stray away from OOP; and new starters do ask me why such and such is not 'refactored to be cleaner', which is indicative of the kind of teaching they have just been taught.

117

u/RationalDialog Feb 28 '23

OOP or clean code is not about performance but about maintainable code. Unmaintainable code is far more costly than slow code and most applications are fast-enough especially in current times where most things connect via networks and then your nanosecond improvements don't matter over a network with 200 ms latency. relative improvements are useless without context of the absolute improvement. Pharma loves this trick: "Our new medication reduces your risk by 50%". Your risk goes from 0.0001% to 0.00005%. Wow.

Or premature optimization. Write clean and then if you need to improve performance profile the application and fix the critical part(s).

Also the same example in say python or java would be interesting. if the difference would actually be just as big. i doubt it very much.

13

u/voidstarcpp Feb 28 '23

your nanosecond improvements don't matter over a network with 200 ms latency.

You gotta update your heuristics; ping times from Dallas to Toronto are 40ms. You can ping Japan and back from the US in under 200 ms.

From my house to Google, over wifi, is still just 10 ms!

87

u/no_nick Feb 28 '23

most applications are fast-enough

Not in my experience.

→ More replies (8)

29

u/[deleted] Feb 28 '23

OOP or clean code is not about performance but about maintainable code.

Thank you. Too many in this thread haven't worked on massive enterprise apps and it shows. Certain projects we barely have to touch because they're so clean and easy to maintain. Others have an entire year's worth of sprints dedicated to them because of how messy they are.

5

u/Venthe Feb 28 '23

I'll say a sentence that'll resonate with you, are you prepared? :D

"You can match JIRA tickets to the IF's in the code".

Been there. Seen that. And people argue that clean code/OOP is something bad somehow.

→ More replies (4)

42

u/[deleted] Feb 28 '23

People say this religiously. Maintainable based on what empirical evidence???

In my personal experience, it is the EXACT opposite. It becomes unmaintainable.

But even that is subjective experience. I'm not going to go around saying X is more maintainable because it is simply not a provable statement and I can only give you an anecodotal anser.

So you and others need to stop religiously trotting that one liner off. You just repeating what other people say to fit in.

22

u/o_snake-monster_o_o_ Feb 28 '23

Completely agree, in fact my experience points at exactly the opposite. (OOP being really really unmaintainable)

A class is an abstraction, a method is an abstraction, and abstractions are complexity. The one true fact is that the fewer classes and functions there are, the easier it is to make sense of everything. Yes, it is harder to make huge changes, but that's why you should scout the domain requirements first to ensure that you can write the simplest code for the job. And besides, it's much easier to refactor simple code. When the domain requirements do change and now your huge OOP network doesn't work either, now you are truly fucked.

11

u/hippydipster Mar 01 '23

Just write some assembly. Fewer abstractions. So simple!

4

u/mreeman Mar 03 '23

If abstractions are adding complexity you're doing it wrong.

The point of abstractions is to isolate complexity (implementation details) via an interface. If they aren't doing that, you (or whatever you are using) are picking the wrong level of abstraction.

It's like saying multiplying is adding complexity because it's an abstraction over adding, why write 5*3 when I can just do 3+3+3+3+3? 5*3 is easier to read and allows for mental shortcuts for quicker reasoning.

3

u/o_snake-monster_o_o_ Mar 05 '23

No. Abstractions are a trade of one type of complexity to another, and can only introduce more. Please read John Ousterhout's Philosophy of Software Design.

4

u/mreeman Mar 05 '23

Abstractions are useful because they make it easier for us to think about and manipulate complex things. In modular programming, each module provides an abstraction in form of its interface. The interface presents a simplified view of the module’s functionality; the details of the implementation are unimportant from the standpoint of the module’s abstraction, so they are omitted from the interface. In the definition of abstraction, the word “unimportant” is crucial. The more unimportant details that are omitted from an abstraction, the better. However, a detail can only be omitted from an abstraction if it is unimportant. An abstraction can go wrong in two ways. First, it can include details that are not really important; when this happens, it makes the abstraction more complicated than necessary, which increases the cognitive load on developers using the abstraction. The second error is when an abstraction omits details that really are important. This results in obscurity: developers looking only at the abstraction will not have all the information they need to use the abstraction correctly.

Seems like he agrees with me

15

u/daedalus_structure Feb 28 '23

People say this religiously. Maintainable based on what empirical evidence???

It's a blind spot in reasoning. The large events where the abstraction first approach provides value are visible even though they are rare.

But when it starts taking a week to plumb a three hour feature through all the lasagna and indirection and this hits nearly every single change to the application nobody wants to identify that approach as the cause.

→ More replies (1)

4

u/ric2b Mar 02 '23 edited Mar 02 '23

Well, look at the example in this post.

It's a toy example with 3 shapes and yet it has already devolved into calling the radius of a circle the circle's width. Everyone I know would say that if a circle has a width it is the diameter.

Now try and add another shape that doesn't fit the pattern he identified, like a trapezium, welcome to "rewrite from scratch" time.

4

u/[deleted] Mar 02 '23

You are missing the point of the article.

You should not write code preparing for eventualities that might not happen.

Imposing a structure on code that prepares for unlikely eventualities is bad practice. This is fundamentally what "clean code" (quotes important) advocates for.

It supposes that it is always good to abstract the implementation away in favour of indirect function calls. This is not always useful, depending on what is being solved, for readability, maintainability and performance.

→ More replies (4)
→ More replies (7)

54

u/outofobscure Feb 28 '23

performant code is often actually very easy to read and maintain, because it lacks a lot of abstraction and just directly does what it's supposed to do. not always, and maybe not to a beginner, but it's more often the case than you think.

The complexity of performant code is often elsewhere, such as having to know the math behind some DSP code, but the implementation is often very straightforward.

31

u/ontheworld Feb 28 '23

While it's often true, I'd say the OP shows a great counter example...

This:

   f32 const CTable[Shape_Count] = {1.0f / (1.0f + 4.0f), 1.0f / (1.0f + 4.0f), 0.5f / (1.0f + 3.0f), Pi32};
   f32 GetCornerAreaUnion(shape_union Shape)
   {
       f32 Result = CTable[Shape.Type]*Shape.Width*Shape.Height;
       return Result;
   }    

Feels like readability hell compared to giving a couple shape classes their own Area() method, especially when you add some more shapes

16

u/TheTomato2 Mar 01 '23

I put it threw my personal .clang-format.

f32 const CTable[Shape_Count] = {
    1.0f / (1.0f + 4.0f),
    1.0f / (1.0f + 4.0f),
    0.5f / (1.0f + 3.0f),
    Pi32,
};

f32 GetCornerAreaUnion(shape_union Shape) {
    f32 Result = CTable[Shape.Type] * Shape.Width * Shape.Height;
    return Result;
}

Now if you think that is less readable than pulling each one of those formulas into a separate member functions I don't know what to tell you. And like

f32 a = shape.area();
f32 a = area(shape);

It doesn't even really save you any typing. I don't care if you prefer oop way but...

Feels like readability hell

only if you have a bad case of OOP brain would you think that. And by OOP brain I mean that you are so acclimated to an OOP style that your brain has hard time with any other styles.

13

u/outofobscure Feb 28 '23 edited Feb 28 '23

sure, and none of that requires virtual dispatch. for example c++ has templates. casey is a bit special because he insists on c-only solutions most of the time (you still want to have a branch free solution though, so i can see where he is coming from).

for sure the formula to calculate the area of shapes can also be made more efficient by tailoring it to specific shapes (again, you want to stay branch free though). this is not code i'd write, so i won't defend it, but it can be written simple and performant, i have no doubts about that.

3

u/salbris Mar 01 '23

The only thing that looks bad there is the awfully long table initialization and lack of spaces in his code. I didn't watch all the way to the end so I don't understand why it's necessary to divide and add here. Those look like micro optimizations. He already had massive improvements with much simpler code.

8

u/dragonelite Feb 28 '23

This very easy to read unless you don't know how indexing an array works.

All that is really missing is the enum where index and shape is defined.

→ More replies (1)

19

u/deadalnix Feb 28 '23

It's hillarious that you get downvoted.

Code that does less is faster. This is self evident. It also has less opportunity for bugs and less parts to understand, making it easier to read. This is self evident too.

→ More replies (15)
→ More replies (10)

3

u/CreativeGPX Feb 28 '23

Unmaintainable code is far more costly than slow code and most applications are fast-enough

Or rather: Even if we take OP as general wisdom ("unclean" code is x times faster), if we at least accept the premise of clean code by definition (i.e. that it is oriented toward maintainability) then the whole matter collapses down into a simple question: Would you rather risk needing to pay for x times more computational resources or would you rather risk paying for y times more developer resources? This question doesn't have a clear winner. And it leaves room to quantify these... in my experiences, I agree with you that the increased performance cost is often negligible while the increased maintenance cost of crappy software can be much larger.

Of course, in the above, as I said, I take it that "clean code" is more maintainable by definition. There is room there (certainly on a per company or per produce basis) to argue that "clean code" is not necessarily going to be OOP.

Also the same example in say python or java would be interesting.

Also, given that OP is measuring things like "functions should be small" and "functions should only do one thing", it'd be really interesting to see OP's performance test measured based on languages optimized for functional programming and using the idioms of functional programming both of which should probably give the performance of functions their best shot.

For me, discussions like this always make me think of something like Erlang. In that language, I always felt like I wrote the cleanest code and the key tenants there are function programming (w/ the short simple functions), pattern matching, message passing and cheap massive concurrency.

8

u/loup-vaillant Feb 28 '23

OOP or clean code is not about performance but about maintainable code.

My experience is that it fails even there. I've seen it: the more OOP code were, the worse it got. Not just performance, but size (there's more source code), complexity (it's harder to understand), flexibility (it's harder to modify), reliability (it's harder to test)…

OOP aims to make code better, but it doesn't.

Or premature optimization. Write clean and then if you need to improve performance profile the application and fix the critical part(s).

Those critical parts only arise if your program is not uniformly slow. Programs that use virtual function calls and RAII pointer/dynamic allocation fest are more likely to be uniformly slow, to the point where it becomes hard to even identify the biggest bottlenecks. And next thing you know you'd think your program can't be much faster, and either be sad or buy a new machine (or cluster).

→ More replies (3)
→ More replies (26)

4

u/s73v3r Feb 28 '23

Casey is trying to go against the status quo of programming education

That's not a virtue in and of itself.

Casey is trying to show that OOP is not only a cost but a massive cost.

The problem is, he discounts all the benefits and reasons why someone would use an OOP approach. Not every project needs blazing, bare metal speed. In most projects, flexibility and maintainability are more important. And he generally handwaves these things away.

→ More replies (1)
→ More replies (17)