r/AskComputerScience 13d ago

is clean code usually not fast?

to be specific i'm writing a cpu-based rasterizer. the maths are not difficult but i find a strange property: if i divide the procedure into some small functions, the code looks cleaner and is easier to maintain but a bit slower. on the contrary if i put everything into a single procedure, it looks stupid but fast. why is that? an example illustrating this

code 1:

if cross_product(x0,y0,x1,y1)>0 then zzz

(and i write a "cross_product" function separately)

code 2:

c=x0y1-y0x1

if c>0 then zzz

code 3:

if x0y1-y0x1>0 then zzz

if i write the entire algorithm in the style of "code 3", it runs the fastest. "code 1" is slowest

is it normal?

9 Upvotes

19 comments sorted by

View all comments

14

u/drfangor99 13d ago edited 13d ago

Function calls and variable assignments aren't free, both take time to complete. However, this is going to depend on the programming language you use. For example, C compilers are very good at compiling C code into really efficient assembly, so you can write code in whatever way makes sense to you and little things like this will be optimized away. If you're using an interpreted language like Python, then this code will be interpreted much more literally and it has the double whammy of the inherent slowness of the Python interpreter and its dynamic type system.

Having said all that, I would consider these micro-optimizations. It is an interesting observation that the function call is slower, but it is usually better to care about readability and larger optimizations first, then take care of these if you need to squeeze out every last bit of performance.

Edit: What I said about C doesn't apply if you don't have optimizations on (i.e., if you aren't compiling with an -O flag). But based on your pseudocode I assumed you aren't using C as the variables aren't typed.

2

u/dodexahedron 13d ago edited 13d ago

And, even then, in the absense of memory barriers, the CPU itself is still likely going to perform some amount of uop reordering, branch prediction, and other low level optimizations that you'll never see.

Plus, all modern CPUs are super-scalar, so even on one thread are highky likely to be performing multiple different uops in parallel, per clock cycle, so long as there's not a hard serial dependency between a given set of operations, and there are available execution units for those ops.

Modern Intel and AMD CPUs have 4-6 ALUs per core, for example, so can potentially issue that many arithmetic ops per clock cycle without you or the compiler doing a thing, and there's no way to change that. Memory barriers limit reordering, but not multi-issue (so long as the same ordering constraints are upheld). Mostly, though, they end up still doing a lot of it, but the cost of a misprediction is just higher if it wasn't already executing both branches in the first place.