Reputation: 129
I am not quite familiar with static variable in a function in C++. I know it is only initialize once. e.g.
void func(map<int, int>& m){
static int a = m[0];
}
I would expect static int a = m[0] would only execute once when I first call the function, but it seems to me that every time I call the function, it cost some time to execute m[0]. Here is one test I do. the program itself does not make sense (it will always return the same number) but just want to show the performance as a example
double getDirect(int i, map<int, double>& m){
static double res = i;
return res;
}
double getFromMap(int i, map<int, double>& m){
static double res = m[i];
return res;
}
in main function
clock_t t;
t = clock();
long long i = 0;
map<int, double> m;
for(int i = 0; i < 10; i++){
m.insert(make_pair(i, i));
}
#pragma omp parallel for private(i)
for(i = 0; i < size; i++){
for(int j = 0; j < 10; j++)
double res = getDirect(j, m);
}
t = clock()-t;
cout << " It cost " << t << " clicks (" << ((float)t) / CLOCKS_PER_SEC << " sec) to run." << endl;
t = clock();
#pragma omp parallel for private(i)
for(i = 0; i < size; i++){
for(int j = 0; j < 10; j++)
double res = getFromMap(j, m);
}
t = clock()-t;
cout << " It cost " << t << " clicks (" << ((float)t) / CLOCKS_PER_SEC << " sec) to run." << endl;
I would expect the time between the two would be really similar. But the result is
It cost 14055 clicks (14.055 sec) to run. It cost 150636 clicks (150.636 sec) to run.
The getFromMap is much slower. Is this because m[i] is still executed every time? If not, what is the reason? If so, what is a good way to by-pass this performance cost? Thanks.
Here is some follow up. I get the assembly code\
static double res = m[i];
000000013F83D454 mov eax,104h
000000013F83D459 mov eax,eax
000000013F83D45B mov ecx,dword ptr [_tls_index (013F8503C8h)]
000000013F83D461 mov rdx,qword ptr gs:[58h]
000000013F83D46A mov rcx,qword ptr [rdx+rcx*8]
000000013F83D46E mov eax,dword ptr [rax+rcx]
000000013F83D471 cmp dword ptr [res+0Ch (013F85035Ch)],eax
000000013F83D477 jle getFromMap+0A9h (013F83D4B9h)
000000013F83D479 lea rcx,[res+0Ch (013F85035Ch)]
000000013F83D480 call _Init_thread_header (013F831640h)
000000013F83D485 cmp dword ptr [res+0Ch (013F85035Ch)],0FFFFFFFFh
000000013F83D48C jne getFromMap+0A9h (013F83D4B9h)
000000013F83D48E lea rdx,[i]
000000013F83D495 mov rcx,qword ptr [m]
000000013F83D49C call std::map<int,double,std::less<int>,std::allocator<std::pair<int const ,double> > >::operator[] (013F831320h)
000000013F83D4A1 movsd xmm0,mmword ptr [rax]
000000013F83D4A5 movsd mmword ptr [res (013F850360h)],xmm0
000000013F83D4AD lea rcx,[res+0Ch (013F85035Ch)]
000000013F83D4B4 call _Init_thread_footer (013F8314C4h)
It did seem that m[i] is still called after the first time. I also did a single thread test with a smaller size, but the difference is even larger (a ratio about 100).
Any idea on how to set up visual studio 2012 so that it stops calling m[i] after the first time? Thanks so much for your help
Upvotes: 3
Views: 1706
Reputation: 217135
From quick-bench
It seems that getDirect
is inlined contrary to getFromMap
.
Probably due to size of code to call m[i]
in the initialization case.
(version with at
is even faster that your getDirect
Demo :) )
Upvotes: 2
Reputation: 62563
Having function-local static variables would ever so slightly penalize you. Since static values need to be initialized once, and only once, there has to be a flag which would be checked on every function execution, set to 'yes, please initialize' before the function is entered first time and reset to 'no, please no longer initialize' after such initialization has happened.
Otherwise, compiler would have no way to guarantee you a single initialization.
However, compiler guarantees you that once the variable has been initialized, no further initialization will take place, and the declaration will be skipped: https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables
We can look at a sample codegen to confirm this assumption with the compiler: https://gcc.godbolt.org/z/Q-Dni_ It is clear that if the variable is already initialized, a declaration is skipped and k
is not called. Conclusion: your difference in time comes from somewhere else.
Upvotes: 7