代码之家  ›  专栏  ›  技术社区  ›  Ivan Ivanyuk

相关原子操作排序说明

  •  0
  • Ivan Ivanyuk  · 技术社区  · 3 年前

    我最近不得不研究记忆排序是如何工作的,我想把我的研究结果写给一些人 readme.md 为未来的我(当我忘记的时候)和其他开发者

    原子操作内存排序说明

    示例

    do_under_spinlock_explanation

    use std::sync::atomic::Ordering;
    
    fn do_under_spinlock_explanation() {
        let did_current_thread_acquire_lock;
        loop {
            // See [Explanation for atomic operations]
            // See [Explanation for conditional branch operations]
            did_current_thread_acquire_lock = is_locked.compare_exchange(
                0,
                1,
                // What will happen if we change Acquire to Relaxed
                // - read_and_write_memory_1-2_1-2 won't be able
                // to be reordered before
                // `if did_current_thread_acquire_lock` condition check
                // (since it will break single threaded execution),
                // which can't be run before computing `did_current_thread_acquire_lock`
                // (since using memory which
                // `if did_current_thread_acquire_lock` used(created) would
                // break single threaded logic)
                // - We won't pull actual memory, and will have stale memory
                // snapshot, so there will be race
                Acquire,
                Relaxed
            );
            if did_current_thread_acquire_lock {
                // See [Explanation for non-atomic operations]
                //
                // In which order these not atomic operations can happen?
                // - In random order, but operations on same memory can't
                // happen before previous operations on same memory,
                // since it will break single threaded logic, so
                // `read_and_write_memory_2_2` can happen before
                // `read_and_write_memory_1_1`
                // (if compiler decides that reordering will improve performance),
                // but `read_and_write_memory_2_2` can't happen before `read_and_write_memory_2_1`
                //
                // Where these not atomic operations can be reordered to top?
                // - Not before conditional check, since it would break logic
                // of single threaded execution
                //
                // Where these not atomic operations can be reordered to bottom?
                // - Not after Release, since it prevents reordering after it
                read_and_write_memory_1_1();
                read_and_write_memory_1_2();
                read_and_write_memory_2_1();
                read_and_write_memory_2_2();
                read_and_write_memory_1_and_2_1();
                read_and_write_memory_1_and_2_2();
    
                // What will happen if we change Release to Relaxed
                // - read_and_write_memory_1-2_1-2 won't be able
                // to be reordered after, going out of synchronized section
                // - We won't push actual memory changes, so other threads
                // will have stale memory even if they Acquire it,
                // so there will be race
                is_locked.swap(0, Release);
                break;
            }
        }
    }
    

    relaxed_counter_explanation

    fn relaxed_counter_explanation() {
        // See [Explanation for atomic operations]
        //
        // Where it can be reordered to top?
        // - Anywhere, even outside of method, until it meets operation
        // which also uses `count_1`, or until it meets other atomic
        // with Ordering::Acquire
        //
        // Where it can be reordered to bottom?
        // - Until it meets next operation which also uses `count_1`,
        // which is line
        // ```
        // if count_1_before_add == 0;
        // ```
        let count_1_before_add = count_1.fetch_add(1, Ordering::Relaxed);
        // See [Explanation for conditional branch operations]
        //
        // Where condition check can be reordered to top?
        // - Until it meets `let count_1_before_add = ...`,
        // since that line uses(creates) same memory, and reordering before
        // would break single threaded logic
        //
        // Where condition check can be reordered to bottom?
        // - Anywhere, even outside of method,
        // until it meets other atomic with Ordering::Release,
        // since memory it uses is local and is not used locally
        if count_1_before_add == 0 {
            // See [Explanation for atomic operations]
            // See [Explanation for conditional branch operations]
            //
            // Where this operation can be moved to top?
            // - Not before `if count_1_before_add == 0` check,
            // since it would break logic of single threaded execution
            //
            // Where condition check can be reordered to bottom?
            // - Anywhere, even outside of method,
            // until it meets usage of same atomic or
            // other atomic with Ordering::Release
            //
            // Can it be placed before/after `times_when_count_1_decreased_from_1_to_0.fetch_add`?
            // - Yes!
            times_when_count_1_increased_from_0_to_1.fetch_add(1, Ordering::Relaxed);
        }
    
        // See [Explanation for atomic operations]
        //
        // Where it can be reordered to top?
        // - Until it meets next operation which also uses `count_1`,
        // which is line
        // ```
        // let count_1_before_add = count_1.fetch_add(1, Ordering::Relaxed);
        // ```
        //
        // Where it can be reordered to bottom?
        // - Anywhere, even outside of method, until it meets operation
        // which also uses `count_1`, or until it meets other atomic
        // with Ordering::Release
        let count_1_before_sub = count_1.fetch_sub(1, Ordering:Relaxed);
        // See [Explanation for conditional branch operations]
        //
        // Where condition check can be reordered to top?
        // - Until it meets
        // ```
        // let count_1_before_sub = count_1.fetch_sub(1, Ordering:Relaxed);
        // ```,
        // since that line uses(creates) same memory, and reordering before
        // would break single threaded logic
        //
        // Where condition check can be reordered to bottom?
        // - Anywhere, even outside of method,
        // until it meets other atomic with Ordering::Release,
        // since memory it uses is local and is not used locally
        if count_1_before_sub == 1 {
            // See [Explanation for atomic operations]
            // See [Explanation for conditional branch operations]
            //
            // Where this operation can be moved to top?
            // - Not before `if count_1_before_sub == 1` check,
            // since it would break logic of single threaded execution
            //
            // Where condition check can be reordered to bottom?
            // - Anywhere, even outside of method,
            // until it meets usage of same atomic or
            // other atomic with Ordering::Release
            //
            // Can it be placed before/after `times_when_count_1_increased_from_0_to_1.fetch_add`?
            // - Yes!
            times_when_count_1_decreased_from_1_to_0.fetch_add(1, Ordering::Relaxed);
        }
    
        // Explanations for `count_2` are same as for `count_1`,
        // since it uses different memory and doesn't have not Relaxed atomics 
        let count_2_before_add = count_2.fetch_add(1, Ordering::Relaxed);
        if count_2_before_add == 0 {
            times_when_count_2_increased_from_0_to_1.fetch_add(1, Ordering::Relaxed);
        }
    
        let count_2_before_sub = count_2.fetch_sub(1, Ordering:Relaxed);
        if count_2_before_sub == 1 {
            times_when_count_2_decreased_from_1_to_0.fetch_add(1, Ordering::Relaxed);
        }
    }
    

    解释

    非原子操作说明

    在当前线程的范围内,非原子操作 可以被重新排序到上面/下面的任何位置, 但不是在对同一存储器进行操作之前/之后 在代码中的前/后,因为它会破坏单个 线程逻辑 (操作2不能在同一存储器上的1之前或3之后进行, 但是可以在对其他存储器的操作之前/之后进行), 而不是在当前线程中的Acquire/RequireRelease/SeqCst原子之前, 而不是在当前线程中的Release/AcquireRelease/SeqCst原子之后

    如果在当前线程中发生在获取之后, 当前线程中的获取发生在其他线程中的Release之后, 在其他线程中看到Release之前发生的实际内存更改

    如果在当前线程中不是在获取之后发生, 看不到实际内存,这会导致种族歧视

    如果在当前线程中发生在获取之后, 并且当前线程中的获取不发生在其他线程中的释放之后, 当前修改存储器的存储器看不到实际存储器, 在没有将在其他线程中的Release之前发生的改变的情况下, 这会引起种族歧视

    原子操作说明

    如果松弛,在当前线程的范围内,可以重新排序为 任何高于/低于的地方,但不在操作之前/之后 在代码之前/之后的相同原子(具有任何存储器排序)上, 而不是在当前线程中的其他原子的Acquire/AcqureRelease/SeqCst之前, 而不是在当前线程中其他原子的Release/AcquireRelease/SeqCst之后

    如果获取, 可以像Relaxed一样重新排序,但不能重新排序到底, 在其他线程中提取Release推送的内存更改

    如果释放, 可以像Relaxed一样重新排序,但不能重新排序到顶部, 推送可以由其他线程拉取的内存更改

    条件分支操作的说明

    条件(布尔值或开关情况)计算可以重新排序 到任何非原子(或原子,如果在计算中使用)的地方 操作可以重新排序为

    条件检查可以重新排序到任何位置 其中非原子操作可以被重新排序, 但不在条件计算之前 (由于条件检查发生在条件计算之后, 这是对同一存储器的操作, 由于它为switch case创建了带有布尔值或其他内容的内存, 将使用哪个条件检查)

    请注意,中的操作 if / switch 分支 不能在条件检查之前进行, 由于它会破坏单线程执行的顺序, 但只要它们不移动,就可以移动到底部,甚至可以从树枝外移动 满足对同一内存或原子版本的操作

    0 回复  |  直到 3 年前
    推荐文章