M
Markus
Hello everyone.
Recently I stumbled upon an interesting problem related to thread-parallel
programming in C (and similarily C++). As an example assume a simple "buffer"
array of size 8, e.g. with static lifetime and exteral linkage. One thread fills
the buffer structure, and the other in some way evaluates its contents, e.g.
do {
synchronize_with_other_thread();
for (int i=0;i<8;++i) {
sum += buffer;
}
} while (sum < THRESHOLD);
From what I understand, the programming standard (C99) assumes a single serial
instruction stream. To ensure each loop iteration retrieves current values from
buffer[], one has to make it volatile, which prevents a lot of useful
optimizations by the compiler. In any other case, the compiler would be
_allowed_ to keep the whole contents of buffer[] in the register file and
operate on that.
It is clear, that in practice various ways exist to avoid "volatile" for buffer
[] and allow the compiler to optimize mor aggressively. Especially, one could do
the evaluation (here the sum) in a non-inline function. For data with external
linkage, any library call inside "synchronize_with_other_thread()" will be
sufficient, too, as a compiler cannot assure buffer[] is not affected by it. But
both solutions rely on the inablilities of compiler and linker, and would not be
guaranteed to work with an "omniscient" compiler that is allowed to perform
inter-procedure optimizations.
Clearly related to this, I could not find out about the actual meaning of
casting volatile pointers to non-volatile. Or applied to the example, is there
really a difference from the version above to
do {
synchronize_with_other_thread();
int* buffer_nonvolatile = (int*) buffer; // buffer being qualified volatile
for (int i=0;i<8;++i) {
sum += buffer_nonvolatile;
}
} while (sum < THRESHOLD);
The problem I see here is that only the contents of buffer[] are volatile, not
its address. *buffer_nonvolatile is for sure invariant for all while-iterations
(if it is static), and the semantics are identical with just not declaring
buffer[] volatile.
I'd really be glad if someone could comment on that, either correcting or
confirming my assumptions.
Best regards,
Markus
Recently I stumbled upon an interesting problem related to thread-parallel
programming in C (and similarily C++). As an example assume a simple "buffer"
array of size 8, e.g. with static lifetime and exteral linkage. One thread fills
the buffer structure, and the other in some way evaluates its contents, e.g.
do {
synchronize_with_other_thread();
for (int i=0;i<8;++i) {
sum += buffer;
}
} while (sum < THRESHOLD);
From what I understand, the programming standard (C99) assumes a single serial
instruction stream. To ensure each loop iteration retrieves current values from
buffer[], one has to make it volatile, which prevents a lot of useful
optimizations by the compiler. In any other case, the compiler would be
_allowed_ to keep the whole contents of buffer[] in the register file and
operate on that.
It is clear, that in practice various ways exist to avoid "volatile" for buffer
[] and allow the compiler to optimize mor aggressively. Especially, one could do
the evaluation (here the sum) in a non-inline function. For data with external
linkage, any library call inside "synchronize_with_other_thread()" will be
sufficient, too, as a compiler cannot assure buffer[] is not affected by it. But
both solutions rely on the inablilities of compiler and linker, and would not be
guaranteed to work with an "omniscient" compiler that is allowed to perform
inter-procedure optimizations.
Clearly related to this, I could not find out about the actual meaning of
casting volatile pointers to non-volatile. Or applied to the example, is there
really a difference from the version above to
do {
synchronize_with_other_thread();
int* buffer_nonvolatile = (int*) buffer; // buffer being qualified volatile
for (int i=0;i<8;++i) {
sum += buffer_nonvolatile;
}
} while (sum < THRESHOLD);
The problem I see here is that only the contents of buffer[] are volatile, not
its address. *buffer_nonvolatile is for sure invariant for all while-iterations
(if it is static), and the semantics are identical with just not declaring
buffer[] volatile.
I'd really be glad if someone could comment on that, either correcting or
confirming my assumptions.
Best regards,
Markus