nemequ / hedley

A C/C++ header to help move #ifdefs out of your code
https://nemequ.github.io/hedley/
Creative Commons Zero v1.0 Universal
774 stars 51 forks source link

Cray support for prediction #30

Closed nemequ closed 4 years ago

nemequ commented 4 years ago

I was looking at the Cray manual, and noticed that Cray supports __builtin_expect as well as a probability pragma which could be used to implement HEDLEY_PREDICT.

Shouldn't be hard, I'm just in the middle of something else right now and don't want to forget to come back and write the implementation.

nemequ commented 4 years ago

I took a closer look at this, and I think I spoke too soon about it being easy :(. Luckily they support __builtin_expect, so adding Cray to that list is trivial. 4f8bb0fead29a4e6afd8a7fb0c8d658375bb4d11 does that… copying the commit message which has a lot more information:

Cray also has a probability pragma, but I'm not sure how to make it work here. Unlike __builtin_expect, the pragma goes inside the condition, so instead of

if (__builtin_expect_with_probability(x, 1, 0.9)) {
  do_stuff();
}

Cray wants

if (x) {
  #pragma probability 0.9
  do_stuff();
}

To be honest, I think I prefer Cray's version. It's really nice for the common case (if blocks), though it does have some disadvantages… for one thing, putting it in a pragma means it's a bit of a pain to write a macro where one of the parameters is the probability since you have to go through a secondary macro like HEDLEY_STRINGIFY (e.g., _Pragma(HEDLEY_STRINGIFY(probability N))). It's also a bit ugly to use inside of statements, like when using __builtin_expect_with_probability in a ternary operator.

The biggest disadvantage, though, is that I can't see a good way to make Cray's version act more like __builtin_expect_with_probability so we can have one macro that works everywhere. The best I can think of is

#  define HEDLEY_PREDICT(expr, expected, prob) \
    (__extension__ ({ \
      const __typeof__(expr) hedley_expr_ = (expr); \
      if (hedley_expr_ == (expected)) { \
        _Pragma(HEDLEY_STRINGIFY(probability prob)) \
      } \
      hedley_expr_; \
    }))
#  define HEDLEY_PREDICT_TRUE(expr, prob) \
    (__extension__ ({ \
      const __typeof__(expr) hedley_expr_ = (expr); \
      if (!!hedley_expr_) { \
        _Pragma(HEDLEY_STRINGIFY(probability prob)) \
      } \
      hedley_expr_; \
    }))
#  define HEDLEY_PREDICT_FALSE(expr, prob) \
    (__extension__ ({ \
      const __typeof__(expr) hedley_expr_ = (expr); \
      if (!hedley_expr_) { \
        _Pragma(HEDLEY_STRINGIFY(probability prob)) \
      } \
      hedley_expr_; \
    }))

So for

if (HEDLEY_PREDICT(x, 1, 0.9)) {
  do_stuff();
}

you end up with something that looks vaguely like

if ((__extension__ ({
    const __typeof__(x) hedley_expr_ = (x);
    if (hedley_expr_ == 1) {
      #pragma probability 0.9
    }
    hedley_expr_;
  }))) {
    do_stuff();
}

Which it feels like Cray may be able to optimize, but without access to the compiler I can't be sure so it's probably safer to just rely on __builtin_expect, which we know Cray will be able to take advantage of.