标签: instruction-reordering

如何演示Java指令重新排序问题?

使用Java指令重新排序时,JVM会在编译时或运行时更改代码的执行顺序,这可能导致无关的语句无序执行.

所以我的问题是:

有人可以提供示例Java程序/片段,它可靠地显示指令重新排序问题,这也不是由其他同步问题(例如缓存/可见性或非原子r/w)引起的,就像我在这样的演示中尝试失败一样在我之前的问题中)

为了强调,我不是在寻找理论重新排序问题的例子.我正在寻找的是通过查看正在运行的程序的错误或意外结果来实际演示它们的方法.

除了一个错误的行为示例,只显示在一个简单程序的程序集中发生的实际重新排序也可能是好的.

java multithreading compiler-optimization instruction-reordering

37
推荐指数
2
解决办法
837
查看次数

C++ 中原子加载存储的优化

我读过std::memory_orderC++ 并部分理解。但我对此仍然存有一些疑问。

  1. 解释std::memory_order_acquire说,在此加载之前,当前线程中的任何读取或写入都不能重新排序。这是否意味着编译器和 CPU 不允许移动语句下方acquire或上方的任何指令?
auto y = x.load(std::memory_order_acquire);
z = a;  // is it leagal to execute loading of shared `b` above acquire? (I feel no)
b = 2;  // is it leagal to execute storing of shared `a` above acquire? (I feel yes)
Run Code Online (Sandbox Code Playgroud)

我可以推理出为什么在 之前执行加载是非法的acquire。但为什么这对商店来说是非法的呢?

  1. 跳过atomic对象中无用的加载或存储是否违法?因为它们不是volatile,而且据我所知,只有 volatile 有这个要求。
auto y = x.load(std::memory_order_acquire);  // `y` is never used
return;
Run Code Online (Sandbox Code Playgroud)

relaxed即使使用内存顺序,这种优化也不会发生。

  1. 编译器是否允许移动语句上方acquire或下方的指令?
z …
Run Code Online (Sandbox Code Playgroud)

c++ atomic compiler-optimization memory-barriers instruction-reordering

10
推荐指数
1
解决办法
396
查看次数

排序前修改顺序一致性

来自http://en.cppreference.com

宽松排序标记 std::memory_order_relaxed 的原子操作不是同步操作,它们不排序内存。它们只保证原子性和修改顺序的一致性。例如,x 和 y 最初为零,

// Thread 1:
r1 = y.load(memory_order_relaxed); // A
x.store(r1, memory_order_relaxed); // B
// Thread 2:
r2 = x.load(memory_order_relaxed); // C 
y.store(42, memory_order_relaxed); // D
Run Code Online (Sandbox Code Playgroud)

允许产生 r1 == r2 == 42 因为,虽然 A 在 B 之前被排序并且 C 在 D 之前被排序,但是没有什么可以阻止 D 在 y 的修改顺序中出现在 A 之前,并且 B 在修改中出现在 C 之前x 的顺序。

问题:是什么赋予上述代码属性A 在 B 之前排序并且C 在 D 之前排序

编辑:

int A, B;

void foo()
{
    A = …
Run Code Online (Sandbox Code Playgroud)

c++ multithreading memory-model stdatomic instruction-reordering

6
推荐指数
1
解决办法
316
查看次数

Google 的“DoNotOptimize()”函数如何强制语句排序

我试图准确理解谷歌的DoNotOptimize()运作方式。

为了完整起见,以下是它的定义(对于 clang 和非常量数据):

template <class Tp>
inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {
  asm volatile("" : "+r,m"(value) : : "memory");
}
Run Code Online (Sandbox Code Playgroud)

据我了解,我们可以在如下代码中使用它:

start_time = time();
bench_output = run_bench(bench_inputs);
result = time() - start_time;
Run Code Online (Sandbox Code Playgroud)

为了确保基准保持在关键部分:

start_time = time();
DoNotOptimize(bench_inputs);
bench_output = run_bench(bench_inputs);
DoNotOptimise(bench_output);
result = time() - start_time;
Run Code Online (Sandbox Code Playgroud)

具体来说,我不明白的是为什么这保证(是吗?)run_bench()不会移到上面start_time = time()

(有人在这个评论中问过这个问题,但我不明白答案)。

据我了解,上面DoNotOptimze()做了几件事:

  • 它强制value进入堆栈,因为它是通过 C++ 引用传递的。您不能有指向寄存器的指针,因此它必须位于内存中。
  • 因为value现在位于堆栈上,因此随后破坏内存(如在 asm 约束中完成的那样)将迫使编译器假设value通过调用DoNotOptimize(value).
  • (我不清楚+r,m …

benchmarking inline-assembly compiler-optimization google-benchmark instruction-reordering

5
推荐指数
1
解决办法
1312
查看次数

在具有相同顺序的原子加载/存储之前使用 std::atomic_thread_fence 总是多余的吗?

鉴于:

std::atomic<uint64_t> b;

void f()
{
    std::atomic_thread_fence(std::memory_order::memory_order_acquire);

    uint64_t a = b.load(std::memory_order::memory_order_acquire);

    // code using a...
}
Run Code Online (Sandbox Code Playgroud)

去掉这个调用会有std::atomic_thread_fence什么影响吗?如果有的话有一个简洁的例子吗?请记住,其他函数可能会存储/加载b并调用f.

c++ multithreading atomic lockless instruction-reordering

5
推荐指数
1
解决办法
455
查看次数

memory_order_relaxed 是否尊重同一线程内的数据依赖性?

鉴于:

std::atomic<uint64_t> x;

uint64_t f()
{
    x.store(20, std::memory_order::memory_order_relaxed);
    x.store(10, std::memory_order::memory_order_relaxed);
    return x.load(std::memory_order::memory_order_relaxed);
}
Run Code Online (Sandbox Code Playgroud)

假设只有一个线程写入,是否有可能f返回除 之外的值?对于非原子变量来说显然不是这样,但我不知道relaxed是否如此宽松以至于它会忽略同一线程中的数据依赖关系?10x

c++ multithreading atomic lockless instruction-reordering

4
推荐指数
1
解决办法
291
查看次数

内存模型,加载获取语义实际上是如何工作的?

来自关于内存重新排序的非常好的论文文章

Q1:我知道缓存一致性、存储缓冲区和失效队列是内存重新排序的根本原因吗?

存储释放是很好理解的,必须等待所有加载和存储完成才能将标志设置为 true。

关于加载获取,原子加载的典型用途是等待标志。假设我们有 2 个线程:

int x = 0;
std::atomic<bool> ready_flag = false;
Run Code Online (Sandbox Code Playgroud)
// thread-1
if(ready_flag.load(std::memory_order_relaxed))
{
    // (1)
    // load x here
}
// (2)
// load x here
Run Code Online (Sandbox Code Playgroud)
// thread-2
x = 100;
ready_flag.store(true, std::memory_order_release);
Run Code Online (Sandbox Code Playgroud)

编辑:在线程 1 中,它应该是一个 while 循环,但我复制了上面文章中的逻辑。因此,假设内存重新排序及时发生。

Q2 : 因为(1)和(2)取决于if条件,CPU必须等待ready_flag,这是否意味着write-release就足够了?在这种情况下,内存重新排序是如何发生的?

Q3:显然我们有load-acquire,所以我猜 mem-reorder 是可能的,那么我们应该把栅栏放在哪里,(1)还是(2)?

c++ memory-model stdatomic instruction-reordering

3
推荐指数
1
解决办法
782
查看次数

Java指令重排序和CPU内存重排序

这是一个后续问题

如何演示Java指令重排序问题?

有很多文章和博客提到 Java 和 JVM 指令重新排序,这可能会导致用户操作中出现反直觉的结果。

当我要求演示 Java 指令重新排序导致意外结果时,有几条评论说,更普遍的关注领域是内存重新排序,并且很难在 x86 CPU 上进行演示。

指令重新排序只是内存重新排序、编译器优化和内存模型等更大问题的一部分吗?这些问题真的是 Java 编译器和 JVM 特有的吗?它们是否特定于某些 CPU 类型?

java cpu-architecture memory-barriers instruction-reordering

1
推荐指数
1
解决办法
515
查看次数