我最近不得不研究记忆排序是如何工作的,我想把我的研究结果写给一些人
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
分支
不能在条件检查之前进行,
由于它会破坏单线程执行的顺序,
但只要它们不移动,就可以移动到底部,甚至可以从树枝外移动
满足对同一内存或原子版本的操作