Volatile and threads

Hi all,

My first question is how to recognize a section in the project's code in which some variable(s) may be optimized away by the compiler that we don't wish to? Or, how to know where I should use the volatile specifier for a variable in the code, clearly?

My second question is:
Can we rely on the number given out by std::thread::hardware_concurrency and launch only at most that number of threads if we aim to using multi-threading in a project?
Last edited on
> how to know where I should use the volatile specifier for a variable in the code

Stroustrup in 'The C++ Programming Language'

The volatile specifier is used to indicate that an object can be modified by something external to the thread of control. For example:
volatile const long clock_register; // updated by the hardware clock

A volatile specifier basically tells the compiler not to optimize away apparently redundant reads and writes. For example:
1
2
3
auto t1 {clock_register};
// ... no use of clock_register here ...
auto t2 {clock_register};

Had clock_register not been volatile, the compiler would have been perfectly entitled to eliminate one of the reads and assume t1==t2.

Do not use volatile except in low-level code that deals directly with hardware.

Do not assume that volatile has special meaning in the memory model. It does not. It is not – as in some later languages – a synchronization mechanism. To get synchronization, use an atomic, a mutex, or a condition_variable.



> Can we rely on the number given out by std::thread::hardware_concurrency

As per the standard, the returned value 'should only be considered to be a hint ... If this value is not computable or well-defined, an implementation should return 0'

Nevertheless, it is reasonable to assume that a non-zero value is a usable hint; that it reports 'the number of hardware thread contexts'.
> Can we rely on the number given out by std::thread::hardware_concurrency and launch
> only at most that number of threads if we aim to using multi-threading in a project?
https://en.cppreference.com/w/cpp/thread/thread/hardware_concurrency
Quite clearly, the answer is no.
"The value should be considered only a hint. "

> Or, how to know where I should use the volatile specifier for a variable in the code, clearly?
Is the variable modified by one thread/signal handler, and inspected in another thread/signal handler?

Is the variable a direct reference to some physical hardware register?
For example:
1
2
3
volatile char *serial_port = PORT_ADDRESS;
char *message = "hello world";
while ( *message ) *serial_port = *message++;

Without the volatile, the compiler might deduce that the end result should be *serial_port = 'd' and just do that. Which is not what you really wanted.
Regarding std::thread::hardware_concurrency(), notwithstanding what's been said above, if you assume that the platform your program will run on is similar to yours[1], you can assume that the function will query system and return the actual number of directly controllable[2] compute units. Unless you need absolute portability, this is a reasonable assumption.


[1] I.e. it runs a desktop/server/mobile OS and has a typical computer architecture with fixed CPUs and memory. These assumptions may break down for example if the CPU and RAM are hot-swappable (some high-end servers are known to do that) or if the system has some way to transparently schedule work on CPUs at the other side of a network pipe which may vary in number.

[2] By "directly controllable" I'm excluding for example additional processors that may be connected by some expansion bus such as PCIe. We're only interested in the CPU itself.
My first question is how to recognize a section in the project's code in which some variable(s) may be optimized away by the compiler that we don't wish to? Or, how to know where I should use the volatile specifier for a variable in the code, clearly?

I think this is thinking about it from the wrong direction. Modifying and accessing variables from multiple threads is simply UB without proper synchronization. It's something that you should think about from the start. Compiler optimizations is one easy way to get people to understand why it can go wrong but it's not the only reason. There might be hardware caches that needs to be flushed and variables might not be written all at once so there is a chance of reading outdated/partial values... As others have said, volatile is not the tool for this.

http://IsVolatileUsefulWithThreads.com

Use mutexes, semaphores or atomics instead.
Last edited on
@JLBorges>Nevertheless, it is reasonable to assume that a non-zero value is a usable hint; that it reports 'the number of hardware thread contexts'
Good point! :) The return value for my PC was 4, exactly the same as the number given out in its specs link: https://ark.intel.com/content/www/us/en/ark/products/77488/intel-core-i34160-processor-3m-cache-3-60-ghz.html
So we can firstly check the return value, if it's non-zero, then we rely on that for the number of helper threads. Great.

@Salem c >Is the variable modified by one thread/signal handler, and inspected in another thread/signal handler?
Is the variable a direct reference to some physical hardware register?

Good points, thanks. :)

Will check helios and Peter replies as soon as possible.
PS: Glad to be among professionals to learn things from them. :)
Last edited on
@helios. Thank you, two great points! (Especially portability).
Thank you Peter.
Topic archived. No new replies allowed.