6

I want to put some performance impacting function calls behind a feature gate in my code. If the feature isn't enabled, I was thinking of just having an empty implementation of that function implemented instead. That way, hopefully, the Rust complier can completely remove that from the function.

Something like this:

// Included if feature is enabled
fn foo() {
    // ...
}

// Included if the feature is disabled
fn foo() {}

// Performance critical code
for i in 1..1000000000 {
    // ...
    foo();
}

Would the call to foo() get optimized away if it is empty?

2 Answers 2

11

Just try it in the amazing Compiler Explorer :)

The result assembly for your example is:

example::main:
    push    rbp
    mov     rbp, rsp
    mov     eax, 1
.LBB0_1:
    xor     ecx, ecx
    cmp     eax, 1000000000
    setl    cl
    add     ecx, eax
    cmp     eax, 1000000000
    mov     eax, ecx
    jl      .LBB0_1
    pop     rbp
    ret

As you can see there is no call instruction and foo() isn't called at all. However, you might wonder why the loop isn't removed, as it doesn't have an effect on the outside world. I can just assume that sometimes those loops are in fact used to waste time in some sense. If you decrease the counter to 100, the loop is completely removed.

Anyway: Yes, the optimizer will remove empty functions!

Sign up to request clarification or add additional context in comments.

8 Comments

I was wondering about why the loop itself isn't removed, thanks for mentioning it; maybe someone can explain this behavior.
This behavior with the loop is really weird (especially the 100 threshold). When using Clang those loops are fully removed, so I would assume LLVM is capable to do it with Rust too, yet I can't seem to trigger it :x
@MatthieuM. The loop is optimized away with let mut i = 1_000_000_000; while i > 0 {foo(); i -= 1;}
You could pass -C remark=all to see optimization remarks. The loop failed to optimize because "could not determine number of loop iterations" @paholg @MatthieuM
Just filed an issue at github.com/rust-lang/rust/issues/41097 about the loop optimization for record.
|
1

According to my check with release mode on current stable Rust, the following code:

fn foo() {}


fn main() {
    for _ in 1..1000000000 {
        foo();
    }

    println!(); // a side effect so that the whole program is not optimized away
}

Compiles to the same assembly as if the loop was empty:

for _ in 1..1000000000 {}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.