Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are volatile variables useful? If yes then when?

Answering this question made me think about something is still not clear for me. Let's first assume we read all from this post and this post.

[begin edit] Maybe it's not so obvious (Italian humor?!) but title is just pretty provocative: of course there should be a reason if volatile has been included in C#, I just can't understand exact one.[end edit]

In short we know we have three tools to share a variable between threads:

  • lock because this will prevent instruction reordering.
  • volatile because will force CPU to always read value from memory (then different CPUs/cores won't cache it and they won't see old values).
  • Interlocked operations (Increment/Decrement and CompareExchange) because they'll perform change + assignment in a single atomic (fast faster than, for example, a volatile + lock) operation.

What I don't understand (C# specs reference would be appreciated):

  • How lock will prevent cache problem? Is it implicit a memory barrier in a critical section?
  • Volatile variables can't be local (I read something from Eric Lippert about this but I can't find that post now and I don't remember his comments and - to be honest - I didn't even understand it very well). This makes me think they're not implemented with an Interlocked.CompareExchange() and friends, in what they're different?

What volatile modifier will do for example in this code?

volatile int _volatileField = 0;
int _normalField = 0;

void test()
{
    Interlocked.Increment(ref _normalField);
    ++_volatileField;
}

[begin edit] previous example involves atomic read + write, let's change it to _volatileField = 1;, here I'm not talking about atomic operations. [end edit]

Moreover what compiler (beside warnings) will do here:

Interlocked.Increment(ref _volatileField);

They seems pretty different things (as I would imagine they are) but for my understanding Interlocked.Increment() operand should implicitly be volatile (then it'll add just atomic increment). How is it possible for non volatile fields? Does they imply barriers too? Doesn't this hurt performance a lot (compared to volatile)?

If volatile doesn't imply barriers but others do then why we can't use them as on local variables? Especially when used in, for example, parallel loops this will hurt performance in a significant way (I'm thinking about small functions with little code that work on a lot of data where data cache may be well used).

[begin edit] I found previous sentence was really unclear (sorry for my English). What I mean is: if performance (of volatile compared to CompareExchange, where comparison is applicable) are better (yes we can measure and in some scenario difference is measurable and visible) then why we can't use them for local variables? I'm thinking about parallel loops that manipulates a lot of data (where both overhead and barriers may hurt performance a lot).[end edit]

like image 874
Adriano Repetti Avatar asked Dec 03 '13 10:12

Adriano Repetti


People also ask

In which situation would it be important to use a volatile variable?

Volatile is used in C programming when we need to go and read the value stored by the pointer at the address pointed by the pointer. If you need to change anything in your code that is out of compiler reach you can use this volatile keyword before the variable for which you want to change the value.

When Should a variable be volatile?

A variable should be declared volatile whenever its value could change unexpectedly. In practice, only three types of variables could change: Memory-mapped peripheral registers. Global variables modified by an interrupt service routine.

When should the volatile modifier be used?

The volatile modifier is used to let the JVM understand that a thread accessing the variable should always merge its own personal copy of the variable with the original in the memory. Accessing a volatile variable synchronizes all the cached copy of the variables in the main memory.

What is volatile variable in C where it is used?

The volatile qualifier is applied to a variable when we declare it. It is used to tell the compiler, that the value may change at any time. These are some properties of volatile. The volatile keyword cannot remove the memory assignment. It cannot cache the variables in register.


1 Answers

This question is very confusing. Let me try to break it down.

Are volatile variables useful?

Yes. The C# team would not have added a useless feature.

If yes then when?

Volatile variables are useful in certain highly performance-sensitive multithreaded applications where the application architecture is predicated on sharing memory across threads.

As an editorial aside, I note that it should be rare for normal line-of-business C# programmers to be in any of these situations. First, the performance characteristics we are talking about here are on the order of tens of nanoseconds; most LOB applications have performance requirements measured in seconds or minutes, not in nanoseconds. Second, most LOB C# applications can do their work with only a small number of threads. Third, shared memory is a bad idea and a cause of many bugs; LOB applications which use worker threads should not use threads directly, but rather use the Task Parallel Library to safely instruct worker threads to perform calculations, and then return the results. Consider using the new await keyword in C# 5.0 to facilitate task-based asynchrony, rather than using threads directly.

Any use of volatile in a LOB application is a big red flag and should be heavily reviewed by experts, and ideally eliminated in favour of a higher-level, less dangerous practice.

lock will prevent instruction reordering.

A lock is described by the C# specification as being a special point in the code such that certain special side effects are guaranteed to be ordered in a particular way with respect to entering and leaving the lock.

volatile because will force CPU to always read value from memory (then different CPUs/cores won't cache it and they won't see old values).

What you are describing is implementation details for how volatile could be implemented; there is not a requirement that volatile be implemented by abandoning caches and going back to main memory. The requirements of volatile are spelled out in the specification.

Interlocked operations perform change + assignment in a single atomic (fast) operation.

It is not clear to me why you have parenthesized "fast" after "atomic"; "fast" is not a synonym for "atomic".

How lock will prevent cache problem?

Again: lock is documented as being a special event in the code; a compiler is required to ensure that other special events have a particular order with respect to the lock. How the compiler chooses to implement those semantics is an implementation detail.

Is it implicit a memory barrier in a critical section?

In practice yes, a lock introduces a full fence.

Volatile variables can't be local

Correct. If you are accessing a local from two threads then the local must be a special local: it could be a closed-over outer variable of a delegate, or in an async block, or in an iterator block. In all cases the local is actually realized as a field. If you want such a thing to be volatile then do not use high-level features like anonymous methods, async blocks or iterator blocks! That is mixing the highest level and the lowest level of C# coding and that is a very strange thing to do. Write your own closure class and make the fields volatile as you see fit.

I read something from Eric Lippert about this but I can't find that post now and I don't remember his answer.

Well I don't remember it either, so I typed "Eric Lippert Why can't a local variable be volatile" into a search engine. That took me to this question:

why can't a local variable be volatile in C#?

Perhaps that is what you're thinking of.

This makes me think they're not implemented with an Interlocked.CompareExchange() and friends.

C# implements volatile fields as volatile fields. Volatile fields are a fundamental concept in the CLR; how the CLR implements them is an implementation detail of the CLR.

in what they're different?

I don't understand the question.

What volatile modifier will do for example in this code?

++_volatileField;

It does nothing helpful, so don't do that. Volatility and atomicity are completely different things. Doing a normal increment on a volatile field does not make the increment into an atomic increment.

Moreover what compiler (beside warnings) will do here:

The C# compiler really ought to suppress that warning if the method being called introduces a fence, as this one does. I never managed to get that into the compiler. Hopefully the team will someday.

The volatile field will be updated in an atomic manner. A fence will be introduced by the increment, so the fact that the volatile half-fences are skipped is mitigated.

How is it possible for non volatile fields?

That's an implementation detail of the CLR.

Does they imply barriers too?

Yes, the interlocked operations introduce barriers. Again, this is an implementation detail.

Doesn't this hurt performance a lot (compared to volatile)?

First off, comparing the performance of broken code to working code is a waste of time.

Second, if you do feel like wasting time, you are perfectly capable of measuring the performance of each yourself. Write the code both ways, get out a stopwatch, run it a trillion times each way, and you'll know which is faster.

If volatile doesn't imply barriers but others do then why we can't use them as on local variables?

I can't even begin to make sense of this question.

like image 95
Eric Lippert Avatar answered Sep 22 '22 03:09

Eric Lippert