Can unused function-arguments become optimized away? - c++

I'm trying to ensure an object - wrapped by a shared_ptr - is alive as long as a function is executed by passing it as value. However inside the function the object is not used at all, so I just want to use it for 'pinning':
void doSomething(std::shared_ptr<Foo>) {
// Perform some operations unrelated to the passed shared_ptr.
}
int main() {
auto myFoo{std::make_shared<Foo>()};
doSomething(std::move(myFoo)); // Is 'myFoo' kept alive until doSomething returns?
return 0;
}
I did check the behavior on different optimization-levels (GCC) and it seems that it works as intended, however I don't know whether the compiler still may optimize it away in certain scenarios.

You don't need to worry - the lifetime of the function argument at the call site is guaranteed to survive the function call. (This is why things like foo(s.c_str()) for a std::string s work.)
A compiler is not allowed to break that rule, subject to as if rule flexibility.

This very much depends on what the body of doSomething and Foo will actually look like. For instance, consider the following example:
struct X
{
~X() { std::cout << "2"; };
};
void f(std::shared_ptr<X>) { std::cout << "1"; }
int main()
{
auto p = std::make_shared<X>();
f(std::move(p));
}
This program has the very same observable effect as:
int main()
{
std::cout << "12";
}
and the order "12" is guaranteed. So, in the generated assembly, there may be no shared pointer used. However, most compilers will likely not perform such aggressive optimizations since there are dynamic memory allocations and virtual function calls involved internally, which is not that easy to optimize away.

The compiler could optimise away the copying of an object into a function argument if the function is being inlined and if the copying has no side effects.
Copying a shared_ptr increments its reference count so it does have side effects so the compiler can't optimise it away (unless the compiler can prove to itself that not modifying the reference count has no effect on the program).

Related

Is it safe to use a reference to a const when the referenced variable changes from outside?

Example:
class A {
const int &x;
public:
A(const int &x_) : x{x_} {}
void foo() {
std::cout << x << std::endl;
}
}
int main() {
int x{0};
A a{x};
a.foo() // prints "0"
x = 1;
a.foo() // prints "0" or "1"??
}
Can I safely assume that A::foo() will always read the correct value or is something like a volatile modifier needed?
Is it safe to use a reference to a const when the referenced variable changes from outside?
Yes.
Can I safely assume that A::foo() will always read the correct value
Yes... unless you assume the value to be other than what the correct one is. 1 would be the correct value in the latter function call.
In a singly-threaded program, like the one you have shown, this is perfectly safe. Your first call to foo::A() will execute in its entirety before x = 1, which executed before the next call to foo::A(). The second call is always guaranteed to read 1, unless you have some undefined behavior somewhere else in your program.
In a multi-threaded program, the const only requires that foo not change the value of x, not that other threads don't change its value. Thus, something like this might require synchronization to read this properly. An std::mutex and/or std::atomic might help you with that part of it.
As to using volatile, volatile isn't really for what you seem to think it is for. It is generally for cases when the variable can be changed outside the context of the compiler. Using volatile here won't necessarily make your read safer. Use standard synchronization methods instead to ensure your read is safe.
Oh, and by the way, to answer this question:
// prints "0" or "1"??
This prints 1. You passed it by reference, so changing the value in main() will change the value stored in foo. That's one of the advantages, if you will, of passing by reference.
There are very few uses of volatile; most (not all though) relate to object state changing asynchronously or in ways unknowable by the compiler.
Calling a function in your own code is not an unknowable flow. It's very well known that a called function can change the values of any object that can be designated by the function (with or without using the object name) and whose value can legally be changed (that isn't const).

Can you call the destructor without calling the constructor?

