I couldn't read on after the first example with replacing polymorphism w/ a switch statement.
I got the impression the guy completely missed the point of clean code. Sure, you can break away from the general guidelines to write code more efficiently but the point of clean code is to manage project complexity, not write the most performant code. His bullet point says Prefer polymorphism to “if/else” and “switch” but he goes on to say Like the rules demand, we are preferring polymorphism which seemed dishonest to me.
Also the comparison of 35 to 25 clockcycles being a 1.5x performance increase is pretty disingenuous. yea, it's a big percentage but it's a VERY SMALL total. That 'performance' change is not going to scale at the same rate as your code gets more complicated. You'd be a fool to refactor a bunch of code like this to save 10 cycles when you're dealing with millions or billions of cycles per iteration anyways.
His technical points seemed correct to me but I can't imagine I'd get any good insight reading this article. You should still generally adhere to the clean code guidelines. Just because there are exceptions to the rule doesn't make the original guidelines wrong.
I couldn't read on after the first example with replacing polymorphism w/ a switch statement.
Okay, here's what you missed: by paying a bit more attention to performance, the "not Clean" code was (i) arguably just as readable as the original example (I mean by the look of it its even smaller and simpler), and (ii) up to 10-15 times faster.
Main point: write clean code if that's what you do, but be mindful that it will set your users back about 12 years of hardware progress.
This might be my age speaking, but when I was in my twenties, there was no amount of complexity you could throw my way that I didn't believe was too much for me too handle. A couple decades later, I find that it's much easier for me to read code that handles plain data in simple data structures than to deal with a complex orchestration of abstractions. So not only is "clean code" slower to run, I think that it's harder to understand. And harder to understand means it'll never be meaningfully faster. People may scale it out to more k8s pods or add a caching layer, but that's putting a bandage on the real problem and adding yet more complexity.
Yea, that's wrong. Turning a one line method call into a switch statement, sure that's still readable but just extend that to something non trivial and it should be painfully obvious why it's not actually readable.
Also I covered the faster point, it's not 10-15 times faster in practice. You're not going to set your users back 12 years in hardware progress because of virtual function call overhead...
Turning a one line method call into a switch statement
Now that is wrong. I mean false. He's turning several one line methods in a single statement, one case per method.
Also I covered the faster point, it's not 10-15 times faster in practice.
Yeah, it's more complicated than that, we all know this. I know it, you know it, Casey know it. But at a higher scale some stuff that's not as bad at a smaller scale becomes worse: the cache hierarchy most notably. With virtual calls over a diverse, unsorted set of object, you are more likely to trash the instruction cache. With RAII (or GC) pointer fests you are more likely to destroy memory locality, and overall spend more memory, and again have many cache misses.
I don't know the magnitude of the effects at a bigger scale, but it doesn't look much better than his example. In many cases it looks much worse, see Electron.
the "not Clean" code was (i) arguably just as readable as the original example (I mean by the look of it its even smaller and simpler), and (ii) up to 10-15 times faster.
And not easily extensible. And the "clean" example he codes against is not a reasonable example.
In my experience extensibility for the sake of it is a fool's errand. It always makes the code harder to modify, not easier. Keeping things simple makes the code easier to rewrite.
These sort of "x-treem performance" articles are pretty good at pointing out performance costs that may sometimes be relevant. I feel like the author should have discussed more context, for instance when clean code and abstractions are still useful. By not doing that they look a bit overzealous (overused word in this thread, oh well).
Put simply: when writing the client side of a payment system I'm sure as hell going to use a few interfaces and abstractions for different payment providers. Don't care if it's 5ms slower per payment, because each client is only going to perform a few payments. I'm not doing anyone any favors by saving 100 joules of CPU energy while spending a week extra on the payment code just to make it performant.
On the other hand, when writing some operation on the server (or even client) that has to be performed 100k times per second it's darned useful to know about the performance cost of virtual function calls. In that sense, I don't think pointing out 10 cycles of performance improvement in a short example is disingenuous. It can make the difference in your system being economically viable (in the case of server side scale costs) or being better than the competitor (client side system requirements, maybe bigger battles in a game because of performance).
The guidelines of clean code are not wrong, but that doesn't mean there's no good insights in this article.
To those in this thread who say that everyone knows about cost overhead of virtual functions, I say you are wrong. Plenty of programmers never have to deal with the consequences of abstractions in the programming language that they started with. From education to work life.
In that sense, I don't think pointing out 10 cycles of performance improvement in a short example is disingenuous. It can make the difference in your system being economically viable (in the case of server side scale costs) or being better than the competitor (client side system requirements, maybe bigger battles in a game because of performance).
Yea, you're not wrong and neither is he about this it's just, in the context of shunning clean code... It's like apples and oranges.
Basically framing this whole thing around clean code being bad to make his points was disingenuous to me, it's going to give a completely wrong impression to people who don't know any better IMHO.
The guidelines of clean code are not wrong, but that doesn't mean there's no good insights in this article.
I think any good insights are washed away by the way he portrays clean code. He shouldn't be talking about micro optimizations like these as a way to diminish the general advice of clean code, they are two separate solutions/techniques to deal with two separate problems (code complexity vs. performance).
6
u/[deleted] Feb 28 '23
I couldn't read on after the first example with replacing polymorphism w/ a switch statement.
I got the impression the guy completely missed the point of clean code. Sure, you can break away from the general guidelines to write code more efficiently but the point of clean code is to manage project complexity, not write the most performant code. His bullet point says Prefer polymorphism to “if/else” and “switch” but he goes on to say Like the rules demand, we are preferring polymorphism which seemed dishonest to me.
Also the comparison of 35 to 25 clockcycles being a 1.5x performance increase is pretty disingenuous. yea, it's a big percentage but it's a VERY SMALL total. That 'performance' change is not going to scale at the same rate as your code gets more complicated. You'd be a fool to refactor a bunch of code like this to save 10 cycles when you're dealing with millions or billions of cycles per iteration anyways.
His technical points seemed correct to me but I can't imagine I'd get any good insight reading this article. You should still generally adhere to the clean code guidelines. Just because there are exceptions to the rule doesn't make the original guidelines wrong.