1

As a rookie programmer, I've only recently been digging into benchmarking, and have been comparing various ways of accomplishing the same task in terms of speed.

Most of the time, the result is either that the difference in speed is negligible or that it is so huge that I'm definitely going to use the faster option.

That leaves the cases in the middle. If I understand correctly from reading similar questions and answers, the consensus of software engineers is that any optimizations that clobber the readability of your code or make you expend valuable time should be left until you've run into a performance issue and identified what code is the bottleneck. That seems entirely reasonable to me.

However, what is "readable" and what is not? I have a very concrete example in mind. Take a look at this benchmark in JavaScript: https://jsperf.com/cond-vs-mult. It compares the use of implicit conditioning through multiplication with a boolean value with an explicit conditional by using the ternary operator ?. Now, it costs me no time or effort to simply write the faster version in all cases, so the only concern in my mind is readability.


My opinion after reading comments & answers

I don't agree with the duplication vote. I understand how this is remarkably similar to the linked question, but this was mainly about the readability of an implied conditional. Can you answer the linked question with an answer such as "the ternary operator is much easier to read than addition by a product with a boolean value"? No, but you can this one.

There could - but has not yet been - an underlying answer to both questions. This would have to invoke the idea of signaling, as done by keywords such as if or nearly universal functions such as strlen. Straying away from any such universal methods of doing something towards a language-specific or even implementation-specific method is bound to be less readable, thus constituting a micro-optimization, on which the consensus is clear. However, as it stands, the answers have only concluded the ternary operator is more readable and have thus have been unique to this question. The underlying discussion about micro-optimization, as I tried to make clear by mentioning this in my question, was not the main topic of the question. I will adjust the wording of the question to highlight this. So I really don't consider my question a duplication to the linked one.

RalphD
  • 21

4 Answers4

4

Sweeping the dust under the rug

When you're dealing with micro-optimizations in order to improve your program speed (rewriting O(1) operations to outperform their traditional counterparts), there is something else that seriously needs to be addressed about your program more often than not. You can't fix up a house about to collapse in on itself by sweeping the dust under the rug. In other words, if your program is fast already, micro-optimizations can improve it further, but to no great benefit and if your program is slow, micro-optimizations ain't the problem. At the very least, you shouldn't be able to justify making your program less readable at the expense of micro-optimizations because of the little benefit you stand to gain by doing it.

Except...

Except what? You just said you shouldn't be able to justify making micro-optimizations. Yes, while this is true, there are edge cases when the micro-optimizations such as when your little insignificant O(1) operation is wrapped in an unavoidable nested loop being called at every draw frame. If you do the math, for something that's potentially being called 100,000 times a second, that makes that little O(1) operation that took 1 microsecond suddenly take a tenth of a second instead. Perform five operations like this and your frame rate dropped from fluid 60 frames per second to only 2.

Conclusion

Therefore, if I see you optimizing speed-sensitive portions of your code, I'll try not to judge too harshly. Though it would be helpful to remember that you can always comment on your code either with the more readable version of that line or a brief explanation of what it accomplishes. There are also other ways speed up your code such as using threads, if there is work that can be accomplished in parallel. Just don't perform micro-optimizations unless you can genuinely justify such an optimization in terms of time saved in your program that runs it.

Neil
  • 22,848
3

I believe you have answered your own question. You state that it is reasonable to prefer readability over performance unless there is a performance problem. You also (implicitly) state that the faster method in your example is faster and less readable. Therefore, only use the faster, less-readable method if performance is an issue. If it isn't, slow(er)1 and readable is the way to go.


1 Let's be honest. The difference here is pretty negligible, especially in day to day code. Unless you are processing a ton of things, it won't make a difference. And if you are, well then performance probably matters and therefore using the faster method is justified.

Becuzz
  • 4,855
1

However, what is "readable" and what is not?

That's very subjective. Readability is not a binary trait, it's a spectrum. We can't conclusively call something (un)readable without also defining a frame of reference.

I agree that bool1 ? num1 : num2 is more readable than bool1 * num1 + !bool1 * num2. It's much easier to deduce the intention of the code when you see a ternary operator, compared to a contrived mathematical equation which ends up working exactly like an if statement.

I agree that developers should strive to keep their code as readable as possible. However, this does not mean that you should therefore use the readable version over the performant one.

Why do we avoid unreadable code?

Because we can't easily deduce the intention of the code.

How can we avoid unreadable code?

There are two different approaches here:

1. Change the code

This is often the best solution, because developers first try to "quick assess" the code, i.e. try to understand the intention from its basic structure.
E.g. When glossing over code, I'm able to spot an if/else block long before I've actually read which values are being evaluated and returned.

This applies not only to code structure, but also the operators that are being used. Like I said before, I agree that bool1 ? num1 : num2 is more readable than bool1 * num1 + !bool1 * num2, because the ternary operator quickly reveals that this code is trying to function like an if/else block.

However, if there are meaningful performance differences, where a more readable variant is notably less performant than its less readable counterpart, we should consider option 2:

2. Make the existing code more readable but functionally unchanged.

This can be done through renaming variables, e.g. turning c = b * a into surfaceArea = width * height or totalPrice = unitCost * unitQuantity. This is the same operation, it cannot possible be less performant than its more terse counterpart, but it's still considerably more readable for a developer.

But another way to do is this through commenting. Comments are a great tool here, because they allow you to add information for a developer without impacting the flow/performance of the code at runtime.

res = bool1 * num1 + !bool1 * num2;
    //this is the functional equivalent of "bool1 ? num1 : num2"
    //but its performance is significantly higher

Problem solved! The code is focusing on performance, but the comments enhance the readability of an otherwise fairly unreadable code snippet.

Now we have the best of both worlds.


When I make an improvement to my code based on something I've found online, I'm also likely to add the URL:

res = bool1 * num1 + !bool1 * num2;
    //this is the functional equivalent of "bool1 ? num1 : num2"
    //but its performance is significantly higher
    //See: https://jsperf.com/cond-vs-mult

There are a fair amount of MSDN/StackOverflow links in my code (or references to e.g. the functional analysis), simply because it's the easiest way to provide information without having to write it all in comments.

Flater
  • 58,824
0

Your question isn't really going to have a concrete answer, because "readability" is based on an individual style. For me personally, I would use the ternary in the instance you highlighted above merely because bool1 is a boolean and I like using it in a true/false test vs multiplying it. if the variable name was not as descript as you made it in your example, say a lazy programmer just called it "a", then I would assume I was multiplying two numbers and would have to look further in code to see that it in fact is a boolean. But that's just me.

Shaggy13spe
  • 109
  • 4