static inside a class, inside a method

If I call a method in a class a million times but I don't want it to create and delete the variable a million times, what to do? Can I add static to it and it will only be created once and live throughout the instance of the class. I don't want to create the variable outside of the method though and I want it inside the method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

class Test{
public:
	void foo1()
	{
		static int int1{0};
		int1 = 55;
		std::cout << int1 << std::endl;
	}

	void foo2()
	{
		static int int1{11};
		std::cout << int1 << std::endl;
	}
};

int main(){
	Test class1;
	class1.foo2();
	class1.foo1();
	class1.foo2();
}
side note: types that the cpu can handle directly are often not created at all.
things like int, char, double, pointers, etc where possible are optimized away by the compiler so there is 'create' of the 'variable'.
larger items like your own classes, it can't do that.

as far as it goes, static has limitations that don't always work for what you need.
int that case, consider this:

void foo()
{
thing x; //complex object with ctor, dtor etc.
...whatever
}

for(a whole lot of times)
foo(); //creates and destroys a whole lotta things.

vs
void foo(thing &x)
{
..whatever
}
...
thing x;
for(a whole lot)
{
foo(x); //reuse the same one
}

I know you said you don't want to do it like that, but in situations where static is not correct**, that is the way you do may need to do it, and it is critical to do so if the ctor and dtor are taking a lot of time. . You can add all kinds of fluff around this idea to get it 'out of the way'. It could possibly also be a member variable of the class the method belongs to, so it would be distinct for each variable but shared inside the object if that is useful (?).

**static stops working when you run into needing the value to be distinct across instances one way or another; like a global variable when you discover it needed to have 2 values at the same time. Static also stops working in very large projects where its been used too many times and the space for adding more is low.

as long as static works for you, use that though, its a lot simpler/cleaner.
Last edited on
There will be only 1 instance of the class and never anymore than one. Yes, I prefer for the variable not to be a member of the class or even a reference.
Can I add static ...?
Yes. You can use statics within a function. However, if you have class static members too.

Also, remember, static ints are initialized to zero as in C.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

class Test {
public:
	void foo1() {
		int1 = 55;
		std::cout << int1 << std::endl;
	}

	void foo2() {
		int1 = 11;
		std::cout << int1 << std::endl;
	}

private:
	static int int1{0};
};

int main() {
	Test class1;
	class1.foo2();
	class1.foo1();
	class1.foo2();
}
Last edited on
Yes, I prefer for the variable not to be a member of the class or even a reference.


Why? What difference does it make?
I need it this way because speed will be of the essence in a larger program and there will be many similar names as data members. Both functions will have very similar code with some slight variations that will also be experimented on and changed. The code, with all the calls to other classes and functions looks long and eye watering and it is easy to make a mistake.


If I had this and I had to copy the logic or part of the logic from one function to another then I might miss a rename in the sea of names.
int m_Int1 {0};
int m_Int2 {0};

If I had this in each function, then I never have to worry about it and the function does not have to create and destroy the variable millions of times.
static int int1 {0};
Last edited on
as long as the thing you need does not invoke a constructor that does things, this optimization isn't necessary. For integer, you are not saving anything at all here, and possibly even slowing it down more.
How?
what does static do? It creates a memory location for the variable that persists, its outside the stack frame of the function call, so using it could result in a page fault. On top of that, because its value persists, you must flush your register value back to it, possibly multiple times inside your function. These extra writes may be avoidable with a local which can be optimized to just a register.

if you are constructing an object that has a constructor, static may help you. But with a basic integer, its a tossup as to whether it is better or worse, and you will need to test your code to see if its the right thing to do.

but you are way early on your optimizations. Let the profiler point to where it is slow. Odds are very high, it won't be the stack frame.
SubZeroWins wrote:
there will be many similar names as data members. Both functions will have very similar code with some slight variations that will also be experimented on and changed. The code, with all the calls to other classes and functions looks long and eye watering and it is easy to make a mistake.

Perhaps you could gradually refine the style of the code (to make it more manageable)?
^^ sometimes that can be so aggravating. Math code esp, long variable names are a detriment (x+y) vs (somebiglongnamethatreallymeansx + anotherbiglongnamethatmeansy) isnt cutting it when translating equations and that blows up to the Nth when the equations are somewhat complex. But at the same time, a typo will ruin your week trying to find it.
If its bothering you and fixing it everywhere is a big job, you can locally make references to rename them. That costs nothing, simple references are abstracted to an address in the cpu instructions. Some references can get mangled and become pointers, but a simple one to rename something inside a function won't be at risk of that.
I plan on refining and optimizing when the bulk is done but there will always be minor changes in the future too.

seriously jonnin?
I thought my program uses only 1 stack. Stack from main adds a function call to the top of the stack and when the function ends it pops off the stack from the top. I would have guessed it added the static to the same stack but not popped it off until the end of the program.

I am fairly new to programming so I don't understand this. Page fault, as in trying to access memory location that has not been created yet?

