23811

Why do global variables cause trouble for compiler optimizations in function calls?

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 mutex lock() may read or write any global variable. <strong>Thus a memory reference cannot simply be moved across the call.</strong> This approach also ensures that transitive calls, e.g. a call to a function f() which then calls pthread mutex lock(), are handled in the same way more or less appropriately, i.e. memory operations are not moved across the call to f() either, whether or not the entire user program is being analyzed at once.

Why is that actually so? Any counter example for why references can not be moved?

Answer1:

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.

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:

pthread_mutex_lock(&m); int x = y;

This will probably not affect anything, the variable x was local and has no other effects.

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(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.

Recommend

  • How do I force a browser window to always be on top and in focus
  • Is storing an OAuth token in cookies bad practise?
  • What is the best way to join ordered arrays?
  • getting started with http tunneling
  • How to create L lists of n non-zero random decimals where each list sums to 1.0?
  • How to parse Java properties which contains variables?
  • How to run requests.get asynchronously in Python 3 using asyncio?
  • How to notify a specific thread in Java
  • wxWidgets: Detecting click event on custom controls
  • How to limit cursor movement in WPF based app?
  • mysql select inside limit
  • Redis under Classic ASP(VBScript)
  • Pointer vs Reference difference when passing Eigen objects as arguments
  • get all processes in parallel
  • Create a multiple screen android application
  • LNK1104: cannot open file 'kernel32.lib'
  • Accessing another variable in the same class with a click event
  • What is this strange character in chrome's resource css viewer?
  • How to create a new Bundle object?
  • Is there any way to call saveCurrentTurnWithMatchData without sending a push notification?
  • Why can't UI components be accessed from a backgroundworker?
  • Can my PDF ping my server when it is opened?
  • Angular2 - Template reference inside NgSwitch
  • How to getText() from the input field of an angularjs Application
  • Pycharm: Marking a folder as 'sources root' is not recursive for subfolders
  • DIV instruction jumping to random location?
  • How VBA declared Volatility works
  • CakePHP 2.0.4 - findBy magic methods with conditions
  • ActiveRecord query for a count of new users by day
  • why overloaded new operator is calling constructor even I am using malloc inside overloading functio
  • Reading JSON from a file using C++ REST SDK (Casablanca)
  • How to rebase a series of branches?
  • JavaScriptCore crash on iOS9
  • Updating server-side rendering client-side
  • How to handle AllServersUnavailable Exception
  • Can I have the cursor start on a particular column by default in jqgrid's edit mode?
  • Acquiring multiple attributes from .xml file in c#
  • How to CLICK on IE download dialog box i.e.(Open, Save, Save As…)
  • How can I remove ASP.NET Designer.cs files?
  • java string with new operator and a literal