I've been trying not to initialize memory when I don't need to, and am using malloc arrays to do so:
This is what I've run:
#include <iostream>
struct test
{
int num = 3;
test() { std::cout << "Init\n"; }
~test() { std::cout << "Destroyed: " << num << "\n"; }
};
int main()
{
test* array = (test*)malloc(3 * sizeof(test));
for (int i = 0; i < 3; i += 1)
{
std::cout << array[i].num << "\n";
array[i].num = i;
//new(array + i) i; placement new is not being used
std::cout << array[i].num << "\n";
}
for (int i = 0; i < 3; i += 1)
{
(array + i)->~test();
}
free(array);
return 0;
}
Which outputs:
0 ->- 0
0 ->- 1
0 ->- 2
Destroyed: 0
Destroyed: 1
Destroyed: 2
Despite not having constructed the array indices. Is this "healthy"? That is to say, can I simply treat the destructor as "just a function"?
(besides the fact that the destructor has implicit knowledge of where the data members are located relative to the pointer I specified)
Just to specify: I'm not looking for warnings on the proper usage of c++. I would simply like to know if there's things I should be wary of when using this no-constructor method.
(footnote: the reason I don't wanna use constructors is because many times, memory simply does not need to be initialized and doing so is slow)
No, this is undefined behaviour. An object's lifetime starts after the call to a constructor is completed, hence if a constructor is never called, the object technically never exists.
This likely "seems" to behave correctly in your example because your struct is trivial (int::~int is a no-op).
You are also leaking memory (destructors destroy the given object, but the original memory allocated via malloc still needs to be freed).
Edit: You might want to look at this question as well, as this is an extremely similar situation, simply using stack allocation instead of malloc. This gives some of the actual quotes from the standard around object lifetime and construction.
I'll add this as well: in the case where you don't use placement new and it clearly is required (e.g. struct contains some container class or a vtable, etc.) you are going to run into real trouble. In this case, omitting the placement-new call is almost certainly going to gain you 0 performance benefit for very fragile code - either way, it's just not a good idea.
Yes, the destructor is nothing more than a function. You can call it at any time. However, calling it without a matching constructor is a bad idea.
So the rule is: If you did not initialize memory as a specific type, you may not interpret and use that memory as an object of that type; otherwise it is undefined behavior. (with char and unsigned char as exceptions).
Let us do a line by line analysis of your code.
test* array = (test*)malloc(3 * sizeof(test));
This line initializes a pointer scalar array using a memory address provided by the system. Note that the memory is not initialized for any kind of type. This means you should not treat these memory as any object (even as scalars like int, let aside your test class type).
Later, you wrote:
std::cout << array[i].num << "\n";
This uses the memory as test type, which violates the rule stated above, leading to undefined behavior.
And later:
(array + i)->~test();
You used the memory a test type again! Calling destructor also uses the object ! This is also UB.
In your case you are lucky that nothing harmful happens and you get something reasonable. However UBs are solely dependent on your compiler's implementation. It can even decide to format your disk and that's still standard-conforming.
That is to say, can I simply treat the destructor as "just a function"?
No. While it is like other functions in many ways, there are some special features of the destructor. These boil down to a pattern similar to manual memory management. Just as memory allocation and deallocation need to come in pairs, so do construction and destruction. If you skip one, skip the other. If you call one, call the other. If you insist upon manual memory management, the tools for construction and destruction are placement new and explicitly calling the destructor. (Code that uses new and delete combine allocation and construction into one step, while destruction and deallocation are combined into the other.)
Do not skip the constructor for an object that will be used. This is undefined behavior. Furthermore, the less trivial the constructor, the more likely that something will go wildly wrong if you skip it. That is, as you save more, you break more. Skipping the constructor for a used object is not a way to be more efficient — it is a way to write broken code. Inefficient, correct code trumps efficient code that does not work.
One bit of discouragement: this sort of low-level management can become a big investment of time. Only go this route if there is a realistic chance of a performance payback. Do not complicate your code with optimizations simply for the sake of optimizing. Also consider simpler alternatives that might get similar results with less code overhead. Perhaps a constructor that performs no initializations other than somehow flagging the object as not initialized? (Details and feasibility depend on the class involved, hence extend outside the scope of this question.)
One bit of encouragement: If you think about the standard library, you should realize that your goal is achievable. I would present vector::reserve as an example of something that can allocate memory without initializing it.
You currently have UB as you access field from non-existing object.
You might let field uninitialized by doing a constructor noop. compiler might then easily doing no initialization, for example:
struct test
{
int num; // no = 3
test() { std::cout << "Init\n"; } // num not initalized
~test() { std::cout << "Destroyed: " << num << "\n"; }
};
Demo
For readability, you should probably wrap it in dedicated class, something like:
struct uninitialized_tag {};
struct uninitializable_int
{
uninitializable_int(uninitialized_tag) {} // No initalization
uninitializable_int(int num) : num(num) {}
int num;
};
Demo

C++ transfer ownership of a struct to a function

Is it possible to transfer the ownership of a local variable in C++ (or C++0x) to a function, leaving it undefined after the return, so optimizations can be done?
struct A {
int a[100000];
};
int func(A& s){
//s should now be "owned" by func and be undefined in the calling function
s.a[2] += 4;
return s.a[2];
}
int main(){
A s;
printf("%d\n", func(s));
//s is now undefined
}
I want the function "func" to be optimized to simply return s.a[2]+4, but not change the actual value in memory, just like if "s" had been a local variable in "func".
If it can't be done in standard C++, is it possible with some extension in g++?
No, it's not possible, either Standard or through an extension, and that's because there is no optimization value. Compilers can trivially prove that there are no more references to local variables in such situations. Failing all else, you could trivially mimic the effect by doing
int main() {
{
A s;
printf("%d\n", func(s));
}
}
Being able to do that kind of thing would be hideously dangerous for no benefit.
Leave optimization to compiler - in simple cases it probably can do it.
Don't forget - premature optimization is the root of all evil.
I would expect a reasonable optimizing compiler to be able to make an optimization like this without any special hints if the local variable s is really not referenced after the function returns, assuming the variable and the function were in the same compilation unit or you had some form of link time code generation enabled.
You might be able to help the optimizer by scoping your local variable to make it explicit that it can't be accessed beyond the one reference after the function call:
int main() {
{
A s;
printf("%d\n", func(s));
} //s is now undefined
}
If you have a specific case that doesn't appear to be optimized as effectively as you think it should then perhaps you can provide more detail about your situation. I'm a little unclear what exactly you mean by the function 'owning' the local variable in this case since you do actually want to access it after the function returns.
You are mixing distinct issues like ownership, stack unwinding and value parameters.
Is it possible to transfer the ownership of a local variable
No. Local variables are local to the scope defined and nobody else can see them or manipulate them in any fashion. You can pass to a function the value of a local variable, a reference to a local variable, or the address of a local variable. When a reference or a pointer to a local variable is passed then the calee can manipulate the content of the variable, but by no mean can he influence the scope of the variable in the caller frame. The most common transfer of 'ownership' implies transfer a pointer by value and relying on the callee to take ownership of the allocated memory. All forms of variable passing (by value, by ref, by pointer) can handle this, the issue of memory allocation ownership is distinct.
I want the function "func" to be optimized to simply return s.a[2]+4,
but not change the actual value in memory,
Then do exactly that, why make it any more complicated?
int func(const A& s){
return s.a[2] + 4;
}
This will do exactly what you describe, but is very unlikely this is what you're actually asking. Making a leap of faith and invoking some psychic powers one would guess that what you're really asking is can an object be at the same time changed in the callee scope and left intact in the caller scope? The answer is obviously no, because memory cannot have different values depending on the caller. You can pass a copy of the object (pass by value) which would allow the caller to manipulate its own copy of the object as it sees fit w/o affecting the original one from the caller scope. OR you can pass const reference, preventing the callee from modifying it, and have the callee copy out whatever it needs to to modify.
I'm surprised that nobody else has posted this, but it sounds like what's closest to what you want is simply to remove the reference, and put the function call in a seperate scope
int func(A s){ //removed the &
//s is "owned" by func and changes don't touch anyone else's A objects
s.a[2] += 4;
return s.a[2];
}
int main(){
{
A s;
printf("%d\n", func(s));
// s hasn't changed, func had it's own copy.
} // s goes out of scope and is deleted
//s is now undefined
}
You could also have it pointer based if you prefer that. The code sample below changes your func(A) to take a reference to a pointer so it can be turned safely to null after deallocation.
It's a little bit hooky but it is possible if you absolutely require it. It may be a premature optimization that can be avoided however.
int func(A*& s) {
int retVal = s->a[2] + 4;
delete s;
s = NULL;
return retVal;
}
int main() {
A* s = new A();
printf("%d\n", func(s));
}
You can use the old C++03 auto_ptr for this. The calling function is left with an auto_ptr that points to nullptr, while the function took over and deleted s.
int func(std::auto_ptr<A> s){
//s is "owned" by func and be undefined in the calling function
s.a[2] += 4;
return s.a[2];
}
int main(){
std::auto_ptr<A> s = new A();
printf("%d\n", func(s)); //destructive copy of pointer
//s is now nullptr
}

C++ const-reference semantics?

Consider the sample application below. It demonstrates what I would call a flawed class design.
#include <iostream>
using namespace std;
struct B
{
B() : m_value(1) {}
long m_value;
};
struct A
{
const B& GetB() const { return m_B; }
void Foo(const B &b)
{
// assert(this != &b);
m_B.m_value += b.m_value;
m_B.m_value += b.m_value;
}
protected:
B m_B;
};
int main(int argc, char* argv[])
{
A a;
cout << "Original value: " << a.GetB().m_value << endl;
cout << "Expected value: 3" << endl;
a.Foo(a.GetB());
cout << "Actual value: " << a.GetB().m_value << endl;
return 0;
}
Output:
Original value: 1
Expected value: 3
Actual value: 4
Obviously, the programmer is fooled by the constness of b. By mistake b points to this, which yields the undesired behavior.
My question: What const-rules should you follow when designing getters/setters?
My suggestion: Never return a reference to a member variable if it can be set by reference through a member function. Hence, either return by value or pass parameters by value. (Modern compilers will optimize away the extra copy anyway.)
Obviously, the programmer is fooled by the constness of b
As someone once said, You keep using that word. I do not think it means what you think it means.
Const means that you cannot change the value. It does not mean that the value cannot change.
If the programmer is fooled by the fact that some other code else can change something that they cannot, they need a better grounding in aliasing.
If the programmer is fooled by the fact that the token 'const' sounds a bit like 'constant' but means 'read only', they need a better grounding in the semantics of the programming language they are using.
So if you have a getter which returns a const reference, then it is an alias for an object you don't have the permission to change. That says nothing about whether its value is immutable.
Ultimately, this comes down to a lack of encapsulation, and not applying the Law of Demeter. In general, don't mutate the state of other objects. Send them a message to ask them to perform an operation, which may (depending on their own implementation details) mutate their state.
If you make B.m_value private, then you can't write the Foo you have. You either make Foo into:
void Foo(const B &b)
{
m_B.increment_by(b);
m_B.increment_by(b);
}
void B::increment_by (const B& b)
{
// assert ( this != &b ) if you like
m_value += b.m_value;
}
or, if you want to ensure that the value is constant, use a temporary
void Foo(B b)
{
m_B.increment_by(b);
m_B.increment_by(b);
}
Now, incrementing a value by itself may or may not be reasonable, and is easily tested for within B::increment_by. You could also test whether &m_b==&b in A::Foo, though once you have a couple of levels of objects and objects with references to other objects rather than values (so &a1.b.c == &a2.b.c does not imply that &a1.b==&a2.b or &a1==&a2), then you really have to just be aware that any operation is potentially aliased.
Aliasing means that incrementing by an expression twice is not the same as incrementing by the value of the expression the first time you evaluated it; there's no real way around it, and in most systems the cost of copying the data isn't worth the risk of avoiding the alias.
Passing in arguments which have the least structure also works well. If Foo() took a long rather than an object which it has to get a long from, then it would not suffer aliasing, and you wouldn't need to write a different Foo() to increment m_b by the value of a C.
I propose a slightly different solution to this that has several advantages (especially in an every increasing, multi-threaded world). Its a simple idea to follow, and that is to "commit" your changes last.
To explain via your example you would simply change the 'A' class to:
struct A
{
const B& GetB() const { return m_B; }
void Foo(const B &b)
{
// copy out what we are going to change;
int itm_value = m_b.m_value;
// perform operations on the copy, not our internal value
itm_value += b.m_value;
itm_value += b.m_value;
// copy over final results
m_B.m_value = itm_value ;
}
protected:
B m_B;
};
The idea here is to place all assignment to memory viewable above the current function at the end, where they pretty much can't fail. This way, if an error is thrown (say there was a divide in the middle of those 2 operations, and if it just happens to be 0) in the middle of the operation, then we aren't left with half baked data in the middle.
Furthermore, in a multi-threading situation, you can do all of the operation, and then just check at the end if anything has changed before your "commit" (an optimistic approach, which will usually pass and usually yield much better results than locking the structure for the entire operation), if it has changed, you simply discard the values and try again (or return a value saying it has failed if there is something it can do instead).
On top of this, the compiler can usually optimise this better, because it is no longer required to write the variables being modified to memory (we are only forcing one read of the value to be changed and one write). This way, the compiler has the option of just keeping the relevant data in a register, saves L1 cache access if not cache misses. Otherwise the compiler will probably make it write to the memory as it doesn't know what aliasing might be taking place (so it can't ensure those values stay the same, if they are all local, it knows it can't be aliasing because the current function is the only one that knows about it).
There's a lot of different things that can happen with the original code posted. I wouldn't be surprised if some compilers (with optimizations enabled) will actually produce code that produces the "expected" result, whereas others won't. All of this is simply because the point at which variables, that aren't 'volatile', are actually written/read from memory isn't well defined within the c++ standards.
The real problem here is atomicity. The precondition of the Foo function is that it's argument doesn't change while in use.
If e.g. Foo had been specified with a value-argument i.s.o. reference argument, no problem would have shown.
Frankly, A::Foo() rubs me the wrong way more than your original problem. Anyhow I look at it, it must be B::Foo(). And inside B::Foo() check for this wouldn't be that outlandish.
Otherwise I do not see how one can specify a generic rule to cover that case. And keep teammates sane.
From past experience, I would treat that as a plain bug and would differentiate two cases: (1) B is small and (2) B is large. If B is small, then simply make A::getB() to return a copy. If B is large, then you have no choice but to handle the case that objects of B might be both rvalue and lvalue in the same expression.
If you have such problems constantly, I'd say simpler rule would be to always return a copy of an object instead of a reference. Because quite often, if object is large, then you have to handle it differently from the rest anyway.
My stupid answer, I leave it here just in case someone else comes up with the same bad idea:
The problem is I think that the object referred to is not const (B const & vs const B &), only the reference is const in your code.

Returning Large Objects in Functions

Compare the following two pieces of code, the first using a reference to a large object, and the second has the large object as the return value. The emphasis on a "large object" refers to the fact that repeated copies of the object, unnecessarily, is wasted cycles.
Using a reference to a large object:
void getObjData( LargeObj& a )
{
a.reset() ;
a.fillWithData() ;
}
int main()
{
LargeObj a ;
getObjData( a ) ;
}
Using the large object as a return value:
LargeObj getObjData()
{
LargeObj a ;
a.fillWithData() ;
return a ;
}
int main()
{
LargeObj a = getObjData() ;
}
The first snippet of code does not require copying the large object.
In the second snippet, the object is created inside the function, and so in general, a copy is needed when returning the object. In this case, however, in main() the object is being declared. Will the compiler first create a default-constructed object, then copy the object returned by getObjData(), or will it be as efficient as the first snippet?
I think the second snippet is easier to read but I am afraid it is less efficient.
Edit: Typically, I am thinking of cases LargeObj to be generic container classes that, for the sake of argument, contains thousands of objects inside of them. For example,
typedef std::vector<HugeObj> LargeObj ;
so directly modifying/adding methods to LargeObj isn't a directly accessible solution.
The second approach is more idiomatic, and expressive. It is clear when reading the code that the function has no preconditions on the argument (it does not have an argument) and that it will actually create an object inside. The first approach is not so clear for the casual reader. The call implies that the object will be changed (pass by reference) but it is not so clear if there are any preconditions on the passed object.
About the copies. The code you posted is not using the assignment operator, but rather copy construction. The C++ defines the return value optimization that is implemented in all major compilers. If you are not sure you can run the following snippet in your compiler:
#include <iostream>
class X
{
public:
X() { std::cout << "X::X()" << std::endl; }
X( X const & ) { std::cout << "X::X( X const & )" << std::endl; }
X& operator=( X const & ) { std::cout << "X::operator=(X const &)" << std::endl; }
};
X f() {
X tmp;
return tmp;
}
int main() {
X x = f();
}
With g++ you will get a single line X::X(). The compiler reserves the space in the stack for the x object, then calls the function that constructs the tmp over x (in fact tmp is x. The operations inside f() are applied directly on x, being equivalent to your first code snippet (pass by reference).
If you were not using the copy constructor (had you written: X x; x = f();) then it would create both x and tmp and apply the assignment operator, yielding a three line output: X::X() / X::X() / X::operator=. So it could be a little less efficient in cases.
Use the second approach. It may seem that to be less efficient, but the C++ standard allows the copies to be evaded. This optimization is called Named Return Value Optimization and is implemented in most current compilers.
Yes in the second case it will make a copy of the object, possibly twice - once to return the value from the function, and again to assign it to the local copy in main. Some compilers will optimize out the second copy, but in general you can assume at least one copy will happen.
However, you could still use the second approach for clarity even if the data in the object is large without sacrificing performance with the proper use of smart pointers. Check out the suite of smart pointer classes in boost. This way the internal data is only allocated once and never copied, even when the outer object is.
The way to avoid any copying is to provide a special constructor. If you
can re-write your code so it looks like:
LargeObj getObjData()
{
return LargeObj( fillsomehow() );
}
If fillsomehow() returns the data (perhaps a "big string" then have a constructor that takes a "big string". If you have such a constructor, then the compiler will very likelt construct a single object and not make any copies at all to perform the return. Of course, whether this is userful in real life depends on your particular problem.
A somewhat idiomatic solution would be:
std::auto_ptr<LargeObj> getObjData()
{
std::auto_ptr<LargeObj> a(new LargeObj);
a->fillWithData();
return a;
}
int main()
{
std::auto_ptr<LargeObj> a(getObjData());
}
Alternatively, you can avoid this issue all together by letting the object get its own data, i. e. by making getObjData() a member function of LargeObj. Depending on what you are actually doing, this may be a good way to go.
Depending on how large the object really is and how often the operation happens, don't get too bogged down in efficiency when it will have no discernible effect either way. Optimization at the expense of clean, readable code should only happen when it is determined to be necessary.
The chances are that some cycles will be wasted when you return by copy. Whether it's worth worrying about depends on how large the object really is, and how often you invoke this code.
But I'd like to point out that if LargeObj is a large and non-trivial class, then in any case its empty constructor should be initializing it to a known state:
LargeObj::LargeObj() :
m_member1(),
m_member2(),
...
{}
That wastes a few cycles too. Re-writing the code as
LargeObj::LargeObj()
{
// (The body of fillWithData should ideally be re-written into
// the initializer list...)
fillWithData() ;
}
int main()
{
LargeObj a ;
}
would probably be a win-win for you: you'd have the LargeObj instances getting initialized into known and useful states, and you'd have fewer wasted cycles.
If you don't always want to use fillWithData() in the constructor, you could pass a flag into the constructor as an argument.
UPDATE (from your edit & comment) : Semantically, if it's worthwhile to create a typedef for LargeObj -- i.e., to give it a name, rather than referencing it simply as typedef std::vector<HugeObj> -- then you're already on the road to giving it its own behavioral semantics. You could, for example, define it as
class LargeObj : public std::vector<HugeObj> {
// constructor that fills the object with data
LargeObj() ;
// ... other standard methods ...
};
Only you can determine if this is appropriate for your app. My point is that even though LargeObj is "mostly" a container, you can still give it class behavior if doing so works for your application.
Your first snippet is especially useful when you do things like have getObjData() implemented in one DLL, call it from another DLL, and the two DLLs are implemented in different languages or different versions of the compiler for the same language. The reason is because when they are compiled in different compilers they often use different heaps. You must allocate and deallocate memory from within the same heap, else you will corrupt memory. </windows>
But if you don't do something like that, I would normally simply return a pointer (or smart pointer) to memory your function allocates:
LargeObj* getObjData()
{
LargeObj* ret = new LargeObj;
ret->fillWithData() ;
return ret;
}
...unless I have a specific reason not to.