简体   繁体   中英

Can using `__builtin_expect` affect program semantics?

GCC (and Clang also), provide this __builtin_expect for human-assisted branch prediction, as explained here . Informally, people explain its semantics in the following way: "compiler just processes the indicated branch unconditionally, and if the condition should turn out to be different than indicated, an expensive roll-back takes place".

But if I have a piece of code as the following:

if (__builtin_expect(p != 0, 1)) // line 1
  p->access_object();            // line 2

If I treated the informal explanation above literally, the compiler can just execute line 2 without waiting for the computation of the condition in line 1, and therefore cause an undefined behavior (null pointer dereference), should the pointer happen to be null.

My question is, if I use __builtin_expect do I still get the guarantee, that my defensive checks work? And if so, do I get any run-time benefit if I use __builtin_expect in defensive checks as the above one?

(Note: my goal for using __builtin_expect like this is to get the maximum performance for the cases where p is non-null, at the expense of slowing down (even by orders of magnitude) the cases where p is null; Even if the latter case appears quite often.)

No, builtin_expect will not affect the semantics of a race-free program.

In particular, the compiler must not emit code which would execute the body of the if block if that code has side effects which cannot be undone. The code must be "as if" builtin_expect were not used, apart from performance.

For your specific example:

if (__builtin_expect(p != 0, 1)) // line 1
  p->access_object();            // line 2

p cannot be dereferenced if it is null. So what is the point of builtin_expect in this case? The most it can do is tell the compiler " p is probably not null, so access_object() will probably be called." If the definition of access_object() is inline , the compiler will probably try to inline it, whereas if you said " p is probably null," the compiler might decide it was better not to inline the code for access_object() at this call site, because it is unlikely to be used.

In fact, this leads to a non-intuitive use of builtin_expect in practice: you can use to mean "this code is the slow path," regardless of how "likely" it is. As a trivial example, a server program might do this:

if (__builtin_expect(is_allowed(user, request), 1))
    process(request);
else
    reject(request);

Even if we find that 50% of requests are illegitimate and will be rejected, we might still decide to mark the "happy path" as likely taken, because we do not care about slowing down rejections.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM