问题标签 [instruction-reordering]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
java - 对象访问是否可以在 Java 中使用该对象的最终字段访问重新排序?
下面的代码示例取自 JLS 17.5 “final Field Semantics”:
由于实例FinalFieldExample
是通过数据竞争发布的,f != null
检查是否有可能成功评估,但随后的f.x
取消引用会显示f
为null
?
换句话说,是否有可能获得NullPointerException
评论为“保证看到3”的在线?
c++ - 编译器是否允许围绕 `std::condition_variable::notify_one` 重新排序语句?
下面的代码是我编写的一个结构,它允许我将工作排队以在多个工作线程上运行,它会阻塞主线程,直到有新的工作线程可用。
我对由 lambda 执行的代码有两个问题jthread
:
mu
设置时是否需要锁定互斥锁threadActive[index] = false
?编译器是否允许在设置
cond.notify_one()
之前threadActive[index] = false
重新排序代码并执行?
我认为当且仅当允许编译器重新排序语句时,才有必要锁定互斥锁。这个对吗?
c++ - as-if 规则是否允许这种执行重新排序
众所周知,编译器或 CPU 可以根据需要重新排序执行,前提是它们遵循 as-if 规则。例如,如果我们有这样一段代码:
编译器或 CPU 可能会D = E + F
在C = A + B
. 我可以理解。
现在让我们谈谈另一个案例。
说我有两个线程a
和b
. 我想在执行时设置一些标记,以便我可以监控和的整个a
过程b
。
我的问题是:既然我们有 as-if 规则并且执行顺序可能会重新排序,是否意味着其中的消息q
不可靠?意思是真正的执行顺序是a - 1
, b - 1
,但其中可能有, ,b - 2
和? 如果可能发生这种情况,我应该如何设计或使用哪种技术来监控多线程进程?a - 2
q
a - 1
a - 2
b - 1
b - 2
c++ - 这种执行重新排序是否可能
众所周知,编译器或 CPU 可以根据需要重新排序执行,前提是它们遵循 as-if 规则。例如,如果我们有这样一段代码:
编译器或 CPU 可能会D = E + F
在C = A + B
. 我可以理解。
今天同事尝试用C++搭建一个日志库。他的想法是使用构造函数和析构函数在一些重载流函数的帮助下制作一些日志,例如operator<<()
.
基本上,他提供了这样一种课程:
现在,我是日志库的用户。我的同事告诉我,我可以使用以下库:
所以当第一行执行时,构造函数被调用,所以我可以在日志中得到一个“开始”。当函数返回时,log
将调用析构函数,所以我会end
在日志中得到一个。借助 object log
,我们可以清楚地找到函数的开始和函数的结束。
这听起来清晰而伟大。
但是,正如我在文章开头提到的那样,机器可能会根据需要进行一些重新排序。所以我现在想知道是否有可能log
调用的构造函数比我们想象的要晚和/或调用的析构函数log
比我们想象的要早,因此log
不能按我们预期的那样工作。我的意思是,代码func
确实如上所示,但是当它被编译或执行时,真正的顺序变成了:
顺便说一句,类中的流Log
被定向到其他地方,例如文件、共享内存或 TCP 套接字。
那我讲道理吗?或者这种重新排序永远不会发生?如果可能发生,是否有某种技术可以禁止这种重新排序,或者是否有某种技术可以提供一个可用的日志库来告诉我们任何函数的开始和结束?
事实上,我听说过一些 C++11 中的新技术,例如std::atomic
and std::atomic_thread_fence
. 据我了解,如果这种重新排序是可能的,我需要的可能是……栅栏?
我真的不知道这是否可能......
关于副作用/可观察的行为
据我了解,这是一种副作用/可观察到的行为:
为什么?因为 的值发生了A
变化。
现在,如果我这样编码怎么办:
所以顺序可能是:
- 的构造函数
log
A = B + C
- 的析构函数
log
但是,如果订单变为:
A = B + C
- 的构造函数
log
- 的析构函数
log
我认为那会很好。为什么?因为A
关于副作用/可观察行为的 的值没有被修改。无论我们采用哪种顺序,它的值总是B + C
。
我对吗?如果我是对的,我认为这意味着Log
不会按预期工作。
更新
A = B + C
有一个副作用,即 的值发生了A
变化,但这不是可观察到的行为。
c# - 我什么时候应该考虑内存屏障和指令重排序?
我试图用 C# 像这样实现彼得森锁
但是当我在2个线程中使用这个锁时,它不起作用,有人说我应该考虑指令重排序并使用内存屏障。A线,B线和C线是否像A->B->C,或A->C->B,或C->A->B或其他顺序重新排序?所以我把我的代码改成这样:
- 不知道这两行是不是都需要?
- 有什么规则可以帮助我判断哪一行会被重新排序,哪一行不会?
- 什么时候应该使用内存屏障?
rust - 具有互斥的编译器重新排序部分
由于 rust 将互斥锁应用为其数据的容器/所有者并且不使用像 C++ 这样的外部保护,我想知道 rust 编译器是否可能在以下伪代码中重新排序循环的内部部分(并且这样做, 使其无效..) 如果是这样,我该如何防止它?
dependencies - 数据依赖和控制依赖的区别
我正在努力理解数据依赖和控制依赖之间的区别。
所以我看到的一个例子是:
数据依赖,例如,指令使用由另一条指令创建的数据
控制相关性,例如,指令等待更新的 reg 进行比较
我很难理解的是,例如在 beq 我等待寄存器更新但我也依赖他的数据。
所以我不知道如何知道它们之间的区别。
benchmarking - Google 的 `DoNotOptimize()` 函数如何强制语句排序
我试图确切地了解谷歌DoNotOptimize()
应该如何工作。
为了完整起见,这里是它的定义(对于clang和非常量数据):
据我了解,我们可以在这样的代码中使用它:
为了确保基准保持在关键部分:
具体来说,我不明白的是为什么这保证(是吗?)run_bench()
没有移到上面start_time = time()
。
(有人在此评论中确切地问了这个问题,但是我不明白答案)。
据我了解,上面DoNotOptimze()
做了几件事:
- 它强制
value
堆栈,因为它是通过 C++ 引用传递的。你不能有一个指向寄存器的指针,所以它必须在内存中。 - 因为
value
现在在堆栈上,所以随后破坏内存(如在 asm 约束中所做的那样)将强制编译器假定value
调用DoNotOptimize(value)
. - (我不清楚
+r,m
约束是否相关。据我所知,这表明指针本身可能存储在寄存器或内存中,但指针值本身可能被读取和/或写入。)
这就是我变得模糊的地方。
如果start_time
还分配了堆栈,则内存破坏DoNotOptimize()
将意味着编译器必须假定DoNotOptimize()
可能读取start_time
. 因此语句的顺序只能是:
但是如果start_time
不存储在内存中,而是存储在寄存器中,那么破坏内存不会破坏start_time
,对吧?在这种情况下,所需的start_time = time()
and顺序DoNotOptimize(bench_inputs)
会丢失,编译器可以自由地执行以下操作:
显然我误解了一些东西。谁能帮忙解释一下?谢谢 :)
我想知道这是否是因为重新排序优化发生在寄存器分配之前,因此所有东西都被认为是当时堆栈分配的。但如果是这样的话,那DoNotOptimize()
将是多余的,ClobberMemory()
就足够了。
c# - 为什么使用 CancellationTokenSource.Cancel 的多线程代码需要较少的反重排序措施
我试图理解为什么共享CancellationTokenSource
变量在这里不受锁或内存屏障的保护。
我知道有一条经验法则,如果允许编译器优化,则可以使用局部变量读取和写入对共享(状态)变量的读取或写入进行重新排序。
这是CancellationTokenSource
文档中的一个示例。
这究竟是什么原因?Microsoft 是否暗示这种重新排序是安全的,因此不需要保护措施,或者根本不可能重新排序?
java - Java 指令重新排序和 CPU 内存重新排序
这是一个后续问题
有许多文章和博客提到了 Java 和 JVM 指令重新排序,这可能会导致用户操作出现反直觉的结果。
当我要求演示导致意外结果的 Java 指令重新排序时,有几条评论指出,更普遍的关注领域是内存重新排序,并且很难在 x86 CPU 上演示。
指令重新排序只是内存重新排序、编译器优化和内存模型等更大问题的一部分吗?这些问题真的是 Java 编译器和 JVM 独有的吗?它们是否特定于某些 CPU 类型?