根据我的理解:
编译器可以对我的代码进行任何重新排序。
关键部分内的代码不会移动到关键部分的外部。
现在说我有以下代码:
printf("Hi");
EnterCriticalSection(&CriticalSection);
printf("Inside the critical section");
printf("Also inside the critical section");
LeaveCriticalSection(&CriticalSection);
printf("Bye");
现在,编译器是否会实际查找函数EnterCriticalSection()
和LeaveCriticalSection()
,而不是将它们内部的代码移到外面?
答案 0 :(得分:6)
编译器可以对我的代码进行任何重新排序。
这是不正确的。编译器是有限的。
关键部分内的代码不会移动到关键部分的外部。
这也是不正确的,具体取决于关键部分内/外的代码。
编译器对函数中的每段代码都有一堆约束。这些约束可能是输入和输出(除非你先执行X,否则不能执行Y)或更多的通用语句,如"这会影响某处内存的内容"。编译代码时,编译器将保留这些约束。如果编译器不知道函数是什么,它将使用最重的约束集。
通常,这意味着编译器不会翻转两个函数调用 1 的顺序。
f(); // Maybe affects memory somewhere.
g(); // Maybe affects memory somewhere.
这也意味着通常,如果访问内存,那些内存访问必须相对于函数调用进行排序。
void myfunc(X *ptr) {
my_lock(ptr->mutex); // Maybe affects memory somewhere.
ptr->field++; // Definitely affects memory at ptr->field.
my_unlock(ptr->mutex); // Maybe affects memory somewhere.
}
但是,它可以对关键部分的内容进行重新排序:
int i = 5;
my_lock(); // Maybe affects memory somewhere.
i++; // This is not "memory somewhere", this is my variable,
// it's a register or on the stack and nobody else can
// change it.
my_unlock(); // Maybe affects memory somewhere.
因此可以将其重新排序为:
int i = 6;
my_lock();
my_unlock();
上面的代码可以重新排序,因为编译器对允许修改i
的人有特殊的了解。如果你在其他地方有一些特殊代码,它们会在堆栈中试图创建一个指向i
的指针,即使程序中没有出现&i
,你也会违反与编译器的合同(又名你的程序有未定义的行为)。
我希望这能澄清事情。
1 :您可以添加更改此规则的__declspec(noalias)
等注释,LTO /整个程序优化/过程间优化也可以改变这一点。
答案 1 :(得分:0)
假设编译器没有任何关于EnterCriticalSection的特殊知识;它无法对函数的作用做出任何假设。
因此,它无法对printf
语句重新排序,因为它必须允许EnterCriticalSection
也可能写入stdout
,stdout
上的输出必须出现与执行printf
语句的顺序相同。
这同样适用于访问全局变量之类的事情,编译器必须允许函数访问这些变量。