在 C++ 和 Java 内存模型的上下文中,是否存在针对单线程程序的唯一 legal/valid 执行?
In the context of C++ and Java memory models, is there a unique legal/valid execution for a single thread program?
问题针对 C++ 或 Java 内存模型的主题,它定义了允许程序展示的行为。查看内存模型的一种简单方法是将其视为"filter",它定义了一组规则来关闭具有非法行为的执行(动作痕迹),其余的执行集是合法的。
问题是:对于单线程程序,给定一个固定的初始条件(如输入参数,变量初值),不与外部进程交互,是否存在唯一合法的执行(即只有一次执行满足内存模型)?
跟进问题:如果合法执行不止一次,是什么导致了非确定性?
备注:对于 C++,让我们将 sequenced-before 顺序视为总顺序。
编辑:正如 juanchopanza 在评论中所建议的,动态分配地址是单线程程序不确定性的来源之一。
在C++
中有多个路径,都是合法的
char * a = new char[2000];
char * b = new char[2000];
if( ((uintptr_t)a) < ((uintptr_t)b) ) {
// even on the same operating system ALSR may cause different runs to execute in here.
}
所以在 C++ 中没有一条合法的路径——有多个路径。
不,没有唯一的执行路径,也没有为 C++ 保证的单一最终状态。
即使有 sequenced-before 保证,最常见的原因之一是评估不同参数时的副作用:标准未定义参数评估的顺序,并且取决于实现。例如,以下代码可以提供多个有效输出,具体取决于所使用的编译器:
int display (int i, int j) {
std::cout << i << " " << j << std::endl;
return i<j ? i:j;
}
void my_funny_func (int a, int b, int c) {
std::cout << a << " " << " " << b << " " c << std::endl;
}
...
int i=1, j=1;
my_funny_func(display(i,j), display(++i, j), display(i, ++j));
该标准将对执行路径的保证限制为可观察的行为(即文件操作、对 volatile 变量的操作等):
1.9/1: Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained
below.
1.9/5: A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible
executions of the corresponding instance of the abstract machine
with the same program and the same input. However, if any such
execution contains an undefined operation, this International Standard
places no requirement on the implementation executing that program
with that input (not even with regard to operations preceding the
first undefined operation).
这是有目的的:它旨在最大限度地提高优化器对不可观察事件重新排序的自由度。但这留下了几种可能的结果(特别是对于非易失性变量,它们可能会被缓存而不是在每次更改时立即存储到内存中)。
对于java,我认为评估的顺序更准确地确定(参见this other answer)。这将显着减少有效执行路径的数量。
问题针对 C++ 或 Java 内存模型的主题,它定义了允许程序展示的行为。查看内存模型的一种简单方法是将其视为"filter",它定义了一组规则来关闭具有非法行为的执行(动作痕迹),其余的执行集是合法的。
问题是:对于单线程程序,给定一个固定的初始条件(如输入参数,变量初值),不与外部进程交互,是否存在唯一合法的执行(即只有一次执行满足内存模型)?
跟进问题:如果合法执行不止一次,是什么导致了非确定性?
备注:对于 C++,让我们将 sequenced-before 顺序视为总顺序。
编辑:正如 juanchopanza 在评论中所建议的,动态分配地址是单线程程序不确定性的来源之一。
在C++
中有多个路径,都是合法的
char * a = new char[2000];
char * b = new char[2000];
if( ((uintptr_t)a) < ((uintptr_t)b) ) {
// even on the same operating system ALSR may cause different runs to execute in here.
}
所以在 C++ 中没有一条合法的路径——有多个路径。
不,没有唯一的执行路径,也没有为 C++ 保证的单一最终状态。
即使有 sequenced-before 保证,最常见的原因之一是评估不同参数时的副作用:标准未定义参数评估的顺序,并且取决于实现。例如,以下代码可以提供多个有效输出,具体取决于所使用的编译器:
int display (int i, int j) {
std::cout << i << " " << j << std::endl;
return i<j ? i:j;
}
void my_funny_func (int a, int b, int c) {
std::cout << a << " " << " " << b << " " c << std::endl;
}
...
int i=1, j=1;
my_funny_func(display(i,j), display(++i, j), display(i, ++j));
该标准将对执行路径的保证限制为可观察的行为(即文件操作、对 volatile 变量的操作等):
1.9/1: Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.
1.9/5: A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible executions of the corresponding instance of the abstract machine with the same program and the same input. However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).
这是有目的的:它旨在最大限度地提高优化器对不可观察事件重新排序的自由度。但这留下了几种可能的结果(特别是对于非易失性变量,它们可能会被缓存而不是在每次更改时立即存储到内存中)。
对于java,我认为评估的顺序更准确地确定(参见this other answer)。这将显着减少有效执行路径的数量。