根据我的理解:
编译器可以对我的代码进行任何它想要的重新排序。
临界区内的代码不会被移到临界区外。
现在假设我有以下代码:
printf("Hi");
EnterCriticalSection(&CriticalSection);
printf("Inside the critical section");
printf("Also inside the critical section");
LeaveCriticalSection(&CriticalSection);
printf("Bye");
现在,编译器会实际查找函数 EnterCriticalSection()
和 LeaveCriticalSection()
而不是将它们内部的代码移到外部吗?
最佳答案
A compiler can do whatever re-ordering it wants to my code.
这是不正确的。编译器是有限的。
The code inside a critical section will not be moved to the outside of the critical section.
这也是不正确的,具体取决于关键部分内部/外部的代码。
优化和约束
编译器对函数中的每一段代码都有一堆约束。这些约束可能是输入和输出(除非先执行 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/whole-program-optimization/interprocedural optimization 也可以改变事情。
关于c - 编译器如何知道不要将临界区内的代码移动到临界区外?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42683527/