从http://www.hpl.hp.com/techreports/2004/HPL-2004-209.pdf:
为了防止编译器将内存操作移至对诸如pthread Mutex之类的函数的调用
lock()
,它们实际上被视为对不透明函数的调用,编译器对此不了解任何信息。编译器有效地假设pthread互斥锁lock()
可以读取或写入任何全局变量。因此,不能简单地在调用中移动内存引用。这种方法还确保以适当的方式或多或少地适当地处理传递调用(例如f()
,对随后调用pthread互斥锁的函数的调用)lock()
,即f()
,无论整个用户程序是否执行,内存操作都不会跨该调用移动马上被分析。
为什么会这样呢?为什么引用不能移动的任何反例?
编译器可以自由移动代码。(略有简化)要求是没有可见的副作用。
本文介绍了为什么需要在编译器级别而不是库级别支持线程。因此,让我们考虑一下编译器正在优化代码时的含义。我将从Kerrek SB的出色示例开始,因为此回复对于评论太长。
int x = 0;
pthread_mutex_lock(&m);
x = y;
优化器看到的值不会被修改,但会被设置两次。copmiler可以访问该函数内部的代码,并且看不到任何东西可以修改赋值。由于没有可见的副作用,优化器可以消除分配给零的值,而只需将其替换为y的值即可。优化器将其删除,将其转换为:
pthread_mutex_lock(&m);
int x = y;
这可能不会影响任何东西,变量x是局部变量,没有其他影响。
现在让我们做一个更具问题的人为例子。
if(globals.hasData) {
int prelock_value = globals.foo;
pthread_mutex_lock(&m);
if(prelock_value != globals.foo) {
// value changed before we could lock it, do something different
DoSpecialStuffSinceValueChangedWhileWaiting();
pthread_mutex_unlock(&m);
return;
}
DoOtherStuff();
...
因此,现在我们将从优化器的角度进行研究。优化程序会看到您读取了一个值,然后执行了一些不会修改该值的操作,然后针对刚存储的值进行了测试。由于看不到可见的副作用,因此可能会像这样删除比较:
if(globals.hasData) {
int prelock_value = globals.foo;
pthread_mutex_lock(&m);
if( false /* always false: prelock_value != globals.foo */ ) {
// value changed before we could lock it, do something different
DoSpecialStuffSinceValueChangedWhileWaiting();
pthread_mutex_unlock(&m);
return;
}
DoOtherStuff();
...
然后,它看起来再次删除了无效代码。由于if的结果始终为false,因此它会看到对整数的不必要分配,不必要的条件,并得出以下结果:
if(globals.hasData) {
pthread_mutex_lock(&m);
// everything was removed.
DoOtherStuff();
如果将其与原始函数进行比较,则可以很清楚地看出这根本不是程序员想要的。
这些年来,已经发现了大量潜在的优化方法。他们中的许多人都假设将代码从一个地方安全移到另一个地方的安全性,或者假设值仅由该代码块修改。这些假设在并行编程中可能会严重破坏。
优化器需要了解某些功能不能移动,并且某些功能会成为无法跨越的障碍,否则会使优化器的假设无效。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句