ContentsThe library

What Makes Code Fast

Compute Both Answers and Throw One Away, Which Is Faster Than Deciding Which to Compute

Last timeGuessing Which Way You Will Go

An unpredictable branch can be turned into arithmetic that always runs. You pay for work you discard and you stop paying for mistaken guesses. Here is where the line falls.

Choosing without jumping

The previous lesson ended with a loop losing nine cycles an iteration to a

branch the processor could not guess. The remedy is to remove the thing being

guessed.

A processor can select between two values without any jump at all. The

comparison sets a flag, and a single instruction copies one of two registers

depending on that flag. No guess is made because no instruction stream was ever

in question: the same instructions execute in the same order regardless of the

data. The whole operation costs about one cycle, every time, with no variance.

FIG 1The same choice, with and without a branch
plaintext
with a branch:

    if a > b:
        m = a
    else:
        m = b

without one:

    flag = (a > b)
    m = select(flag, a, b)

the accumulate case, with a branch:

    if a[i] > t:
        total = total + a[i]

and without:

    flag = (a[i] > t)
    total = total + select(flag, a[i], 0)
Both versions put the larger of two values in the result. The first contains a jump the processor must predict. The second contains a comparison and a select, both of which always execute, and neither of which can be wrong.

The accumulate case at the bottom is the loop from the previous lesson. The

addition now happens on every iteration regardless, adding zero when the

element is below the threshold. The total comes out identical and no branch was

involved.

Paying for the path not taken

The transformation is not free and the cost is always the same shape. Both arms

of the conditional are computed on every visit, so the work done per iteration

is the work of the if-arm plus the work of the else-arm, rather than one of

them.

When each arm is a comparison and an addition, that is an extra cycle or two

and nobody notices. When one arm contains a multiplication chain, or a function

call, or a load from memory, the picture changes completely, because that cost

is now paid on every iteration including the ones that did not want it.

The lesson stops here

2 more paragraphs to go

You have read the opening. The rest of the argument, the problems that check whether it landed, and the lines worth keeping at the end all come with a plan.

The first lesson of every course in the library reads the whole way through, free, so you can see exactly what the rest of them are.

See the planThe contents

This is the reading half

Starting the course gives you your own copy of it. Every idea on every page has problems standing under it, marked with a reason rather than a tick, and any sentence you do not believe can be opened and argued with. None of that can happen on a page nobody owns.

The contents

The rest of this course

  1. 01Two Loops Doing Exactly the Same Arithmetic, One of Them Fifty Times Slower
  2. 02A Hundred Additions in the Time of Four, Unless Each One Waits for the Lastopening only
  3. 03Sorting the Array First Makes the Loop Six Times Faster Without Changing What It Computesopening only
  4. 04Compute Both Answers and Throw One Away, Which Is Faster Than Deciding Which to Computeyou are here
  5. 05One Number Tells You Which Half of the Toolbox to Openopening only
  6. 06The Same Add, Eight Numbers at a Time, If You Can Convince the Compiler It Is Safeopening only
  7. 07Most of the Time Is Going Somewhere That Does Not Appear Anywhere in Your Sourceopening only
  8. 08The Fastest Possible Version of a Tenth of Your Program Buys You Eleven Per Centopening only

Read alongside