Hacker News new | ask | show | jobs
by krupan 980 days ago
"Programmers often put in speed hacks based on performance knowledge which is outdated or inapplicable to the target platform. Separating cases lets the programmer supply their high-level knowledge about what values are likely to be encountered, which the compiler can optimize to the platform based on its low-level information."

But if our performance knowledge is outdated, how do we know which cases to separate the code into? Using the toy example here, how do we know that multiplying by 2 is a special case we should add a branch for? What about multiplying by 3 or 4 or 5? It seems like you still need to use up-to-date performance knowledge for this technique to work, and in that case just write the left shift into your code so that when this code gets read later it will be more clear why it's there.

3 comments

Reminds me of a point in the interview with Donald Knuth in Coders at Work where he criticized overuse of abstraction in code, and said that he thinks it's more important for code to be easy to read and edit than for it to be easy to reuse.

He didn't say it explicitly, but the implication that I took was that things that are meant, in essence, to make code more configurable can sometimes (often?) do more harm than good. (SOLID, I'm looking at you.) Using them successfully often requires oracular knowledge about how the code might evolve in the future.

I've found that doing my job got a lot easier after I started following this idea. The code might be more repetitive and boilerplate-y, but there's actually less of it, so it's still easier to read, understand, and maintain.

That. YAGNI >> DRY.

The DRY should be obvious when to refactor, not because of some compulsive behavior.

And then once you need more than 2 branches, please break the DRY that tend to stay forever.

I really like the way functional programmers tend to think about factoring code. There's more of a tendency to think in terms of creating algebras. That forces you to think a lot more carefully about what abstractions you're producing, why, and how they relate to your domain model. And to be more cautious about it.

DRY, by comparison, is an almost offensively crude heuristic.

> how do you know which cases to separate the code into?

It takes practice. For this you really just have to read the assembly, and profile the code. Then try separating it one way and see if it helps, and if not then another. In my experience, it takes many tries to update your perf knowledge, and optimizations sometimes don’t work even when your knowledge is current. All this is why the article noted people should be “Giving the compiler an opportunity to do something is useful, but measure carefully before forcing it.”

>But if our performance knowledge is outdated, how do we know which cases to separate the code into?

Case separation is less of a commitment than deciding on the specific optimization yourself. You know empirically which cases are used in your application and can separate those to see if there's a benefit without any manual optimization knowledge, like I did in the image scaling example, without having much of a theory in mind as to why it might be faster.