looking at senpai's code, i came across something odd:
* variables subject to concurrent access are declared as volatile. why?
* they are always modified under lock, which is correct. but could it be faster in some cases to not use locking, and use atomic variables? eg. you just need to increment the node cnt by 1 seems wasteful to use a lock.
volatile?
Moderators: hgm, Rebel, chrisw
-
- Posts: 127
- Joined: Sat Jan 22, 2011 7:14 pm
- Location: Lille, France
Re: volatile?
I think at a level more abstract than C++ because of my interest in programming-language design. I used "volatile" as you said, as an "executable comment" that these variables are shared. I agree that it's not what volatile means in C++ but I would like to have this feature in a programming language, ideally with a run-time error in a special debug mode if I forget a marker.
Regarding atomic. I first implemented SMP in "normal" C++, and only changed threads to use the C++11 model later. During the last week before the release we had many last-minute problems and I decided to sacrifice code sanitisation in order to minimise the risk of introducing a new bug, especially in SMP. But I will look at the C++11 memory model which seems interesting, including thread-local storage.
Short version: I couldn't clean up in time and I suggest not over-thinking my weird stuff.
Regarding atomic. I first implemented SMP in "normal" C++, and only changed threads to use the C++11 model later. During the last week before the release we had many last-minute problems and I decided to sacrifice code sanitisation in order to minimise the risk of introducing a new bug, especially in SMP. But I will look at the C++11 memory model which seems interesting, including thread-local storage.
Short version: I couldn't clean up in time and I suggest not over-thinking my weird stuff.
-
- Posts: 3232
- Joined: Mon May 31, 2010 1:29 pm
- Full name: lucasart
Re: volatile?
Yes, it doesn't look like there is a concept of "please make this variable thread safe" in C++. From what I understood, you have to use either locks, or atomic variables (but then you need to make sure your operations are guaranteed to be atomic, which looks like another can of worms...)Xann wrote:I think at a level more abstract than C++ because of my interest in programming-language design. I used "volatile" as you said, as an "executable comment" that these variables are shared. I agree that it's not what volatile means in C++ but I would like to have this feature in a programming language, ideally with a run-time error in a special debug mode if I forget a marker.
The reason I ask, is because I was wondering what this obscure "volatile" keyword really means. I read this short article:
https://www.kernel.org/doc/Documentatio ... armful.txt
Essentially they make the point that
They say that volatile has nothing to do with concurrency, it's almost never correct to use it, and the lock is enough.In properly-written code, volatile can only serve to slow things down
On the other hand, Stockfish also declares all shared variables as volatile. And I know that Marco is much more knowledgeable than I am in C++, especially when it comes to multi-threading. So I can't help wondering if there isn't indeed a good reason for all this volatile stuff
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
-
- Posts: 741
- Joined: Tue May 22, 2007 11:13 am
Re: volatile?
Quote from the latest draft C++ Standardlucasart wrote: The reason I ask, is because I was wondering what this obscure "volatile" keyword really means. I read this short article:
https://www.kernel.org/doc/Documentatio ... armful.txt
Essentially they make the point thatThey say that volatile has nothing to do with concurrency, it's almost never correct to use it, and the lock is enough.In properly-written code, volatile can only serve to slow things down
7.1.6.1 The cv-qualifiers [dcl.type.cv]
7 [ Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object
because the value of the object might be changed by means undetectable by an implementation. Furthermore,
for some implementations, volatile might indicate that special hardware instructions are required to access
the object. See 1.9 for detailed semantics. In general, the semantics of volatile are intended to be the
same in C++ as they are in C. — end note ]
-
- Posts: 5566
- Joined: Tue Feb 28, 2012 11:56 pm
Re: volatile?
Volatile means the compiler must generate code that reads from and writes to it in the order specified by the C or C++ abstract machine.lucasart wrote:The reason I ask, is because I was wondering what this obscure "volatile" keyword really means. I read this short article:
https://www.kernel.org/doc/Documentatio ... armful.txt
Essentially they make the point thatThey say that volatile has nothing to do with concurrency, it's almost never correct to use it, and the lock is enough.In properly-written code, volatile can only serve to slow things down
Volatile is just what you want if you're accessing memory mapped hardware I/O registers. You don't normally do this in application programs.
For multithreaded programs using volatile is not necessary if you are properly using the synchronisation primitives of a thread library, e.g. pthreads or C++11 threads.
If you don't want to be restricted by a thread library, but (in addition) want to rely on how the compiler will map your code to the machine, then volatile is useful. Your program will be full of undefined behavior, but if that is a conscious choice that is not necessarily bad. It will just not be "standard C/C++ plus pthreads" or "standard C11/C++11".
I'll have a look at this.On the other hand, Stockfish also declares all shared variables as volatile. And I know that Marco is much more knowledgeable than I am in C++, especially when it comes to multi-threading. So I can't help wondering if there isn't indeed a good reason for all this volatile stuff
For senpai it would be interesting to add a line
Code: Select all
#define volatile
-
- Posts: 5566
- Joined: Tue Feb 28, 2012 11:56 pm
Re: volatile?
Btw, there were some very interesting (and very very long) threads on this subject not that long ago.lucasart wrote:The reason I ask, is because I was wondering what this obscure "volatile" keyword really means.
-
- Posts: 3232
- Joined: Mon May 31, 2010 1:29 pm
- Full name: lucasart
Re: volatile?
Thanks Ronald. So you basically confirm exactly what the Linux people said in the article. I'll remove volatile and do some measurements (when my computer is free, right now it's busy running tests).
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
-
- Posts: 5566
- Joined: Tue Feb 28, 2012 11:56 pm
Re: volatile?
Many fields of the SplitPoint struct are volatile, but for a reason:lucasart wrote:On the other hand, Stockfish also declares all shared variables as volatile. And I know that Marco is much more knowledgeable than I am in C++, especially when it comes to multi-threading. So I can't help wondering if there isn't indeed a good reason for all this volatile stuff
Code: Select all
struct SplitPoint {
...
// Shared data
Mutex mutex;
volatile uint64_t slavesMask;
volatile uint64_t nodes;
volatile Value alpha;
volatile Value bestValue;
volatile Move bestMove;
volatile int moveCount;
volatile bool cutoff;
};
Code: Select all
if (SpNode)
alpha = splitPoint->alpha;
It seems splitPoint->moveCount is always accessed under lock protection (except for an assert), so it seems it could be made non-volatile. It might give the compiler a little bit more freedom when compiling this line:
Code: Select all
moveCount = ++splitPoint->moveCount;
Or do I have it wrong here? Maybe the compiled code must in fact do the following?
1. read splitPoint->moveCount
2. increment the retrieved value
3. write this value to splitPoint->moveCount
4. read this value from splitPoint->moveCount and assign it to moveCount
Does anyone know which of the two (if any of the two) is mandated by the C/C++ abstract machine?
In any case, without volatile anything is allowed as long as the specified result is achieved, and the compiler may assume that no other code or hardware is messing with splitPoint->moveCount (which is in fact a valid assumption due to the locks around this code).
All this means that SF relies on UB. It is not impossible that future compiler optimisations break it in subtle ways. But personally I would advise against fixing this.
-
- Posts: 931
- Joined: Tue Mar 09, 2010 3:46 pm
- Location: New York
- Full name: Álvaro Begué (RuyDos)
Re: volatile?
Using volatile doesn't allow you to access the variable without locking. You are most likely going to invoke undefined behavior if you do so.
C++11 provides std::atomic, which is probably closer to what you want.
C++11 provides std::atomic, which is probably closer to what you want.
-
- Posts: 2684
- Joined: Sat Jun 14, 2008 9:17 pm
Re: volatile?
A variable accessed under lock protection does not require to be defined 'volatile'.lucasart wrote: On the other hand, Stockfish also declares all shared variables as volatile. And I know that Marco is much more knowledgeable than I am in C++, especially when it comes to multi-threading. So I can't help wondering if there isn't indeed a good reason for all this volatile stuff
Instead if accessed by many threads outside lock protection is better to define volatile, although of course this doesn't give you any protection against races and you really need to know what you are doing.
But races are an intrinsic part of a SMP chess engine, for instance TT table is intrinsically racy for performance reasons, because to protect with lock it would be very slow...this is not a problem per-se, as long as you know it and you deal with it.