Live data from Hacker News

Branch predictor: How many “if”s are too many?

blog.cloudflare.com

31–40 of 109 posts

Re: Branch predictor: How many “if”s are too many?

#31
post #2

I'm wondering how you guys would optimize that code ? My naive approach would be something like this: const int numCountryIndicies = however many there are..; const char* countries = "A1\0A2\0..."; return (cc < numCountryIndicies)?countries[cc*2]:"UNKNOWN";

> I'm wondering how you guys would optimize that code ?

Run it on infinite multiverse, construct a mechanism to destroy the Universe every time branch predictor did not predict every single conditional correctly.

Re: Branch predictor: How many “if”s are too many?

#32
post #31
post #2

I'm wondering how you guys would optimize that code ? My naive approach would be something like this: const int numCountryIndicies = however many there are..; const char* countries = "A1\0A2\0..."; return (cc < numCountryIndicies)?countries[cc*2]:"UNKNOWN";

> I'm wondering how you guys would optimize that code ? Run it on infinite multiverse, construct a mechanism to destroy the Universe every time branch predictor did not predict every single conditional correctly.

won't we need quantum computing for that first?

Re: Branch predictor: How many “if”s are too many?

#33
Great article, just to add one thing: conditional branches can seriously mess with the compiler's ability to optimize, especially the ability to auto-vectorize. So even if a never-taken branch is more or less free when the CPU executes it, just having it there might have resulted in much less efficient codegen. Just another thing to keep in mind with this issue.

Re: Branch predictor: How many “if”s are too many?

#34
post #21

This is a pretty amazing analysis that answers questions we probably all had as newbie programmers before realizing that code readability mattered more than efficiency 99.9% of the time.

I agree of course but I'd argue that an array lookup would be more readable and more maintainable than this `if` soup in the `getCountry` example code. One added benefit for instance is that the language will never allow you to declare two values for the same index, whereas you could very easily mess up a copy/paste and forget to update the condition.

More generally I don't think we should consider that readability and efficiency are at opposing ends of the same axis. With a bit of care and good tools it's often possible to get the best of both worlds, or close enough.

Re: Branch predictor: How many “if”s are too many?

#35
post #7
post #2

I'm wondering how you guys would optimize that code ? My naive approach would be something like this: const int numCountryIndicies = however many there are..; const char* countries = "A1\0A2\0..."; return (cc < numCountryIndicies)?countries[cc*2]:"UNKNOWN";

You have an off-by-one error. The strings are 3 bytes long. I'd write something similar, more or less; Probably the following, not for optimization, but more as a matter of style: const char countries[][3] = { "A1", "A0", // [...] }; // [...] int total_countries = (int) (sizeof(countries) / sizeof(countries[0])); return cc No need to hard code the length, and the cast is guaranteed to be within the bounds of an int o…

I think that's the best solution (the only potential optimization I can think of would be to add a padding byte after every country to force 32bit alignment which would make the offset computation faster on some architectures, although probably not on x86).

Stylistically-wise I think the best solution would be to write the array like:

    const char countries[][3] = {
        [0] = "A1",
        [1] = "A0",
    };
This way the codes are explicit when you read the code and it makes editing the array a little easier. Unfortunately gcc only warns if you set the same index twice with -Wextra, it remains silent with -Wall.

Re: Branch predictor: How many “if”s are too many?

#37

The “if (debug) log” example in the article is likely compiled to an always taken conditional jump past the logging call. And therefore not quite free. Nitpicking of course, I love such in-depth articles.

You can easily verify this in your specific code base but I believe the compiler prefers not-taken jumps for paths it predicts to be less frequently taken. You can provide hints that the path is a cold one to ensure the compiler uses the most efficient conditional branch layout. On some architectures the compiler will also provide this hint to the CPU (with a prefix, the instruction it selects, or a flag bit in the i…

C++ 20 introduces standard likely/unlikely attributes: https://en.cppreference.com/w/cpp/language/attributes/likely

Re: Branch predictor: How many “if”s are too many?

#38

Unrelated to the actual topic, but I believe there is a copy-paste bug in the first sample: const char *getCountry(int cc) { if(cc == 1) return "A1"; if(cc == 2) return "A2"; if(cc == 3) return "O1"; if(cc == 4) return "AD"; if(cc == 5) return "AE"; if(cc == 6) return "AF"; if(cc == 7) return "AG"; if(cc == 1) return "AI"; ... if(cc == 252) return "YT"; if(cc == 253) return "ZA"; if(cc == 254) return "ZM"; if(cc == 2…

Wouldn't the compiler optimize this to an array lookup? static const char* countries[257]; Or even: static const char countrycodes[514]; return countrycodes[i * 2];

    static const char countrycodes[514];
    return countrycodes[i * 2];
Err... each of the strings is 3 bytes long.

Re: Branch predictor: How many “if”s are too many?

#39
> This code is in a performance critical loop and it looks like a waste - we never run with the "debug" flag enabled[1]. 1. One historical solution to this specific 'if debug' problem is called "runtime nop'ing".

You know what another historical solution is called? Use a compile-time constant and let the damn optimizer optimize. (Virtually) no modern, widely used compiler would fail to optimize out `if (0) { ... }`.

Also, why does M1 performance matter to Cloudflare's software? I kinda doubt their servers are on Macbooks.

Re: Branch predictor: How many “if”s are too many?

#40
post #35
post #7

Earlier quoted context omitted.

You have an off-by-one error. The strings are 3 bytes long. I'd write something similar, more or less; Probably the following, not for optimization, but more as a matter of style: const char countries[][3] = { "A1", "A0", // [...] }; // [...] int total_countries = (int) (sizeof(countries) / sizeof(countries[0])); return cc No need to hard code the length, and the cast is guaranteed to be within the bounds of an int o…

I think that's the best solution (the only potential optimization I can think of would be to add a padding byte after every country to force 32bit alignment which would make the offset computation faster on some architectures, although probably not on x86). Stylistically-wise I think the best solution would be to write the array like: const char countries[][3] = { [0] = "A1", [1] = "A0", }; This way the codes are exp…

> This way the codes are explicit when you read the code

Good call. It might have caught the off-by-one mistake I made (the codes start from 1 and not 0 in the blog post). Maybe even switch to using an enum as our index instead of an int.

Post reply on HN