Just a beginner question.
In the code from cppreference.com, there is a comment saying, "make sure it's a side effect" on the line using std::accumulate.
What is the author's intention in saying this? Is it related to the volatile int sink? (Maybe it means something like "make sure the sink is not optimized by the compiler"?)
Why volatile for sink? I think almost every compiler might know the std::accumulate will change the value of sink.
I thought I knew what volatile and "side-effect" meant, but maybe I'm missing some point!
#include <iostream>
#include <iomanip>
#include <vector>
#include <numeric>
#include <chrono>
volatile int sink;
int main()
{
std::cout << std::fixed << std::setprecision(9) << std::left;
for (auto size = 1ull; size < 1000'000'000ull; size *= 100) {
// record start time
auto start = std::chrono::system_clock::now();
// do some work
std::vector<int> v(size, 42);
// This is the line I mentioned.
sink = std::accumulate(v.begin(), v.end(), 0u); // make sure it's a side effect
// record end time
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> diff = end - start;
std::cout << "Time to fill and iterate a vector of " << std::setw(9)
<< size << " ints : " << diff.count() << " s\n";
}
}
Not necessarily.
C++ has the 'as if' rule. The compiler must generate code that works 'as if' the source code was executed, but it doesn't have to do everything that the source code does, in exactly the same order.
Now look at the sink variable. After the line you mention it's never used again. So it's value has no visible effect on the program. So why should the compiler bother calculating it's value? Because of the as if rule it is perfectly legal for the compiler not to do so.
But there are a few exceptions to the as if rule, and one of them is volatile variables. Reads and writes to volatile variables must occur in exactly the way that the source code says. That's why volatile is important in this code. It forces the compiler to execute the std::accumulate call even though it has no visible effect on the program.
Further reading https://en.cppreference.com/w/cpp/language/as_if
The issue in the code sample given is that the value of sink is never used after it has been calculated. Thus, a compiler would be free to "optimize out" the call to std::accumulate, as doing so would have no observable effect.
Adding the volatile qualifier to sink prevents such an undesired optimization. From another cppreference page (bold emphasis mine):
volatile object - An object whose type is volatile-qualified, or a subobject of a
volatile object, or a mutable subobject of a const-volatile object.
Every access (read or write operation, member function call, etc.) made through a glvalue expression of volatile-qualified type
is treated as a visible side-effect for the purposes of optimization (that is, within a single thread of execution,
volatile accesses cannot be optimized out or reordered with another
visible side effect that is sequenced-before or sequenced-after the
volatile access. …)
The example code referenced is intended to show timing functions.
The C++ standard allows the compiler to look at some code, and remove it, if it believes the result is not used.
The example uses volatile and the side effect to be sure that the timing is not optimized away by the compiler.
Related
I'm trying to understand pointers and I encounter this example.
If I have int MAX_VALUE = 90; the program behaves like I expected. But with const int MAX_VALUE = 90; it doesn't.
What is the explication?
#include <iostream>
using namespace std;
int main()
{
const int MAX_VALUE = 90;
cout << "Create variable in the heap." << endl;
int* a = new int;
*a = 2;
cout << "Point a to MAX_VALUE" << endl;
a = (int*)&MAX_VALUE; // Now a points to MAX_VALUE
cout << "Change content of a" << endl;
*a = 6; // Shouldn't this change the value of MAX_VALUE ??
cout << "Content of a: "<< *a << endl;
cout << "MAX_VALUE: " << MAX_VALUE << endl;
return 0;
}
Result:
Content of a: 6
MAX_VALUE: 90
But shouldn't MAX_VALUE change when I did *a = 6;?
I know you can't change const, but how does C++ handle it?
I have a pointing to MAX_VALUE, how can I change a and not MAX_VALUE?
As others have said, you're exploiting undefined behavior. In your case, your program is able to execute without crashing because your compiler's linker allocated space for the "constant" MAX_VALUE in RAM, and it was allocated in a RAM region whose attributes are not set as "read-only". I.e. it's being placed in writable RAM, even though you've declared it as const.
On another system, MAX_VALUE might be allocated in ROM (e.g. in an embedded system where the entire program is stored in memory-mapped flash space, and never copied to RAM). On such a system, you could try to write to MAX_VALUE all you wanted and it would never change, unless you knew the magic incantation pattern to trigger the flash memory's erase/program operations, in which case, yes it would change, but not to the value you wanted to change it to. (And if you triggered an erase operation, a whole lot of bytes around it would quickly change to all 0xFF's!) But that's really only possible on primitive embedded systems where the ROM address space isn't write-protected by an MMU, MPU, or by its chip-select configuration.
On yet another system, MAX_VALUE might be allocated in RAM, but in a section that's marked as read-only in the MMU/MPU. So even though the hardware would be physically capable of allowing its value to change at runtime, it is configured to trap/catch any attempt to do so, and will crash your program for an illegal access attempt (or other similar wording).
But as I said, on your system, since you can change the value of MAX_VALUE at runtime (bypassing compiler safety by casting away its const attribute), we know that on your system MAX_VALUE is allocated in writable RAM.
But then you're of course asking, how could the line...
cout << "MAX_VALUE: " << MAX_VALUE << endl;
...print 90 when you had previously changed its value to 6? Well, the compiler is allowed to optimize that line by not even performing a read of MAX_VALUE, using instead the initialization value from its definition, since in this compilation unit its initialization value is known. The const qualifier is a promise to the compiler that you won't be changing it, and at the same time, a request to the compiler that it alert you if you ever mistakenly try to change it. But by casting away the const attribute, you're bypassing that safety and the result is undefined behavior according to the C++ standard. In your case, your system will execute the code without crashing for the reasons I mentioned above.
It is because of compiler optimization. Since you have declared it as 'const', the compiler has optimized the "cout" to the actual value 90. So, it will not really use the value stored as MAX_VALUE. But I don't know why it is done even if we switch off the optimization.
If you watch the disassembly, you can see that the real value pushed is '5A' which is hex of 90
The problem is not so much that you are not allowed to change the value of *a but that the assignment of a const int* to an int* is not allowed by the C++ standard. If you remove your c-style cast the compiler will tell you so:
error: invalid conversion from ‘const int*’ to ‘int
15 | a = &MAX_VALUE; // Now a points to MAX_VALUE
| ^~~~~~~~~~
| |
| const int*
The reason that the compiler accepts your original code but produces a result that differs from your expectation is that casting away constness can lead to undefined behaviour (as it does in this situation).
The standard states in [dcl.type.cv] 10.1.7.:
Except that any class member declared mutable (10.1.1) can be modified, any attempt to modify a const object during its lifetime (6.8) results in undefined behavior.
(emphasis added)
And in your code MAX_VALUE refers to a const object, regardless of how you access it (e.g. through a).
Undefined behaviour means that while the compiler accepts the program and turns it into something, it is not defined what the result of this conversion is. For any program that exhibits undefined behaviour, the compiler is allowed to do literally anything it sees fit with your code. See also nasal demons.
As,you make MAX_VALUE as constant integer.You cannot change its value if once its value is specified.
You can learn more about constants here:
http://www.cplusplus.com/doc/tutorial/constants
Remove const from your code and problem will be solved
Constants refer to as fixed values, unlike variables whose value can be altered, constants - as the name implies does not change, they remain constant. Constant must have to be initialized at the time of creating it, and new values cannot be assigned later to it.
You can use the Variables if you want to alter the Value in it.
A variable provides us with named storage that our programs can manipulate and the set of operations that can be applied to the variable.
int c = 2;
int d = std::move(c);
std::cout << "c is: " << c << std::endl;
std::cout << "d is: " << d << std::endl;
this code output:
c is: 2
d is: 2
I thought that after move(c) to d, c will be empty, why does it still have 2 as its value ? Can you anyone please help me explain this ? Thank you.
I thought that after move(c) to d, c will be empty,
Your expectation was mis informed.
why does it still have 2 as its value ?
Fundamental types do not have move constructors. You have simply made a copy. Copying does not modify the source object.
For class types, it would not be safe to assume what the move constructor does exactly, and specifically what state the source object is left in. It is not necessarily guaranteed to be "empty". See the documentation of the class for what it does. If there is no documentation, or documentation doesn't give any guarantees, then you cannot assume anything about the state of the source object.
std::move doesn't move anything! (contrary to it's name). It is exactly equivalent to a static_cast to an rvalue reference type.
That it, it is just a cast to rvalue- more specifically to an xvalue, as opposed to a prvalue. And it is also true that having a cast named move sometimes confuses people. However the intent of this naming is not to confuse, but rather to make your code more readable.
Using the xvalue, we can trigger the right overload and hence, we can use std::swap in such overloads to take the ownership of another object (but aren't required).
For example, a move constructor of a linked list might copy the pointer to the head of the list and store nullptr in the argument instead of allocating and copying individual nodes.
why does it still have 2 as its value
As mentioned std::move doesn't move and the real job of swapping/moving the resources is being performed by the overloads like move constructor and move assignment. std::move tasks is just to cast so that compiler can call the right overload (for example, move constructor in favor of copy constructor) and the actual moving of resources has to be defined by the software developer in their respective overloads. Since, fundamental types like int doesn't have any move constructor, the statement int c = std::move(a); merely copies the value of a to c.
Try this:
#include <iostream>
#include <utility>
void hello(int& a)
{
std::cout << "LVALUE" << std::endl;
}
void hello(int&& a)
{
std::cout << "RVALUE" << std::endl;
}
int main(void)
{
int a = 8;
hello(a);
hello(std::move(a));
return 0;
}
First, as mentioned by #eerorika. Moving fundamental types is equivalent to copying. The reason of this behavior is pretty clear. Move semantic is developed for saving computational resource, and you clearly saved nothing (but wasted something) by clearing the value of an integer variable, which is not going to be further used. Leave it there is the best here.
Second, a "moved" variable is not necessarily "empty" or "cleared". It may be in any status formally, but for standard library objects there are some guarantees: (Quoted from here)
Unless otherwise specified, all standard library objects that have
been moved from are placed in a valid but unspecified state. That is,
only the functions without preconditions, such as the assignment
operator, can be safely used on the object after it was moved from
As a result, you may see a "moved" std::vector contains random values and it is perfectly correct. Never assume such a std::vector is (or is not) empty because it might yields undefined behaviours. More generally, make no assumption (except the status is valid for standard library object) about the status of a object that was moved from.
Is there a standard way to find out what the compiler does to constexpr functions?
(Side note: For debug, every constexpr function is deferred to runtime by default. Why is this sensible? Is there a way to influence this?)
For release it depends on the context. Obviously, for small test settings you can easily inspect the generated machine code, but this cannot be the way to go for a real project.
My current 'workaround' (VC++) is to break somewhere, go to my constexpr function and (try to) inspect the disassembly. If none is there, I conclude that it was all done at compile time.
But it is not 100% reliable this way. (Optimization, etc.)
Only the other way around is certain: If I do find disassembly (and can even break there), I know that it was NOT done at compile time.
It's impossible. constexpr does not guarantee value inlining, you can see this manipulating optimization level here: https://godbolt.org/z/dAoiM-
Only since -O2 everything is inlined and the structure gets dissolved. Below that compiler happily uses runtime evaluation even for code used in constexpr context.
There are no standard language tools to inquire whether compiler applies particular optimization. It all boils down to the as-if rule. If the code behaves the same compiler can do anything to it. The only exception is mandatory RVO and other RVOs (they are allowed to changed observed behaviour.)
That being said. The constexpr is a useful hint. In the linked example if one removes constexpr specifiers even O3 (on recent clang and gcc) does not manage to remove the map.
It's worthwhile optimization-wise to write constexpr functions and data structure, making sure the compiler can optimize, though you cannot force it to.
You can force function to be evaluated in constexpr context, and you can also guard non-constexpr paths to throw, to prevent guaranteed run-time evaluation.
#include <iostream>
#include <vector>
using namespace std;
constexpr int f(int el) {
return el > 0 ? el : throw "error";
}
int main() {
// constexpr auto r = f(-1); // #1 compiler errors that throw is forbidden in
// constexpr, so it went into a non-constexpr path
// and failed
constexpr auto r = f(1); // #2 fine - has to be interpreted in constexpr context
cout << f(1) << '\n'; // #3 fine - can be interpreted in both contexts
try {
cout << f(-1) << '\n'; // # 4 // throws - i.e. runtime evaluation
}
catch (const char* e) {
cout << e << '\n';
}
return 0;
}
As we now have the current C++20 standard, we can use consteval.
From the docs:
consteval - specifies that a function is an immediate function, that is, every call to the function must produce a compile-time constant.
doc
This will fix the problem with constexpr.
I was just trying to learn the new c++17 changes related "auto rules for direct-list-initialization"
Few of the stackoverflow question thread has answers like its not a safe thing to do
Why is direct-list-initialization with auto considered bad or not preferred?
Tried one of the selected answer to understand
#include <typeinfo>
#include <iostream>
struct Foo{};
void eatFoo (const Foo& f){}
int main() {
Foo a;
auto b{a};
eatFoo(b);
std::cout << "a " << typeid(a).name() << '\n';
std::cout << "b " << typeid(b).name() << '\n';
}
But to my surprise it compiled without any warning or compile error
output
a 3Foo
b 3Foo
Program ended with exit code: 0
Does it mean now its safe to use auto for direct initialization
for example like this
auto x { 1 };
"Safe" is in the eye of the beholder.
C++14's behavior was "safe", in that it was well-defined what the result would be. auto var_name{expr} would create an initializer_list of one element.
C++17 makes it so that auto var_name{expr} results in the same type deduction as auto var_name = expr; would have. So long as you expect this to be the case, then it is "safe". But it is no more "safe" than the old behavior.
Then again, this is a language change that is backwards-incompatible with the C++14 behavior. By that standard, it is not "safe", in that a C++14 user will expect your code to be doing something different than it is under a C++17 compiler. So it creates some subtle confusion, which may not be safe.
Then there's the issue that "uniform initialization" is supposed to be "uniform" (ie: performs the same kind of initialization under the same rules in all places), yet auto var_name{expr} will do something very different from auto var_name = {expr}. The latter will still be an initializer_list; the former will be a copy/move. By that token, it is not "safe" to assume that "uniform initialization" is uniform.
Then again, the C++17 behavior is sometimes the thing you really wanted. That initializing a variable from a single value, to you, means to copy/move it. After all, if you had done decltype(expr) var_name{expr}, that's the behavior you'd get. So from that perspective, it could seem to be "safe" behavior.
At the very least, we can say that the situation is more teachable through two rules:
Direct-list-initialization of auto variables means "copy/move the expression into the variable, and if you provide more than one expression, you're using the wrong syntax."
Copy-list-initialization of auto variables means "make an initializer_list, and if you provide values that can't do that, you're using the wrong syntax."
Perhaps the simplicity creates a sense of "safety".
The document that instigated these changes makes a case that you're less likely to get accidental UB by returning a stack-bound initializer_list by sticking to the above rules. That's one definition of "safe".
So it all depends on what you mean by "safe".
The following code:
int main() {
int a, b, c, d, e, f, g;
auto func = [&](){cout << a << b << c << d << e << f << g << endl;};
cout << sizeof(func) << endl;
return 0;
}
outputs 56 compiled with g++ 4.8.2
Since all local variables are stored in the same stack frame, remembering one pointer is sufficient to locate the addresses of all local variables. Why the lambda expression constructs a so big unnamed function object?
I do not understand why you seem surprised.
The C++ Standard gives a set of requirements, and every single implementation is free to pick any strategy that meets the requirements.
Why would an implementation optimize the size of the lambda object ?
Specifically, do you realize how that would tie down the generated code of this lambda to the generated code for the surrounding function ?
It's easy to say Hey! This could be optimized!, but it's much more difficult to actually optimize and make sure it works in all edge cases. So, personally, I much prefer having a simple and working implementation than a botched attempt at optimizing it...
... especially when the work-around is so easy:
struct S { int a, b, c, d, e, f, g; };
int main() {
S s = {};
auto func = [&](){
std::cout << s.a << s.b << s.c << s.d << s.e << s.f << s.g << "\n";
};
std::cout << sizeof(func) << "\n";
return 0;
}
Look Ma: 4 bytes only!
It is legal for a compiler to capture by reference via stack pointer. There is a slight downside (in that offsets have to be added to said stack pointer).
Under the current C++ standard with defects included, you also have to capture reference variables by pseudo-pointer, as the lifetime of the binding must last as long as the referred-to-data, not the reference it directly binds to.
The simpler implementation, where each captured variable corresponds to a constructor argument and class member variable, has the serious advantage that it lines up with "more normal" C++ code. Some work for magic this need be done, but other than that the lambda closure is a bog-standard object instance with an inline operator(). Optimization strategies on "more normal" C++ code will work, bugs are going to be mostly in common with "more normal" code, etc.
Had the compiler writers gone with the stack-frame implementation, probably reference capture of references in that implementation would have failed to work like it did in every other compiler. When the defect was resolved (in favor of it working), the code would have to be changed again. In essence, the compilers that would have used a simpler implementation would almost certainly have had fewer bugs and more working code than those who used a fancy implementation.
With the stack-frame capture, all optimization for a lambda would have to be customized for that lambda. It would be equivalent to a class that captured a void*, does pointer arithmetic on it, and casts the resulting data to typed pointers. That is going to be extremely hard to optimize, as pointer arithmetic tends to block optimization, especially pointer arithmetic between stack variables (which is usually undefined). What is worse is that such pointer arithmetic means that the optimization of stack variable state (eliminating variables, overlapping lifetime, registers) now has to interact with the optimization of lambdas in entangled ways.
Working on such an optimization would be a good thing. As a bonus, because lambda types are tied to compilation units, messing with the implementation of a lambda will not break binary compatibility between compilation units. So you can do such changes relatively safely, once they are a proven stable improvement. However, if you do implement that optimization, you really really will want the ability to revert to the simpler proven one.
I encourage you to provide patches to your favorite open-source compiler to add this functionality.
Because that's how it's implemented. I don't know if the standard says anything about how it should be implemented but I guess it's implementation defined how big a lambda object will be in that situation.
There would be nothing wrong for a compiler to store a single pointer and use the offsets, to do what you suggest, as an optimization. Perhaps some compilers do that, I don't know.