On top of that, because its value persists, you must flush your register value back to it, possibly multiple times inside your function.

I don't even know what this means. I just create and use variables in my program. Now you are telling me I have to flush something? I don't understand?

In the beginning I had the variable as a member but then I needed to branch off and so I made it local without static for the time being just to test things out as I continue to change code.

I just tried to make it static local in the function and no good. I don't get it as I initialize and set the variable before each of the for loops that I use. It works fine without static and when I add static I get an error trying to access the vector outside the range. As if the 2 functions are overlapping with the variable as static. But it works on my small sample foo function I posted here before.
Last edited on
Are you also telling me that a reference to an int created and deleted a million times is not as bad as an int created and deleted a million times?

Both the reference and the int are 4 bytes long and they both are bits in memory. How can it be that it is not as bad? I could understand it not being as bad when passing and copying to functions and classes. But we are talking about creating and deleting them so many times? If this is true that this is a minimal hit that would be awesome! Please convince me.
Last edited on
SubZeroWins wrote:
I thought my program uses only 1 stack.

Each thread has its own stack. If your program doesn't create any additional threads then there will only be one stack.

SubZeroWins wrote:
Stack from main adds a function call to the top of the stack and when the function ends it pops off the stack from the top. I would have guessed it added the static to the same stack but not popped it off until the end of the program.

With stacks you always add (push) and remove (pop) from the top. You cannot just leave something and pop something underneath. That's not how stacks work.

Even if you could, how would the function find the static local variable next time it was called? Note that the stack frame might not end up in the same location each time the same function is called, and other functions that gets called in between might end up using that same part of the stack and overwrite whatever was left there by earlier functions.


SubZeroWins wrote:
I just tried to make it static local in the function and no good. I don't get it as I initialize and set the variable before each of the for loops that I use. It works fine without static and when I add static I get an error trying to access the vector outside the range.

Note that static local variables are only initialized the first time the code is executed. The next time the variable will still hold the old value that it had at the end of last time.

Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

int count()
{
	static int i = 0;
	++i;
	return i;
}

int main()
{
	std::cout << count() << "\n"; // prints "1"
	std::cout << count() << "\n"; // prints "2"
	std::cout << count() << "\n"; // prints "3"
	std::cout << count() << "\n"; // prints "4"
}
Last edited on
SubZeroWins wrote:
Both the reference and the int are 4 bytes long and they both are bits in memory. How can it be that it is not as bad? I could understand it not being as bad when passing and copying to functions and classes. But we are talking about creating and deleting them so many times? If this is true that this is a minimal hit that would be awesome! Please convince me.

Creating a variable is essentially "free" if you ignore the initialization. All it takes to reserve room for a variable on the stack is to increment the "stack pointer".

Note that a lot of the things being discuss here are implementation details. The C++ standard doesn't say how things should work, it just describes what observable behaviour we can expect.

The stack is an implementation detail that the C++ standard says nothing about. The compiler often do "optimizations" to avoid storing things on the stack. The values of local variables are often only stored in CPU registers. It analyses how the values flow through the code so the actual named variables doesn't matter that much (I'm talking about simple types like integers and pointers).

In a function like this
1
2
3
4
5
6
7
8
int f(int arg)
{
	int x = arg - 2;
	++x;
	int y;
	y = x;
	return x + y;
}
my only concern would be that it's difficult to read and make adjustments to.

Performance-wise I feel comfortable that the compiler will optimize it to the same as
1
2
3
4
int f(int arg)
{
	return 2 * (arg - 1);
}
(or something very similar)

If you use Compiler Explorer to view the assembly output generated by GCC and Clang you can see that they indeed generate the same instructions for both versions of the function: https://godbolt.org/z/eE51ErGM3

EDIT: Earlier I showed how marking x as static made the generated assembly much worse but it wasn't a fair comparison because it also meant the function didn't behave the same way as before. Changing it to use static in a way that preserves the old behaviour gives the same optimized assembly as above: https://godbolt.org/z/vvfose556
Last edited on
Just a sidenote:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

int count(int x)
{
	static int i = x;
	++i;
	return i;
}

int count2()
{
	static int i = 7;
	++i;
	return i;
}


int main()
{
	std::cout << count(7) << "\n"; // prints "8"
	std::cout << count(4) << "\n"; // prints "9"
	std::cout << count(42) << "\n"; // prints "10"
	std::cout << count2() << "\n"; // prints "8"
	std::cout << count2() << "\n"; // prints "9"
}

* Each function has its own variable named "i".
* Initialization occurs only on the first call to function. Hence the "x" is not used in later calls. Not very intuitive, is it?



If your variable is an int and the function that you do call a million times has many instructions, then do you really think that the time taken to "create" int each time becomes significant?
I guess this is why they make you take assembly language, or used to. As noted, this isn't c++ but your c++ is affected by how the hardware or machine language actually does things.

