把 `let` 移出循环值得吗?

huangapple go评论73阅读模式
英文:

Is it worth moving `let` out of loop?

问题

在这段代码中
```rust
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;
        }
    }
}

let移到循环外,像这样

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;
        }
    }
}

会带来一些性能上的好处,当调用Drop时会发生变化吗(根据https://doc.rust-lang.org/rust-by-example/trait/drop.html#drop
> Drop特性只有一个方法:drop,在对象超出作用域时会自动调用。Drop特性的主要用途是释放实现者实例拥有的资源。

乍一看,lock_acquired的作用域发生了变化
),还是编译器会生成相同的代码?检查编译器输出的最佳方法是什么?

我期望编译器能理解开发者的意图是相同的,并生成相同的二进制代码。


<details>
<summary>英文:</summary>

In this code
```rust
fn do_something_under_lock(
    is_locked: &amp;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: &amp;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

答案1

得分: 4

第一段代码在理论上每次都为一个新的布尔值在堆栈上分配空间,并在循环体结束时将其删除。第二段代码在函数结束时分配一次新的布尔值然后将其删除。

然而,编译器几乎肯定会将它们编译成相同的指令,如果使用堆栈,则使用相同的堆栈位置,但更可能使用寄存器来存储布尔值。

第一段代码也更容易阅读,因为清楚地表明布尔值的值仅在循环体的持续时间内设置,不会影响后续的迭代。

在性能方面更值得关注的是,此代码将在另一个线程持有锁时自旋在一个紧密的循环中,这可能会影响另一个线程解锁的速度。我建议使用现有的提供锁定功能的包(std::syncparking_lot)。

英文:

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::sync or parking_lot).

huangapple
  • 本文由 发表于 2023年7月3日 13:24:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76602017.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定