Reputation: 12371
As we know, the compiler or the CPU may reorder the execution as they want, only if they follow the as-if rule. For example, if we have such a piece of code:
C = A + B;
D = E + F;
The compiler or the CPU may execute D = E + F
before C = A + B
. I can understand that.
Today, my colleague tried to build a log library with C++. His idea is to use constructor & destructor to make some logs with the help of some overloading stream functions, such as operator<<()
.
Basically, he offered such a kind of class:
class Log
{
public:
Log() { streamObj << "start: " << getCurrentTime() << endl; }
~Log() { streamObj << "end: " << getCurrentTime() << endl; }
};
Now, I'm the user of the log library. My colleague told me that I could use the library as below:
void func()
{
Log log;
// do something1
// do something2
// do something3
return;
}
So when the first line is executed, the constructor is invoked so I can get a "start" in the log. And when the function returns, the destructor of log
will be invoked so I will get an end
in the log. With the help of the object log
, we can clearly find the start of the function and the end of the function.
That sounds clear and great.
However, as I mentioned at the beginning of the post, the machine may do some reordering as it wants. So I'm now wondering if it is possible that the constructor of log
is invoked later than we think and/or the destructor of the log
is invoked sooner than we think so that the log
can't work as we expected. I mean, the code of func
did look as above but when it was compiled or was executed, the real order became:
// do something1
Log log;
// do something2
// call the destructor of `log`
// do something3
return
BTW, the stream in the class Log
is directed into somewhere else, such as a file, or a shared memory or a TCP socket.
So am I reasonable? Or this kind of reordering will never happen? If it may happen, is there some technique to forbid this kind of reordering or some technique to offer an useable log library which can tell us the start and the end of any function?
In fact, I've heard some new technique in C++11, such as std::atomic
and std::atomic_thread_fence
. As my understanding, if this kind of reordering is possible, what I need might be... a fence?
class Log
{
public:
Log() {
streamObj << "start: " << getCurrentTime() << endl;
// build a fence here!
}
~Log() { streamObj << "end: " << getCurrentTime() << endl; }
};
I really don't know if it is possible...
About side effects/observable behavior
As my understanding, this is a side effect/observable behavior:
A = B + C
Why? Because the value of A
is changed.
Now, what if I code like this:
void func()
{
Log log;
A = B + C;
}
So the order could be:
log
A = B + C
log
However, if the order becomes:
A = B + C
log
log
I think that would be fine. Why? Because the value of A
, which is about a side effect/observable behavior, is NOT modified. No matter which order we take, its value would be always B + C
.
Am I right? If I'm right, I think it means that the Log
won't work as expected.
UPDATE
A = B + C
has a side effect, which is that the value of A
is changed, but it isn't an observable behavior.
Upvotes: 1
Views: 196