Very Strange Constructor behaviour
I have the following situation
Class A
{
public:
A(/* x number of arguments */);
};
A::A(/*.....*/)
{
std::set<__int64> tmpSet;
tmpSet.insert( /*<num>*/ ); // repeat statement for some 30 K or more
tmpSet.insert( /*<num>*/ );
//-----repeat statement for some 30K or more times
}
main()
{
A obj(/*开发者_Python百科arguments */); // Run time Error : Stack over flow
}
What I observed , when I reduced the number of inserts in the constructor the Error disappeared . I wonder how did the compiler knew at run time to calculate the size of the container before the object was created .
std::set.insert()
returns a std::pair<iterator, bool>
. The compiler has to allocate stack memory to store the return value, as it is too large to fit into registers.
A good optimisation would be to reuse this stack memory for the next call. It looks like your compiler does not do so.
Hence, your constructor requires a huge amount of stack space. At run time, it tries to allocate this, and fails.
Solutions:
- Use a bigger stack. Look for linker options to set this.
- Turn on a compiler optimisation so it will reuse the stack space for the next insert.
- Switch to a different compiler that performs this optimisation
- Put the numbers in a static array, which does not use stack space, and copy them into the set.
Note that it is very dependent on the compiler you use. However, at this time, it's unknown to me which compiler you used.
EDIT: Note that this is pure speculation, as we don't know which compiler was used.
My guess is that your constructor generates a compiled code that does not fit in your program stack. You should consider exchanging your insert statements with a loop, which drastically reduces the function size on the stack. Another option is to put the data to be inserted into a static array, and then loop over it in the constructor to produce the set.
it has nothing to do with the container, what's happening is that the 30,000 arguments you're passing take up space on the stack, and you're going over a (reasonable) limit. When you reduce the use of those paramaters inside the constructor, the compiler is optimising away the unused ones.
eg. if you pass (a,b,c) into the constructor but never reference those parameter variables, the compiler can remove them completely - no point having something that's never used. In your case, this reduces the amount of stack used and allows it to work.
//repeat statement for some 30K or more times
Well, that's probably a bad idea for any number of reasons. Assuming that isn't 30K of hand-typed code, stick the data in a file or resource and read it in a loop.
The actual 'data' for the std::set
is heap-allocated rather than stack allocated, so that's unlikely to be the problem.
But, the reason for stack overflow is probably this.
For many compilers (and many calling conventions) parameters are passed on the stack.
The parameters to insert
are pushed on the stack as required. But, the compiler optimizes all the pops into a single 'big pop' at the end of the function. Unfortunately, your compiler hasn't realised that the # of pushed arguments is ridiculously big...
Here's pseudo-code for what the compiler does (ignoring results from insert)
Before optimize:
PUSH arg1
CALL tmpset.insert
ADD 4 to SP // pop arg1
PUSH arg2
CALL tmpset.insert
ADD 4 to SP // pop arg2
PUSH arg3
CALL tmpset.insert
ADD 4 to SP // pop arg3
After optimize:
PUSH arg1
CALL tmpset.insert
PUSH arg2
CALL tmpset.insert
PUSH arg3
CALL tmpset.insert
ADD 12 to SP // pop arg1,arg2,arg3
精彩评论