From http://www.hpl.hp.com/techreports/2004/HPL-2004-209.pdf:
To prevent the compiler from moving memory operations around calls to functions such as pthread mutex
lock()
, they are essentially treated as calls to opaque functions, about which the compiler has no information. The compiler effectively assumes that pthread mutexlock()
may read or write any global variable. Thus a memory reference cannot simply be moved across the call. This approach also ensures that transitive calls, e.g. a call to a functionf()
which then calls pthread mutexlock()
, are handled in the same way more or less appropriately, i.e. memory operations are not moved across the call tof()
either, whether or not the entire user program is being analyzed at once.为了防止编译器围绕对pthread mutex lock()等函数的调用移动内存操作,它们基本上被视为对opaque函数的调用,编译器没有这些函数。编译器有效地假设pthread互斥锁()可以读取或写入任何全局变量。因此,不能简单地在调用中移动存储器引用。这种方法还确保了可传递的呼叫,例如调用函数f()然后调用pthread互斥锁(),或多或少地以相同的方式处理,即无论是否整个用户程序,内存操作都不会通过调用f()移动正在分析中。
Why is that actually so? Any counter example for why references can not be moved?
为什么会这样呢?为什么引用无法移动的任何反例?
1 个解决方案
#1
3
The compiler is free to move code around. The (somewhat simplified) requirement is that there can be no visible side effects.
编译器可以*移动代码。 (稍微简化)的要求是不存在可见的副作用。
The article is describing why threading needs to be supported at the compiler level rather than the library level. So let's consider what it means as the compiler is optimizing the code. I'll start with Kerrek SB's excellent example, since this reply is too long for a comment.
本文描述了为什么需要在编译器级而不是库级支持线程。因此,让我们考虑一下编译器在优化代码时的含义。我将从Kerrek SB的优秀示例开始,因为此回复太长,无法发表评论。
int x = 0;
pthread_mutex_lock(&m);
x = y;
The optimizer sees a value that doesn't get modified but gets set twice. The copmiler has access to the code inside the function and can see that nothing can possibly modify the value of the assignment. Since there is no visible side effect, the optimizer can eliminate the assignment to zero and just replace it with the value of y. The optimizer will remove it, turning it into:
优化器会看到一个不会被修改但会被设置两次的值。 copmiler可以访问函数内部的代码,并且可以看到没有任何东西可以修改赋值的值。由于没有可见的副作用,优化器可以将赋值消除为零,并将其替换为y的值。优化器将删除它,将其转换为:
pthread_mutex_lock(&m);
int x = y;
This will probably not affect anything, the variable x was local and has no other effects.
这可能不会影响任何东西,变量x是本地的,没有其他影响。
Now let's make a more problematic contrived example.
现在让我们做一个更有问题的人为例子。
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();
...
So now we'll look at this from the optimizer's view. The optimizer sees that you read a value, then you do something that doesn't modify the value, then you test against the value you just stored. Since it cannot see a visible side effect, it might remove the comparison like this:
所以现在我们将从优化器的角度来看这个。优化器看到您读取了一个值,然后执行了一些不修改该值的操作,然后根据刚存储的值进行测试。由于它看不到可见的副作用,它可能会删除这样的比较:
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();
...
Then it looks again to remove dead code. It sees an unnecessary assignment to an integer, an unnecessary conditional since the result of the if is always false, and comes up with this:
然后它再次查看删除死代码。它看到一个不必要的赋值给一个整数,一个不必要的条件,因为if的结果总是为false,并提出这个:
if(globals.hasData) {
pthread_mutex_lock(&m);
// everything was removed.
DoOtherStuff();
If you compare that to the original function, it is hopefully clear this is not what the programmer intended at all.
如果将其与原始函数进行比较,则可以清楚地知道这不是程序员想要的。
There are a huge number of potential optimizations that have been discovered over the years. Many of them make assumptions about when it is safe to move code from one place to another or assuming that values are only modified by that block of code. These assumptions can break badly in concurrent programming.
多年来发现了大量潜在的优化。他们中的许多人都假设何时将代码从一个地方移动到另一个地方是安全的,或者假设这些值仅由该代码块修改。这些假设在并发编程中可能会严重破坏。
The optimizer needs to understand that certain bits of functionality cannot be moved, and that certain functions serve as barriers that cannot be moved across or that otherwise invalidate optimizer assumptions.
优化器需要理解某些功能不能被移动,并且某些功能作为无法移动的障碍或者使优化器假设无效。
#1
3
The compiler is free to move code around. The (somewhat simplified) requirement is that there can be no visible side effects.
编译器可以*移动代码。 (稍微简化)的要求是不存在可见的副作用。
The article is describing why threading needs to be supported at the compiler level rather than the library level. So let's consider what it means as the compiler is optimizing the code. I'll start with Kerrek SB's excellent example, since this reply is too long for a comment.
本文描述了为什么需要在编译器级而不是库级支持线程。因此,让我们考虑一下编译器在优化代码时的含义。我将从Kerrek SB的优秀示例开始,因为此回复太长,无法发表评论。
int x = 0;
pthread_mutex_lock(&m);
x = y;
The optimizer sees a value that doesn't get modified but gets set twice. The copmiler has access to the code inside the function and can see that nothing can possibly modify the value of the assignment. Since there is no visible side effect, the optimizer can eliminate the assignment to zero and just replace it with the value of y. The optimizer will remove it, turning it into:
优化器会看到一个不会被修改但会被设置两次的值。 copmiler可以访问函数内部的代码,并且可以看到没有任何东西可以修改赋值的值。由于没有可见的副作用,优化器可以将赋值消除为零,并将其替换为y的值。优化器将删除它,将其转换为:
pthread_mutex_lock(&m);
int x = y;
This will probably not affect anything, the variable x was local and has no other effects.
这可能不会影响任何东西,变量x是本地的,没有其他影响。
Now let's make a more problematic contrived example.
现在让我们做一个更有问题的人为例子。
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();
...
So now we'll look at this from the optimizer's view. The optimizer sees that you read a value, then you do something that doesn't modify the value, then you test against the value you just stored. Since it cannot see a visible side effect, it might remove the comparison like this:
所以现在我们将从优化器的角度来看这个。优化器看到您读取了一个值,然后执行了一些不修改该值的操作,然后根据刚存储的值进行测试。由于它看不到可见的副作用,它可能会删除这样的比较:
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();
...
Then it looks again to remove dead code. It sees an unnecessary assignment to an integer, an unnecessary conditional since the result of the if is always false, and comes up with this:
然后它再次查看删除死代码。它看到一个不必要的赋值给一个整数,一个不必要的条件,因为if的结果总是为false,并提出这个:
if(globals.hasData) {
pthread_mutex_lock(&m);
// everything was removed.
DoOtherStuff();
If you compare that to the original function, it is hopefully clear this is not what the programmer intended at all.
如果将其与原始函数进行比较,则可以清楚地知道这不是程序员想要的。
There are a huge number of potential optimizations that have been discovered over the years. Many of them make assumptions about when it is safe to move code from one place to another or assuming that values are only modified by that block of code. These assumptions can break badly in concurrent programming.
多年来发现了大量潜在的优化。他们中的许多人都假设何时将代码从一个地方移动到另一个地方是安全的,或者假设这些值仅由该代码块修改。这些假设在并发编程中可能会严重破坏。
The optimizer needs to understand that certain bits of functionality cannot be moved, and that certain functions serve as barriers that cannot be moved across or that otherwise invalidate optimizer assumptions.
优化器需要理解某些功能不能被移动,并且某些功能作为无法移动的障碍或者使优化器假设无效。