Live data from Hacker News

“Clean” code, horrible performance

computerenhance.com

81–90 of 932 posts

Re: “Clean” code, horrible performance

#81
Much of this is very compiler dependent. For example, Java's compiler is generally able to perform more aggressive optimisations, and even virtual calls are often, and even usually, inlined (so if at a particular call-site only one shape is encountered, there won't even be a branch, just straight inline code, and if there are only two or three shapes, the call would compile to a branch; only if there are more, i.e. a "megamorphic" call site will a vtable indirection actually take place). There is no general way of concluding that a virtual call is more or less costly than a branch, but the best approximation is "about the same."

Having said that, even Java now encourages programmers to use algebraic data types when "programming in the small", and OOP/encapsulation at module boundaries: https://www.infoq.com/articles/data-oriented-programming-jav... though not for performance reasons. My point being is that the "best practice" recommendations for mainstream language does change.

Re: “Clean” code, horrible performance

#82
Most of those Clean code rules are BS.

1. Prefer polymorphism to “if/else” and “switch” - if anything, that makes code less readable, as it hides the dispatch targets. Switch/if is much more direct and explicit. And traditional OOP polymorphism like in C++ or Java makes the code extensible in one particular dimension (types) at the expense of making it non-extensible in another dimension (operations), so there is no net win or loss in that area as well. It is just a different tool, but not better/worse.

2. Code should not know about the internals of objects it’s working with – again, that depends. Hiding the internals behind an interface is good if the complexity of the interface is way lower than the complexity of the internals. In that case the abstraction reduces the cognitive load, because you don't have to learn the internal implementation. However, the total complexity of the system modelled like that is larger, and if you introduce too many indirection levels in too many places, or if the complexity of the interfaces/abstractions is not much smaller than the complexity they hide, then the project soon becomes an overengineered mess like FizzBuzz Enterprise.

3. Functions should be small – that's quite subjective, and also depends on the complexity of the functions. A flat (not nested) function can be large without causing issues. Also going another extreme is not good either – thousands of one-liners can be also extremely hard to read.

4. Functions should do one thing – "one thing" is not well defined; and functions have fractal nature - they appear do more things the more closely you inspect them. This rule can be used to justify splitting any function.

5. “DRY” - Don’t Repeat Yourself – this one is pretty good, as long as one doesn't do DRY by just matching accidentally similar code (e.g. in tests).

Re: “Clean” code, horrible performance

#83

I don't think there is a contradiction or surprising point here. At least my understanding of the case for clean code is that developer time is a significantly more expensive resource than compute, therefore write code in a way which optimises for developers understanding and changing it, even at the expense of making it slower to run (within sensible limits etc etc).

Yeah you're right, nobody ever said to me "use interfaces because the code it's faster". On the other hand, when I studied dynamic dispatch and stuff like that, I don't think enough people told me "when you do that, you are making this tradeoff". I feel like it's worth sharing this kind of knowledge in order to make better informed decisions (possibly based on numbers). There's no need to become an extremist in eithe…

The trade off has changed over time as memory access has become a bigger and bigger bottleneck. But caring about this is still "premature optimisation".

The claim of 20x program performance difference is overblown. Compilers can often remove virtual function calls, JITs can also do it at runtime. Virtual function calls in a tight loop are slow but most of your program isn't in a tight loop and few programs have compute as a bottleneck.

Measure your program, find the tight loops in your program and optimise that small part of your program.

Re: “Clean” code, horrible performance

#85
post #81

Much of this is very compiler dependent. For example, Java's compiler is generally able to perform more aggressive optimisations, and even virtual calls are often, and even usually, inlined (so if at a particular call-site only one shape is encountered, there won't even be a branch, just straight inline code, and if there are only two or three shapes, the call would compile to a branch; only if there are more, i.e. a…

> and even virtual calls are often, and even usually, inlined.

Last time I checked it could not inline megamorphic call sites, evn if implementations were trivial (returning constants). At the same time I saw C++ compilers able to replace an analogue switch that dispatched to constants with a simple array lookup, with no branching at all.

Re: “Clean” code, horrible performance

#86

Earlier quoted context omitted.

> developer time is a significantly more expensive resource than compute Depends on the number of invocations of the program.

Not just that. The “developer time is valuable” mantra is thrown left and right, disregarding how much of that valuable resource will be wasted down the line due to bad implementations. If we optimize for developer time, let’s optimize across the software’s entire lifecycle, not just that first push of a MVP to production.

This is a good point but it gets complicated as you often don't know the software's entire lifecycle so you have to optimize for something slightly different.

Re: “Clean” code, horrible performance

#87
I take issue with the idea that maintainable code is about "making programmers' lives easier", rather than "making code that is correct and is easy to keep correct as it evolves". Correctness matters for the user - indeed, sometimes it is a matter of life and death.

Re: “Clean” code, horrible performance

#88
The example of using shape area seems like a poor choice.

First off, the number of problems where having an analytical measure of shape area is important is pretty small by itself. Second, if you do need to calculate area of arbitrary shapes, then limiting yourself to formulas of the type `width * height * constant` is just not going to cut it. And this is where the entire optimization exercise eventually leads: to build a table of precomputed areas for affinely transformed outlines.

Throw in an arbitrary polygon, and now it has to be O(n). Throw in a bezier outline and now you need to tesselate or integrate numerically.

What this article really shows is actually what I call the curse of computer graphics: if you limit your use cases to a very specific subset, you can get seemingly enormous performance gains from it. But just a single use case, not even that exotic, can wreck the entire effort and demand a much more complex solution which may perform 10x worse.

Example: you want to draw lines? Easy, two triangles! Unless you need corners to look good, with bevels or rounded joins, and every pixel to only be painted once.

Game devs like to pride themselves on their performance chops, but often this is a case of, if not the wrong abstraction, at least too bespoke an abstraction to allow future reuse irrespective of the use case.

This leads to a lot of dickswinging over code that is, after sufficient encounters with the real world, and sufficient iterations, horrible to maintain and use as a foundation.

So caveat emptor. Framing this as a case of clean vs messy code misses the reason people try to abstract in the first place. OO and classes have issues, but performance is not the most important one at all.

Re: “Clean” code, horrible performance

#89

TL;DR: "Game developer optimizes code for execution as opposed to readability that 'clean-code' people suggest". There are few considerations: - most code is not CPU bound so his claims that you are eroding progress because you are not optimizing for CPU efficiency is baseless - writing readable code is more important than writing super optimal code (few exceptions: gaming is one) - using enums vs OOP is not changing…

I'm tired of every tool I install on my latest gen Intel CPU + 32GB RAM + NVMe drive machine being a complete slog. To each their own, but I don't find Casey's performant version less readable, I don't see the need for so many abstractions.

“CLEAN” doesn’t care so much about readability, but rather testability. The video would have been far more compelling if Casey had spent more time showing how he would test his application without the test surface area blowing up exponentially as the feature space expands.

Re: “Clean” code, horrible performance

#90
This is the first video I’ve seen by him. I’m by no means a fan of clean code. But I think he’s making a fool of himself here. Picking out 1 code example from the book doesn’t proof that much on its own. This stuff is so language, os, hardware and compiler specific anyway.

The iPhone comparisons are extremely cringe. Real application do so much more then this contrived example. Something that feels fast isn’t the same thing as something is fast.

Would I advise beginner programmer’s to read this book? Sure, let them think about ways to structure code.

If he just had concluded with, that it is important to optimize for the right thing that would be fine. But he seems more interested in picking a fight with clean code.

And yes performance is a lost art in programming

Post reply on HN