In this code
fn do_something_under_lock(
is_locked: &AtomicBool,
) {
loop {
let lock_acquired = is_locked
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok();
if lock_acquired {
// Do something
// ...
is_locked.store(false, Ordering::Release);
break;
}
}
}
will moving let outside of loop, like
fn do_something_under_lock(
is_locked: &AtomicBool,
) {
let mut lock_acquired;
loop {
lock_acquired = is_locked
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok();
if lock_acquired {
// Do something
// ...
is_locked.store(false, Ordering::Release);
break;
}
}
}
give some performance benefit, will it change when Drop is called(according to https://doc.rust-lang.org/rust-by-example/trait/drop.html#drop
The Drop trait only has one method: drop, which is called automatically when an object goes out of scope. The main use of the Drop trait is to free the resources that the implementor instance owns.
and at first glance, scope of lock_acquired changed
), or will compiler produce same code anyway? What is the best way to check compiler output?
I expect compiler to understand that developer intent is the same and produce the same binary
In theory the first code allocates space on the stack for a new boolean each time and removes it at the end of the loop body. The second code allocates a new boolean once and removes it at the end of the function.
However, the compiler will almost certainly compile these to the same instructions, using the same location on the stack if it uses the stack, but more likely using a register for the boolean.
The first code is also easier for a human to read, since it is clear that the value of the boolean is only set for the duration of the loop body and does not affect subsequent iterations.
Of more concern for performance, this code will spin in a tight loop while another thread has the lock, which may affect the speed at which the other thread gets to unlock. I'd recommend using existing crates that provide locking (
std::syncorparking_lot).