keean / zenscript

A trait based language that compiles to JavaScript
MIT License
42 stars 7 forks source link

The differents method for dealing with temporary / local object storage #37

Open NodixBlockchain opened 6 years ago

NodixBlockchain commented 6 years ago

I've been investigating the different ways to deal with local / short lived object past days, and so far i've seen 3 ways for doing this.

  1. Heap allocated object holding reference to local objects.

https://stackoverflow.com/questions/2800463/how-variables-are-allocated-memory-in-javascript

When you call a function, amongst other things a "variable environment" for that call is created, which >has something called a "binding object". (Call it the "variable object" for short; saying "the binding >object of the variable environment" is just a tad bit long-winded!) The variable object has properties for >the arguments to the function, all local variables declared in the function, and all functions declared >within the function (along with a couple of other things). Unqualified references (e.g., the foo in foo, >not obj.foo) within the function are first checked against the variable object to see if they match >properties on it; if they do, those properties are used.

Which is what i use in the script to hold procedure arguments or local variable for the moment.

  1. Heap allocated stack frame.

Each time a function/procedure is called, a stack frame is allocated on the heap to store local objects/variables, and collected normally as another heap allocated memory, when the collection is run after the function return.

  1. Pure stack.

A specific memory area is allocated for each threads additionally to the heap, and the program can push/pop elements to it by decrementing/incrementing the top pointer.

Pure stack can be made using the cpu built in stack registers and instruction, or implemented separately using higher level primitive to manipulate the stack.

The best i guess is to have a stack system, the advantages are that memory can be reclaimed easily immediately when the function / procedure return without much performance or complexity cost.

It also make recursion easy, and having lot of function call with local variable will leave the memory use as low as possible across differents functions call without havng to wait for a garbage collection.

The disavantage i see with stack is first if the language need to support blocking behavior, such as waiting on a promise to fullfill, it needs to pause the execution, then go back some level below to execute the background function or other events handlers while the promise is unavailable, and resume the execution when the promise is fullfilled.

As far as i understand, if the blocking mechanism of pause/resume need to be implemented, it would need a thread-like context switching for the stack, to make in sort that when a function execution is paused, the stack is saved, and it would needs another stack to keep the other part running, and then restoring the stack when the function execution is resumed.

It can be avoided by eliminating all blocking behavior, which is more or less what i intend to do, and using event/callback mechanism to handle asynchronous execution, similar to AS3, even if that can sometime be annoying to have everything as a callback, but otherwise if the language need to have a blocking behavior, even if it's single threaded in green thread manner, it still needs some kind of context switching to save/restore the stack.

With heap allocated structures, only the execution position needs to be saved/restored, and it doesn't require much complex thread-like context switching for blocking behavior.

The other problem is that the memory used by local objects will be invalid when the function return and the stack is restored to previous stack frame, so references to those objects will be invalid after the end of the function execution, but this one is probably not too hard to track either at compile time or runtime, as it should be made explicit that object allocated on the stack cannot be referenced by object allocated on the heap that live longer than the function. If they are simples values, they can just be copied by value. For more complex objects instances, it should be made explicit that heap objects can't hold reference to stack objects ( Not like in C or C++ where stack pointers are undistinguishable from heap pointers ).

Incorrect. I already stated that rarely are any of the objects still around when the tracing+copy >compacting occurs. The cost is amortized away to negligible. I think this is the 3rd time I have stated >this in the past few posts.

Not necessarily, because let say a function or procedure trigger the memory collection when being high on the call stack, the collection still need to trace all the live objects in memory which can still represent high number of objects even if they are 'short lived' or their lifetime is stricly bound.

Using a stack those objects doesn't need to be cared about at all, and their memory is immediately reclaimed automatically for other local objects/variables when the function or procedure return.

NodixBlockchain commented 6 years ago

There is one clear situation.where having no stack is better, and that is in an asynchronous language >where you want a 'cactus' stack.

Correct me if i'm wrong, but In the case the asynchronous language is made in the same fashion than AS3 or js, in sort that the main loop is outside of program scope, the program only being message handlers, and the asynchronous calls are handled via a message queue in sort that asynchronous calls are only made by the main loop outside of program scope, and the language doesn't allow blocking, it doesn't require a cactus stack does it ?

With a language based on message queue processed outside of program scope, there is no need for intra-function context switch, and only a single linear stack is required right ?

It's not necessarily optimal because long message processing can introduce latency in repsonse to other messages, but if message processing time is kept low, and all long operations are made asynchronous with message handlers, it can keep low latency, even if it can introduce more complexity in the program when the handler code has to depend on local state which can only be solved with the use of closures, or some kind of heap allocated object passed along in the message.