To add to the other responses, there is a hidden cost to using local statics. Because the standard requires that the object only be initialized the first time it is encountered, the compiler must secretly place code around the constructor of those items to check if it is the first encounter or not. So there is the added code and conditional branching.
Code: Select all
#include <string>
#include <iostream>
std::string GetStackString()
{
std::string s = "012345678901234567890123456789";
s = "I have changed the string";
return s;
}
std::string GetStaticString()
{
static std::string s = "012345678901234567890123456789";
s = "I have changed the string";
return s;
}
int main()
{
std::cout << GetStackString() << '\n';
return 0;
}
Local variable compiled code:
Code: Select all
mov QWORD PTR [rsp+8], rcx
sub rsp, 88 ; 00000058H
mov rax, QWORD PTR __security_cookie
xor rax, rsp
mov QWORD PTR __$ArrayPad$[rsp], rax
mov DWORD PTR $T1[rsp], 0
lea rdx, OFFSET FLAT:$SG36021
lea rcx, QWORD PTR s$[rsp]
call std::string(char const * const);
npad 1
lea rdx, OFFSET FLAT:$SG36022
lea rcx, QWORD PTR s$[rsp]
call std::string& std::string::operator=(char const * const);
lea rdx, QWORD PTR s$[rsp]
mov rcx, QWORD PTR __$ReturnUdt$[rsp]
call std::string( std::string&& );
mov eax, DWORD PTR $T1[rsp]
or eax, 1
mov DWORD PTR $T1[rsp], eax
lea rcx, QWORD PTR s$[rsp]
call std::string::~string(void);
mov rax, QWORD PTR __$ReturnUdt$[rsp]
mov rcx, QWORD PTR __$ArrayPad$[rsp]
xor rcx, rsp
call __security_check_cookie
add rsp, 88 ; 00000058H
ret 0
static local variable compiled code
Code: Select all
mov QWORD PTR [rsp+8], rcx
sub rsp, 56 ; 00000038H
mov DWORD PTR $T1[rsp], 0
mov eax, OFFSET FLAT:_Init_thread_epoch
mov eax, eax
mov ecx, DWORD PTR _tls_index
mov rdx, QWORD PTR gs:88
mov rcx, QWORD PTR [rdx+rcx*8]
mov eax, DWORD PTR [rax+rcx]
cmp DWORD PTR TSS0<`template-parameter-2',tStaticString,std::string,void,int, ?? &>, eax
jle SHORT $LN2@GetStaticS
lea rcx, OFFSET FLAT:TSS0<`template-parameter-2',tStaticString,std::string,void,int, ?? &>
call _Init_thread_header
cmp DWORD PTR TSS0<`template-parameter-2',tStaticString,std::string,void,int, ?? &>, -1
jne SHORT $LN2@GetStaticS
lea rdx, OFFSET FLAT:$SG36037
lea rcx, OFFSET FLAT:std::string `std::string GetStaticString(void)'::`2'::s
call std::string( char const * const);
lea rcx, OFFSET FLAT:void `std::string GetStaticString(void)'::`2'::`dynamic atexit destructor for 's''(void) ; `GetStaticString'::`2'::`dynamic atexit destructor for 's''
call atexit
npad 1
lea rcx, OFFSET FLAT:TSS0<`template-parameter-2',tStaticString,std::string,void,int, ?? &>
call _Init_thread_footer
$LN2@GetStaticS:
lea rdx, OFFSET FLAT:$SG36038
lea rcx, OFFSET FLAT:std::string `string GetStaticString(void)'::`2'::s
call std::string& std::string::operator=(char const * const);
lea rdx, OFFSET FLAT:std::string `std::string GetStaticString(void)'::`2'::s
mov rcx, QWORD PTR __$ReturnUdt$[rsp]
call std::string::string(std::string const &);
mov eax, DWORD PTR $T1[rsp]
or eax, 1
mov DWORD PTR $T1[rsp], eax
mov rax, QWORD PTR __$ReturnUdt$[rsp]
add rsp, 56 ; 00000038H
ret 0
push rbp
sub rsp, 32 ; 00000020H
mov rbp, rdx
lea rcx, OFFSET FLAT:TSS0<`template-parameter-2',tStaticString,std::string,void,int, ?? &>
call _Init_thread_abort
add rsp, 32 ; 00000020H
pop rbp
ret 0
I also forgot to mention, but realizing after reading through the assembly, the standard also requires the initialization of static variables to be thread safe if I recall correctly. It looks like this code creates a thread local version of 's' for the static local code. This means that each thread that enters the GetStaticString() function will get their own copy of 's'.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com