1

I have the code:

const DEBUG_MODE: bool = false;

fn main() {
    if DEBUG_MODE {
        println!("In debug mode!");
    }
    println!("Normal code");
}

Will the Rust compiler remove the branching so it is the same as:

fn main() {
    println!("Normal code");
}

Will there be any differences in the compiled assembly output?

In the case where DEBUG_MODE is true, will it inline the branch or will it actually do the check in assembly?

If we have a function like this:

fn debug_fn() {
    if !DEBUG_MODE {
        return;
    }
    println!("Some debug function");
}

If DEBUG_MODE is false, will all the calls to it be optimized out or will it still have some form of overhead?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Marko Borković
  • 1,884
  • 1
  • 7
  • 22
  • 4
    yes: https://blog.rust-lang.org/inside-rust/2019/12/02/const-prop-on-by-default.html – coredump Aug 04 '21 at 10:13
  • 5
    In any case, I'd recommend using the #[cfg] attribute: https://doc.rust-lang.org/rust-by-example/attribute/cfg.html – Krish Aug 04 '21 at 10:16
  • 3
    Even without MIR-level const propagation, this is the kind of thing LLVM would happily would have done. MIR-level const propagation was mostly about compile-time performance, not runtime performance. – mcarton Aug 04 '21 at 10:17
  • 4
    Note that using a `DEBUG` constant like that isn't the idiomatic way. `#[cfg]` is. Using a constant forces that the code must compile in both debug and release mode even if it's going to be unused in release mode. – mcarton Aug 04 '21 at 10:18
  • 2
    https://godbolt.org/z/TbY8Gr9fz – Ömer Erden Aug 04 '21 at 10:19

1 Answers1

4

Yes, it will. As a simpler example:

const DEBUG_MODE: bool = false;

pub fn example() {
    if DEBUG_MODE {
        call_me();
    }
    call_me();
}

#[inline(never)]
fn call_me() {
    println!("called");
}

When compiled in release mode, Rust 1.54 produces this x86_64 assembly for example when DEBUG_MODE is false:

playground::example:
    jmp playground::call_me

When DEBUG_MODE is true:

playground::example:
    pushq   %rax
    callq   playground::call_me
    popq    %rax
    jmp playground::call_me

As mentioned in the comments, it's more idiomatic to use cfg attributes for conditional compilation as it can be handled at a earlier level in the compiler. This means you can have some types of invalid code present. For example:

pub fn example() {
    #[cfg(some_debug_mode)]
    {
        oops_i_never_defined_this_function();
        call_me();
    }
    call_me();
}

Less commonly used, but still valuable, is the cfg! macro. This resolves to a true or a false and you rely on the same dead code elimination as the original case:

pub fn example() {
    if cfg!(some_debug_mode) {
        call_me();
    }
    call_me();
}

See also:

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Any other see also : https://stackoverflow.com/questions/63858774/different-versions-of-the-same-function-with-conditional-compilation-in-rust – Ömer Erden Aug 04 '21 at 19:30