from the top, then.
- if you make a static variable that exists for the whole program's duration, it lands in memory somewhere, and it sits right there for the duration. Some time later, you call your function, and it makes its stack frame, ok. But when it talks to the static variable, its not in the stack frame. If you are lucky, its still paged into the cpu's cache. If you are not, it has to go get the page where it lives, which is probably just a 1 time cost since it will keep the page there afterwards. If your function is touching enough pages, though, it can thrash and has to shuffle the pages around every iteration. Not likely for the simple examples thrown around here, but its the (unlikely) worst case scenario.

Ok, so yea, that one is low risk. ^^
but the flushing? How does that play into it?
when calling a function a bunch of times, with a static variable (it has to keep the value, remember that key thing)...
so the function's assembly looks roughly like
get the current value from memory and put into a cpu register
do something
put the new value back into memory.
when the variable is not static, a different set of instructions is likely:
the value is pushed into a register when the function is called
the function does something
the new value remains in the register
the calling program picks it up from there directly and does whatever with it.
the extra write back to memory is possibly optimized out and avoided, if it is not necessary (for example, a loop counter's value is discarded, and never flushed from register to memory).

as far as variables go, they are just memory locations in human readable form, in your code. so, if x is at location 1234, then int &y = x; //does nothing. in both cases, the compiler just sees 1234 whenever x or y are used. If it has to turn &y into a pointer, its no longer free of course. The compiler won't do that for a local rename of an annoying variable name; its triggered by more complicated code.

none of that is anything you can really do much about. The compiler does its best to give you the best code it can if you have optimizations turned on. The point of all that, then, is simply that you can't assume that static is the best choice. You have to test it both ways, see if having it created each time or static has any effect on the time. And while doing that, profile the whole piece of code, see where it really is spending the time. Odds are good that the biggest time taken won't be related to creating or destroying or handling an integer in any way that is similar to the examples shown here.

in a similar topic, calling a function a bunch of times begs a study to see if it was inlined, or why it wasn't, and if it could be, and if doing so would actually help... an aggravating topic, but if you are tweaking...
the biggest, cheapest thing you can do on modern computers is, if at all possible, thread it out. a million function calls on the current home computer CPUs can maybe be just a hair over 50k iterations per processor (assuming 20 or so processors, always leaving 1 free for the OS).
Last edited on
yes my program is not multi-threaded and my program has only 1.


1
2
3
4
5
6
7
void foo(){
static int x = 2;
}

int main(){
	int x = 1;
}


I thought the stack would look something like this

**STACK**
void foo(){
static int x = 2;
int x = 1;


At the end pop off the function and the static still stays on. How does the function access it if called a million times you asked, geez the same way it calls any other variable through memory addresses.

but yawza, the compiler prefers to store local variables in cpu registers. Now my pupils are more dilated.

I guess you learn harder when the anvil falls harder, as it now works.
static int int1 = 0;
int1 = 0;

stop it, gurls can learn c++ too! Thank you for your precious time fellas I have learned a lot here.


What profiler do you recommend? free vs paid?
Don't try to out-optimise a good C++ optimising compiler without info. Code the best way for what the code is doing. Then measure the performance. If the performance is OK then fine. If it's not then start looking first for algorithm optimisation. Often changes here can generate far better performance improvement then tinkering with small code changes for program optimisation. But for each change made, test. What you think may improve performance in practice may not - or may make it worse. If you really need to get that far down and dirty with code optimisation 'tricks' then you probably need to understand the compiled code generated. Compiler Explorer (godbolt.org) is great for this.
What profiler do you recommend? free vs paid?

I have visual studio pro which has a profiler built into it. I have not looked for an external tool in decades, just used that one since vs 5.0 era :)

I have written my own before, and its not hard to craft a decent one. Macro out what you do so it can be eliminated from your compiles with a flag, and then you want at least # of times a function was called, total time all that took, longest and fastest (optional) execution times. Roll all that into a report using the function name macro. The less work your timer start and stop calls do, the better: the one I did was (very, very long ago) just an assembler call to get the cpu clock and store it (to start) and same with a subtraction (end-start times) for end. Don't get fancy, just store the time difference associated with the function's name and do all the heavy work at the end in your report. If you can't find what you need for free, that is. If you can find a nice freebie, just use that of course, it likely has more info and more info = good.
Last edited on
At the end pop off the function and the static still stays on.

But what would happen when main() ends and x needs to get popped off the stack?

If it helps, let's pretend foo() is not called from main() but from some other function. Also think about what would happen if you later have other functions with other variables that gets called.

How does the function access it if called a million times you asked, geez the same way it calls any other variable through memory addresses.

But then you would need to store that memory address (i.e. pointer) at some fixed location so that the function could look up where the static variable was stored. There is no real point in doing that if you can just store the static variable directly at such a fixed location.

What profiler do you recommend?

I like Callgrind (A Valgrind tool), and then KCachegrind to view the result. Don't think it works on Windows though.
Last edited on
Topic archived. No new replies allowed.