tags:

views:

220

answers:

3
+3  A: 

The question is, in this case pf is itself volatile, so is the compiler allowed to assume that b won't change in the loop even if b is not volatile?

It can't, because you say that pf might be changed by the other threads, and this indirectly changes b if pf is called then by the while loop. So while it is theoretically not required to read b normally, it in practice must read it to determine whether it should short circuit (when b becomes false it must not read vb another time).


Answer to the second part

In this case pf is not volatile anymore, so the compiler can get rid of it and see that f1 has an empty body and f2 sets b to false. It could optimize main as follows

int main()
{
   // threads here (which you say can only change "vb")

   while(vb)
   {
      int x;
      std::cin >> x;
      if(x != 0)
         break;    
   } 
}

Answer to older revision

One condition for the compiler to be allowed to optimize the loop away is that the loop does not access or modify any volatile object (See [stmt.iter]p5 in n3126). You do that here, so it can't optimize the loop away. In C++03 a compiler wasn't allowed to optimize even the non-volatile version of that loop away (but compilers did it anyway).

Note that another condition for being able to optimize it away is that the loop contains no synchronization or atomic operations. In a multithreaded program, such should be present anyway though. So even if you get rid of that volatile, if your program is properly coded I don't think the compiler can optimize it away entirely.

Johannes Schaub - litb
Armen Tsirunyan
@Armen for sure, if `b` is never changed then C++03 entirely allows that. It needs to be volatile for it to be not allowed to optimize it away.
Johannes Schaub - litb
@Johannes: Edited my question. Also I can't say I am completely satisfied with the answer. You see, your answer also implies that if a loop contains a call via a pointer-to-function which is set inside the loop according to user input the compiler can assume b does not change, which I don't want to believe is true. Should I?
Armen Tsirunyan
Note that in C++, `volatile` does _not_ guarantee that one thread sees the other one's changes to a variable. (This is different, for example, in .NET, where it does this.) If you need thread thread synchronization, you need proper synchronization mechanisms like atomic values, mutexes etc.
sbi
@sbi: my question clearly reads: "Let's forget about synchronization"
Armen Tsirunyan
@Armen: But perhaps it is relevant. There is nothing to tell the compiler that `b` can change unexpectedly.
Oli Charlesworth
@Johannes: Thank you, I understand twice as more now. But I have edited my question yet again, and if you could please answer my last (I promise) revision, I would be very thankful and would accept your answer.
Armen Tsirunyan
I don't think your answer to the first example can be right. You're omitting the key fact that `b` is global; thus, a function call may change it. In particular, the call to `fp` may change it, if the pointer points to an appropriate function. There seems to be nothing here that would allow a compiler to prove that the function call won't change it, and so it can't optimize it away, regardless of its volatility.
Brooks Moses
@Brook I think I was assuming that the compiler would inline the call to f1. But if pf can be changed by the threads, it can't inline anymore. And then, in order to not read vb another time when b is false, it has to always rad vb. I will change my anwer accordingly.
Johannes Schaub - litb
@Johannes: FWIW, I agree with the changed answer. Upvoted. :)
Brooks Moses
A: 

volatile only hurts you if you think you could have benefited from an optimization that can't be done or if it communicates something that isn't true.

In your case, you said that these variables can be changed by other threads. Reading code, that's my assumption when I see volatile, so from a maintainer's perspective, that's good -- it's giving me extra information (which is true).

I don't know whether the optimizations are worth trying to salvage since you said this isn't the real code, but if they aren't then there aren't any reasons to not use volatile.

Not using volatile when you are supposed to results in incorrect behavior, since the optimizations are changing the meaning of the code.

I worry about coding the minutia of the standard and behavior of your compilers because things like this can change and even if they don't, your code changes (which could effect the compiler) -- so, unless you are looking for micro-optimization improvements on this specific code, I'd just leave it volatile.

Lou Franco
+2  A: 

The exact requirements on volatile in the current C++ standard in a case like this are, as I understand it, not entirely well-defined by the standard, since the standard doesn't really deal with multi-threading. It's basically a compiler hint. So, instead, I'll address what happens in a typical compiler.

First, suppose the compiler is compiling your functions independently, and then linking them together. In either example, you have a loop in which you're checking a variable, and calling a function pointer. Within the context of that function, the compiler has no idea what the function behind that function pointer will do, and thus it must always re-load b from memory after calling it. Thus, volatile is irrelevant there.

Expanding that to your first actual case, and allowing the compiler to make whole-program optimizations, because pf is volatile the compiler still has no idea what it's going to be pointing at (it can't even assume it's either f1 or f2!), and thus likewise cannot make any assumptions about what will be unmodified across the function-pointer call -- and so volatile on b is still irrelevant.

Your second case is actually simpler -- vb in it is a red herring. If you eliminate that, you can see that even in completely single-threaded semantics, the function-pointer call may modify b. You're not doing anything with undefined behavior, and so the program must operate correctly without volatile -- remember that, if you aren't considering a situation with external thread tweaks, volatile is a no-op. Therefore, without vb in the picture, you cannot possibly need volatile, and it's pretty clear that adding vb changes nothing.

Thus, in summary: You don't need volatile in either case. The difference, insofar as there is one, is that in the first case if fp were not volatile, a sufficiently-advanced compiler could possibly optimize b away, whereas it cannot even without volatile anywhere in the program in the second case. In practice, I do not expect any compilers would actually make that optimization.

Brooks Moses
@Brooks: In my second example, will the compiler typically understand that pf is either f1 or f2, analyze both those functions and see that b can change, or the mere invocation of a pointer-to-function is enough for it to refrain from optimizing reads from a global variable?
Armen Tsirunyan
@Brooks: And, incidentally, c++0x does deal with multi-threading... a lot
Armen Tsirunyan
Right; I was thinking of the current C++ standard (and edited to make that clear). In any case, I would be surprised if in the second example the compiler does any analysis on what the function pointer can point to; I wouldn't expect that in general that would be a productive analysis (even though it's obviously useful in this small example).
Brooks Moses