Declare Local inside or outside ?

BlitzMax Forums/BlitzMax Beginners Area/Declare Local inside or outside ?

Armitage 1982(Posted 2011) [#1]
There is something I always wonder about.

Is :
For Local i:Int = 0 To 10
   Local dummy:int = 0
Next


is as good as :
Local dummy:int
For Local i:Int = 0 To 10
   dummy = 0
Next

And as good as :
Local i:int = 0
Local dummy:int
For i = 0 To 10
   dummy = 0
Next


Of course, when it is possible to declare variable outside the loop !

I always use the third version (because I think it is better for re-allocation memory) but I always see the first one everywhere. I know it won't produce much differences but let's say it's for the sake of choosing a correct guide line.
Maybe I'm wrong, maybe BlitzMax know exactly when re-allocating memory.

But imagine now this situation for an heavy object (build to allow enumeration and using < = > sign). I surely don't want a constructor call every loop while I can reuse the object simply by using equal.


Jesse(Posted 2011) [#2]
all of them work the same way.
local variables are stored on registers and/or the program stack.
the stack is used to store the values so only the stack pointer is shifted depending on the needs
when a variable for example an integer is created, then the stack pointer value is assigned to the variable and the stack pointer is increased by 4(meaning bytes) and and the variable address is set to 0. when the variable goes out of scope the stack pointer is subtracted 4 and the variable is ignored. so it really doesn't matter how you do it, it will use the same principle. for most modern languages it is recommended to use locals as much as possible because there is no need to allocate memory for the creation of those variables and is faster to create and release.

Last edited 2011


Shortwind(Posted 2011) [#3]
Here is a different point of view:

As jesse said, the only real difference is the life of the variables.

If you don't need to retain the value of i or dummy after the for loop ends, then the first method is fine.

The second method is of course used when you need to retain the value of dummy for further use outside the for loop.

The third method would only be used if you need to retain the value of both variables further down in the program. Of course they will still only exist in the context of the procedure/function/file they are contained in.

A simplified view, but hope it helps.


therevills(Posted 2011) [#4]
Ages ago I remember seeing an article that Mark wrote (or was in an interview) which explained what the compiler actually does when using locals etc... but do you think I can find it!? :(


H&K(Posted 2011) [#5]
I think the second one is better, for the one reason not mentioned. It looks nicest.


@ Jesse, When the compiler meets a scope definition doesn't it consider this as the same as a new? That is in the first one, (not the second because the local is only encounterd once at the start of the loop), isn't space for a new int being created each loop?


Yasha(Posted 2011) [#6]
isn't space for a new int being created each loop?


Why would it bother to do that?

The compiler's already been over the code, and knows how many variables are visible and active at any given point in the code; which in turn means it knows in advance how much space the function needs at its most demanding point, and can therefore simply set it up to allocate that amount of space on entering the function. If that variable exists from the start of the function, it doesn't hurt anything, it just goes unused until the code reaches the loop. If another loop also has an inner variable it would presumably overwrite the same space, and no-one would be the wiser.

Some languages will reallocate the loop's subframe each time, but that's usually because they support lexical closures (i.e. call frames are objects) and past iterations may need to persist (e.g. Scheme's "named let" does what you're thinking of). BlitzMax doesn't need to do that.


therevills(Posted 2011) [#7]
I think I found the article I was talking about (check out the comments section too):

http://marksibly.blogspot.com/2009/09/spillage.html

But I remember it being longer... :/

Last edited 2011


H&K(Posted 2011) [#8]
If another loop also has an inner variable it would presumably overwrite the same space, and no-one would be the wiser.
As I think is obvious I don't know if it does this, but your answer of why would it, it would "Presumably" not, is basicly saying you don't know either.

If it does allocate a new int each scope definition it wouldn't overwirte the old one, but create a new one, which would mean that when the GC kicked in there would be have the number of loops INTs to deal with.


Jesse(Posted 2011) [#9]
@H&K
I believe you are right although the way, according to Mark's Blog, BlitzMax handle locals in Registers might make it pointless. I knew about the usage of registers as variable storage but not to the degree he mentions it in his blog. Most modern compilers use the stack for locals and worry less about register to local variable speed optimization.

locals storage according to wikipedia:
http://en.wikipedia.org/wiki/Local_variable


Yasha(Posted 2011) [#10]
you don't know either.


True enough, but my point is that if you can design it to do exactly the same thing either with or without a reallocation each iteration, why wouldn't you set it up with the right size from the beginning? Anything else would just be inefficient.

Anyway, unless BlitzMax uses a radically unusual method of allocating call frames, the difference is irrelevant because locals are put either on the stack or in registers; you don't really "allocate" stack space, because it's already there; you just move the pointer for the current frame up beyond the locals of the previous one (note that this effectively means that you don't really allocate locals at all - they're just "there" in the space beyond the current top of the stack). The GC never kicks in at all, because call frames aren't objects, and don't need to be freed.

The short answer is that Mark would have to go out of his way to make the first option any less efficient; and in fact in the few cases where there's any difference it's likely to be the more efficient option because it gives slightly more opportunities for sharing space.

Last edited 2011


H&K(Posted 2011) [#11]
Would you all feel differently if Dummy was a type?

For Local i:Int = 0 To 10
   Local dummy:MyType = New MyType
Next

Local dummy:MyType = New MyType
For Local i:Int = 0 To 10
Next



Yasha(Posted 2011) [#12]
That code doesn't do even remotely the same thing though. Do you fully understand types in Blitz?

User-defined types create objects. These need to be allocated on the heap when new ones are created with New. Therefore, your latest examples involve one piece of code where an object is created once, and one piece of code where an object is created ten times, and each time the new reference - a pointer to the heap object - assigned to the variable.

In the original examples in post #1, no objects are being created at any point. Integers are passed between variables by value, and don't refer to anything allocated on the heap. Therefore in those original examples, nothing new is allocated each iteration of the loop, only copied. Local variables are not allocated on the heap either.


TomToad(Posted 2011) [#13]
When in doubt, test it out. :)


I had to add a bit of code to the examples or else the compiler will optimize out some of it. As you can see, in all three of the examples, the exact same code is generated.

@H&K: With types, your first example will very likely allocate new space for the type each loop since types are controlled by the GC and not the CPU. However, I would think if you are creating a new type within a loop, there must be a very good reason (possibly adding it to a list at some point).


H&K(Posted 2011) [#14]
That code doesn't do even remotely the same thing though. Do you fully understand types in Blitz?

I know it doesn't do the same thing, My point still is IF you are asked a question like the OP, where INT may have been chosen simply for the simplicity of the question.

So whereas Jesse's original response is correct, my supplementary question of if When the compiler meets a scope definition doesn't it consider this as the same as a new, is a valid question for both basic variables and types.

With types, your first example will very likely allocate new space for the type each loop since types are controlled by the GC and not the CPU. However, I would think if you are creating a new type within a loop, there must be a very good reason (possibly adding it to a list at some point).
Now maybe this is what Armitage was asking in the OP.
Thanks for the "Its the same for basic types"


Armitage 1982(Posted 2011) [#15]
Thank you very much all !

If found precious information in those answers.
It's good to know I still can learn this while never attended any programming courses :)

Register as always been something Voodoo for me when I read ASM code :D
But good to know BlitzMax is efficient on that very point.

I would kill for such efficient level of concern in Max2D ^^
Very strange we never saw a full new optimized Max2D toolkit module over years.