There should be better warnings not to do this in Renpy.

User #1751331

Member
Oct 30, 2019
193
156
The following code is very basic to show something people can do and I haven't seen any real warnings not to do. I might have just missed them though. Frankly it should have a huge warning on the Renpy labels & control flow page. Calls push a next statement onto the call stack. This is so it knows where the return goes back to. However when you jump out of the called area it doesn't clear that off. So the stack continually grows and gets slower and slower and slower. Normally what happens is a routine is called and when it is exited via return the statement is removed from the stack so the size remains fairly small.

Code:
label f1:
    if poodle==1:
        call r1

label f2:
  if cat==1:
       call r1

label f3:
    if snake==1:
         call r1
    
/**********************This is the section that is a problem it uses a jump from inside a called routine to outside of it************************/    
label r1:
    /*code*/
    if foobar==1:
        jump r2
    /*more code*/
    return
    
label r2:
    /*code*/
    jump f1
 
  • Like
Reactions: recreation

KiaAzad

Member
Feb 27, 2019
277
207
the common consensus is: only call a label if you intend to return from it to the same place.
I'm sure somewhere in the renpy documentation there is a line explaining that but you're right, the warning needs to be more prominent.
 
Last edited:

User #1751331

Member
Oct 30, 2019
193
156
the common consensus is: only call a label if you intend to return from it to the same place.
I'm sure somewhere in the renpy documentation there is a line explaining that but you're right, the warning needs to be more prominent.
If there is a warning I haven't found it. I have only found a small line explaining that the call stores a statement in the call back stack. For most new programmers they have no clue what that means or the implications of it.

Honestly, if Renpy was my project I probably wouldn't have included jump at all. Or at the least created flag so that when a call is used till the return happens the only jumps that can be made would be to local labels. That would prevent the issue.
I did send pytom an email today suggesting the flag and limiting to local. Hope he implements it and well see less issues because of it and some poor programmer a year into his project won't find he needs to rewrite the entire damn thing because of a number of those issues he/she didn't know about.
 

Winterfire

Forum Fanatic
Respected User
Game Developer
Sep 27, 2018
5,050
7,397
It is not that big of a deal, it may be ugly, but I doubt the effects would be noticeable unless you are trying to support VERY old machines.

-edit-
I agree there should be a warning tho.
 
  • Like
Reactions: User #1751331

User #1751331

Member
Oct 30, 2019
193
156
It is not that big of a deal, it may be ugly, but I doubt the effects would be noticeable unless you are trying to support VERY old machines.

-edit-
I agree there should be a warning tho.
You are totally wrong. All you need to do is play the game strait for some time and it will begin to slow down more and more depending on how many times that routine is called and how many times they done that in different routines it adds up fast.
If you want to see the basic effect of it create a dictionary using 32 python and fill it up and larger it gets the slower it gets.
Doesn't matter what machine you are on. That will remain true.
 

AgentOrange

Member
Jul 3, 2017
140
1,254
Well, for an old fart like me, started of programming in the early eighties using languages and machines, most of you have never heard of, it's kinda obvious that you don't jump out of a called label/function at any circumstance !

You just have to arrange your code differently. ;-)

But if you insist to jump from a called label/function, you must delete the call-return adress first, like in you example using pop_call() which should get rid of the return adress according to the documentation

Code:
label r1:

    /*code*/

    if foobar==1:

      pop_call()                /*  check out the renpy documentation  */

      jump r2

    /*more code*/

    return
 
Last edited:

User #1751331

Member
Oct 30, 2019
193
156
Well, for an old fart like me, started of programming in the early eighties using languages and machines, most of you have never heard of, it's kinda obvious that you don't jump out of a called label/function at any circumstance !

You just have to arrange your code differently. ;-)

But if you insist to jump from a called label/function, you must delete the call-return adress first, like in you example using pop_call() which should get rid of the return adress according to the documentation

Code:
label r1:

    /*code*/

    if foobar==1:

      pop_call()                /*  check out the renpy documentation  */

      jump r2

    /*more code*/

    return
You and I aren't knew programmers. For most programmers of my generation Jump or GOTO statements are foul word. 35 years of programming in C, C++ I never once found a good reason to use GOTO in any project.
C++ at least limits goto to only being used with in the same function.

They need to encapsulate the jump along with the pop_call() before the return that way if they don't use the call the return doesn't grab the wrong one off the stack as you showed.

There are 1000 wrong ways to use jump and goto for every right way to use it.
 
  • Like
Reactions: hiya02

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,585
2,229
You're technically right.

But on a practical level... the sort of devs that would fuck it up, generally don't use call anyway.

I think of in all the time I've been looking and messing with RenPy games, I've seen 1 game where the developer clearly didn't understand that each call needs a corresponding return and 1 forum post where someone was asking about it.

Inexperienced devs tend cut-and-paste simple games. Those games don't have calls in them, and if they do and the dev doesn't follow the logic - they ignore it and stick with jump.

The problem is that there's no way a compiler/interpreter has no way of knowing that the code is dogshit. That spaghetti example you've used could be intentional, depending on how clever the programmer is being. I'm not suggesting your example is okay in it's current state... but recursive code is a thing. All that is required is that there are an equal number of return statements executed for call statements.

Hell, the real code I saw doing it wrong ran just fine - because it wasn't looping back on itself, just building a bigger and bigger call stack it didn't end up clearing.

But even if there was a way of telling good code from bad code, you'd still want the RenPy forums not the F95 forums. Nobody here can alter the way RenPy works beyond going to the RenPy forums and asking someone on their team. In theory an inexperienced dev could search on here and find a post explaining call and return or even this very thread... the flaw with that though is that sort of dev you're worried about wouldn't be searching for a solution they don't know they have.

As for GOTO, I could make a case for GOTO {exit-label} within non-object oriented code, but I've never managed to convince a "GOTO statements are evil" purest yet... and I don't expect to now. :devilish:
 
  • Like
Reactions: User #1751331

User #1751331

Member
Oct 30, 2019
193
156
You're technically right.

But on a practical level... the sort of devs that would fuck it up, generally don't use call anyway.

I think of in all the time I've been looking and messing with RenPy games, I've seen 1 game where the developer clearly didn't understand that each call needs a corresponding return and 1 forum post where someone was asking about it.

Inexperienced devs tend cut-and-paste simple games. Those games don't have calls in them, and if they do and the dev doesn't follow the logic - they ignore it and stick with jump.

The problem is that there's no way a compiler/interpreter has no way of knowing that the code is dogshit. That spaghetti example you've used could be intentional, depending on how clever the programmer is being. I'm not suggesting your example is okay in it's current state... but recursive code is a thing. All that is required is that there are an equal number of return statements executed for call statements.

Hell, the real code I saw doing it wrong ran just fine - because it wasn't looping back on itself, just building a bigger and bigger call stack it didn't end up clearing.

But even if there was a way of telling good code from bad code, you'd still want the RenPy forums not the F95 forums. Nobody here can alter the way RenPy works beyond going to the RenPy forums and asking someone on their team. In theory an inexperienced dev could search on here and find a post explaining call and return or even this very thread... the flaw with that though is that sort of dev you're worried about wouldn't be searching for a solution they don't know they have.

As for GOTO, I could make a case for GOTO {exit-label} within non-object oriented code, but I've never managed to convince a "GOTO statements are evil" purest yet... and I don't expect to now. :devilish:
There is a pretty good way to tell when a game has issues like this. Ever play a game and it doesn't have a technical memory leak such as forgetting to free assets or some garbage but yet the game runs slower and slower the longer you play. It seems to lag more and more. Then you close the game reload and speed is fine again. That's a pretty good sign of it having been done.
You can find more than one game on this site that has that issue.

The fact is it could easily be prevented by Pytom. Anytime the stack is added to prevent any jumps other than local jumps till the item is removed from the stack.

As for programmers likely to do it. You are sort of right and sort of wrong. People learning at all sorts of stages are apt to do it. Few programmers read every line of how something works. But they often learn by copy and pasting stuff. So they see something on a forums someone else used and think hey that's what I need ... and you know how it goes from there.

The way I see it its part of learning. Unfortunately some of these people learning are also creating projects and will run into the issue as the project gets big enough to make it apparent and then it means going back fixing code one way or another. That sucks for them. Some of them might just give up not realizing there are some simple solutions and workarounds to fix it.

GOTO is a bit of a laugh.
Talking with my prof when I was in school. He said on day three they created Goto to make some stuff simple on day 4 they made compilers better where nothing it offered was worth anything. From a technical stand point he was and is right. Compilers haven't gotten worse and I don't see that ever being the case. So nothing you can do with goto can't be done better not using it or at least equal to it.

With C++ GOTO can only be called to labels in the same function. So you can really only use it to skip over some code or jump back up to the top of a routine. The first can be done with an if or condition statement and using encapsulating the code the second can be done by including code in a function. If it's in a function you can overload it or make a template out of it....
If you are jumping code you don't just do it for no reason so there is a condition for it which means in most cases you use a goto to jump code you are still using the if statement or conditional control flow.
In 35 years I haven used GOTO one time in a program.
 
  • Like
Reactions: hiya02

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,585
2,229
As for GOTO, I could make a case for GOTO {exit-label} within non-object oriented code, but I've never managed to convince a "GOTO statements are evil" purest yet... and I don't expect to now. :devilish:
So nothing you can do with goto can't be done better not using it or at least equal to it.
[...]
In 35 years I haven used GOTO one time in a program.
... and my 100% record continues. :whistle:
 

Kinderalpha

Pleb
Donor
Dec 2, 2019
198
262
Just because you haven't encountered a use for goto, doesn't mean there isn't one. I've made use of it before, can't remember exactly what it was for but it was a very specific use case.

It sounds like what you explained in the OP could be described as poor usage of recursion, I'm not familiar with RenPy though. Like other people have stated, can't see why the average RenPy user would do this. You either know enough about this issue to never do it, or don't know enough about python to even have the chance to. RenPy can't be held responsible with explicitly stating every possible example of poor programming.
 

User #1751331

Member
Oct 30, 2019
193
156
... and my 100% record continues. :whistle:
Using GOTO to reach an exist section of a function for cleanup is based on appearance or readability. Well that's the claim.

Well lets look at that and see how valid it is. GOTO can only be used in C++ inside a function it can't call a label outside a function.
If you are doing cleanup and so on in a function then your program is most likely just one function. Some where at the top of the function you allocated memory you need to free at the bottom. I don't know about you but I've worked on everything from OS, driver, database, crypto systems, networking, servers, clients, games.... I never once had an issue of making it to cleanup in my functions. The reason my code is generally very readable is because I break into modules by function and purpose. To be clear I'm also a performance freak when it comes to programming.

I can safely say in 35 years every single time I seen goto used it was because the programmer made bad choice to start with in how to deal with the situation. That's 100% of the time. Not 99.9999 or some other fraction, 100.

GOTO is used by people who wrote themselves into a corner and refuse to rewrite the code properly. That or they have trouble visualizing how to write it without the GOTO. Usually the problem there is they have trouble letting go of what they wrote and keep thinking about it's form rather than how to look at the function differently.

So yes its going to be real hard to convince any GOTO purest of anything else. The facts don't support it.

Also when you use GOTO it hampers compiler optimizations on that code.
 

Kinderalpha

Pleb
Donor
Dec 2, 2019
198
262
.
I can safely say in 35 years every single time I seen goto used it was because the programmer made bad choice to start with in how to deal with the situation. That's 100% of the time. Not 99.9999 or some other fraction, 100.

GOTO is used by people who wrote themselves into a corner and refuse to rewrite the code properly. That or they have trouble visualizing how to write it without the GOTO. Usually the problem there is they have trouble letting go of what they wrote and keep thinking about it's form rather than how to look at the function differently.
Reading this just puts a real bad taste in my mouth. So you're saying the creators of C have just completely overlooked the goto statement for the entirety of its existence, and it's completely useless and serves no purpose in the language, or those that inherent properties from it? That's just an extremely bold, and rather unecessary, statement to make on an adult game forum.
 
  • Like
Reactions: sakosux

79flavors

Well-Known Member
Respected User
Jun 14, 2018
1,585
2,229
At this point we're all just defending our own perspectives, without adding anything new to age old arguments...

... and going off topic. My bad.

"Opinions vary".
 

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,391
15,307
You are totally wrong. All you need to do is play the game strait for some time and it will begin to slow down more and more depending on how many times that routine is called and how many times they done that in different routines it adds up fast.
No, he's right, and demonstrating it is relatively easy :
Code:
label start:
    $ changeLoop = 0

    while changeLoop < 25:
        "Press a key [changeLoop]."
        $ changeLoop += 1;

    python:
        aList = []
        for i in range( 0, 1000 ):
            tmpList = []
            for j in range( 0, 1000 ):
                aList.append( "something more in that list {} {}".format( i, j ) )

    $ tmp = aList[5500]
    "One value [tmp]"
    $ tmp = aList[25432]
    "and another [tmp]"

    $ changeLoop = 0
    while changeLoop < 25:
        "It's slightly slower [changeLoop]."
        $ aList.append( "A new entry" );
        $ changeLoop += 1;

    $ changeLoop = 0
    while changeLoop < 250:
        "It's not slower [changeLoop]."
        $ changeLoop += 1;

    "DONE"
    $ renpy.pause( hard=true )
For the second loop, the RAM is filled with a list of 1 000 000 entries, all unique, which is way more than what you'll ever have with a polluted return stack. And each loop add an entry in the list, like a call would do.
And for the third loop, the list is still here, but this time there no change on it ; so it simulate the basic play of a scene.

Just skip through the second and third loops, and you'll see that the impact is really insignificant unless you have a very old computer and/or an already saturated RAM. The second loop while be slightly slower, while the third while be insignificantly slower. It's only on smartphone and tablet that you can experience an effectively sensible slowdown, because they are short on memory.
All this in a purely theoretical code. In a game, the addition to the list will happen only during branching, and there's text to read. All this would make the problem totally invisible for the player.

Also have to be noted that the effective problem don't come from the size of the list (or return stack), but from the rollback feature. Every interaction generate an incremental copy of the context, including the variables. But, Python variable being none mutable, and Ren'py doing shallow copies, there's really few to copy in the case of the return stack. Which imply that, at equal RAM occupation, the return stack would have way less influence on the game speed, than the list used in this demonstration.


It doesn't mean that this practice should be encouraged, just that it don't have the impact you think. And anyway, sorry but it's not necessarily a "bad practice". Ren'py design do not enclose labels as independent entities, therefore you don't have the obligation to effectively return from the called label, as long as you're doing it at the end of the called sequence.
Which imply that a code like this is totally legit :
Code:
label mainFlow:
    [...]
    call mainUpdate
    [...]

label mainUpdate:
    [...]
    if time == 1:
       jump updateMorning
    elif time == 2:
       jump updateNoon
    elif time == 3:
       jump updateAfternoon
    elif time == 4:
       jump updateNight
    else:
       $ renpy.notify( "Something goes wrong" )
       return

label updateMorning:
    [...]
    jump updateEnding

label updateNoon:
    [...]
    jump updateEnding

[...]

label updateEnding:
    [...]
    return
In fact, if something should be seen as a "good practice", it should be this and not the same code calling the "update[SOMETHING]" labels. Simply because a jump is less greedy than a call, and don't generate data that will be proceeded by the rollback feature.




As for the Goto problem, I'll quote : "Please don't fall into the trap of believing that I am terribly dogmatical about [the goto statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!".
Said otherwise, all is a matter of needs and personal view over coding. Some will avoid it at all cost, while others will use it time to time, simply because they don't share the same definition of "it will over complicate the code to do it 'this' way".
Personally, while I tend to avoid it, I still had to use it time to time during my nearly 40 years of code writing. Sometimes because it was better this way, sometime because it was easier, and sometime just out of laziness.

What matter is that the code you wrote works, is bug free, and that you understand it. The rest is just a question of personal taste.
 

User #1751331

Member
Oct 30, 2019
193
156
No, he's right, and demonstrating it is relatively easy :

which is way more than what you'll ever have with a polluted return stack.
If that was factual there would be ZERO instances of people having Renpy games crash do to that and they wouldn't see the stack filled errors. And yet some how people have forum posts about it.

The problem here is you are making a false assumption on exactly how much it can be hit and filled up. The only way you can be certain is actually test the specific game with the issue in effect and track the stack size.
Or more blatantly if you will a million entries isn't shit!

We also know stacks respond both in fill and reply slower the more they are filled up. It has shit to do with the age of the machine it is a bi product of how they are coded! Yes, it will be less apparent on a faster machine that doesn't mean it isn't happening. However, when the stack reaches it's limit it won't matter what machine you are on it will still be at it's limit.

You also have a false assumption that if the screen isn't changing or the person isn't responding to dialogue that nothing is going on in the back ground. That's probably true for most games it however isn't true for all games not even those written using Renpy.
The truth is the screen could be refreshing countless times given it is only limited by pythons and SDL's performance on the given hardware.

The Roll back feature is a large part of lag without a doubt. But it's limited in how much you can go back. Once you reach that amount it becomes rather stable. It doesn't keep increasing and you usually hit that in whatever it takes you to see 20 frames or whatever it is set to. You hit it early on. It isn't still increasing 4 hours later. Well I guess it could if it takes you 4hrs to read 20 frames or so.

From Bjarne Stroustrup (developer of c++) He mentions Dijkstra's paper "Goto considered harmful"
 
  • Like
Reactions: hiya02

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,391
15,307
If that was factual there would be ZERO instances of people having Renpy games crash do to that and they wouldn't see the stack filled errors. And yet some how people have forum posts about it.
Where ?
I searched here and on google, I found nothing. Well, nothing except your recent messages on this subject in a game thread. But without more proof of the problem than you gave here.
I searched in Ren'py's code, there's no test of the stack limit. I grepped it, at no time the word "stack" is used in an error message.

So, I performed some test. The size of a return stack entry being relatively fix, this was enough to have a better view of the problem :
Code:
label called:
    $ depth = renpy.call_stack_depth()
    "Stack depth [depth]"
    call called

label start:
    call called
and there nothing like a "stack filled error".

There's an error when Ren'py come around the 62 000 entries in the return stack, but it's a memory use one :
Code:
  File "L:\devel\renpy\renpy\python.py", line 1190, in __init__
    self.context = renpy.game.context().rollback_copy()
  File "L:\devel\renpy\renpy\execution.py", line 717, in rollback_copy
    rv.abnormal_stack = list(self.abnormal_stack)
MemoryError:
Ren'py already used 1.5 GB at this time. And like Ren'py use the 32 bits version of Python :
Code:
[...]\super powered - 0.38\lib\windows-i686>python -v
[...]
Python 2.7.10 (default, May 23 2015, 09:40:32) [MSC v.1500 32 bit (Intel)] on win32
the copy made it hit the which is of 2GB. Ren'py received the MemoryError exception thrown by Python (on its own, because it thought that the limit was here), logged it and silently died.

The fact that the test was running without image have no impact on the result since the default cache size used by Ren'py for the image is of . On real use, the error would have happened around the 55 000 entries.



We also know stacks respond both in fill and reply slower the more they are filled up. It has shit to do with the age of the machine it is a bi product of how they are coded!
In most modern languages (if not all), internally arrays are created to have X entries by default, then have their size increased by X once they are filled. This while the addition to an entry at the end of the array is just the assignation of a pointer. And you don't need more time to do this if the array have a million entries, than you need if it only have three entries ; unless the implementation is shitty and don't keep track of the next free index.
The only point that can possibly slowdown the process because of the size of the array, is when the said size is extended. Yet it only apply if the whole array (the pointers) is copied to the newly allocated memory space. Even if the array is implemented as a chained list, in place of a more effective algorithm, it will only need the time necessary to the allocation of the new memory space and the update of the last segment of the array ; so it will be atomic even with older computers.

The size of the array is a cost only when you need to access a specific entries, or to perform manipulation on the array. But here it's a LIFO stack. Everything always happen at the end of the array, which is assured to be present on all the caches, and everything is just a writing or reading of a pointer. Therefore it's atomic with any CPU since the early 90's ; even slower algorithm should need less than 20 ticks to do this.
The only impacting point is, like I said above, the (shallow) copy made during the rollback.



You also have a false assumption that if the screen isn't changing or the person isn't responding to dialogue that nothing is going on in the back ground. That's probably true for most games it however isn't true for all games not even those written using Renpy.
The truth is the screen could be refreshing countless times given it is only limited by pythons and SDL's performance on the given hardware.
And ? All this have nothing to do with the call statements.

A Ren'py game itself is purely 100% events free. You can't interrupt the flow of the game, you can just change it. The fact that the screens are updated more or less every 5 seconds when there's no changes in the game, and the fact that Ren'py pass its time to predict the future statements, have no influence on the size of the return stack.
As for the influence on the RAM user space, it happen only at the start of the game, before the cache is effectively filled up to its limit. Therefore, this influence happen before the size of the return stack start to possibly be a problem.

The said stack will grow only when a call statement is proceeded, and the said statement can only be proceeded after an interaction coming from the player. There can be more than one call behind this interaction, but they'll all stop once Ren'py end its "interaction" ; so once a dialog line will be displayed, a screen will be called, or the renpy.input will be used.



The Roll back feature is a large part of lag without a doubt. But it's limited in how much you can go back.
Absolutely not.
The problem implied by the rollback is that it (shallow) copy the variables, including the return stack. The time needed to make this copy is obviously 100% dependent of the size of the return stack, and 0% dependent of the number of copies you previously made.
You can have 1 maximal rollback points or 1000, you'll need the same amount of time to copy a 1000 entries list. A time that is bigger than the one needed to copy a 10 entries list.

This said, a smaller number of rollback point permit you to have a bigger return stack, since it occupy less space in RAM.


From Bjarne Stroustrup (developer of c++) He mentions Dijkstra's paper "Goto considered harmful"
Or for those who don't want/can't log on Google, .
 
  • Like
Reactions: sakosux

User #1751331

Member
Oct 30, 2019
193
156
Where ?
I searched here and on google, I found nothing. Well, nothing except your recent messages on this subject in a game thread. But without more proof of the problem than you gave here.
I searched in Ren'py's code, there's no test of the stack limit. I grepped it, at no time the word "stack" is used in an error message.

So, I performed some test. The size of a return stack entry being relatively fix, this was enough to have a better view of the problem :
Code:
label called:
    $ depth = renpy.call_stack_depth()
    "Stack depth [depth]"
    call called

label start:
    call called
and there nothing like a "stack filled error".

There's an error when Ren'py come around the 62 000 entries in the return stack, but it's a memory use one :
Code:
  File "L:\devel\renpy\renpy\python.py", line 1190, in __init__
    self.context = renpy.game.context().rollback_copy()
  File "L:\devel\renpy\renpy\execution.py", line 717, in rollback_copy
    rv.abnormal_stack = list(self.abnormal_stack)
MemoryError:
Ren'py already used 1.5 GB at this time. And like Ren'py use the 32 bits version of Python :
Code:
[...]\super powered - 0.38\lib\windows-i686>python -v
[...]
Python 2.7.10 (default, May 23 2015, 09:40:32) [MSC v.1500 32 bit (Intel)] on win32
the copy made it hit the which is of 2GB. Ren'py received the MemoryError exception thrown by Python (on its own, because it thought that the limit was here), logged it and silently died.

The fact that the test was running without image have no impact on the result since the default cache size used by Ren'py for the image is of . On real use, the error would have happened around the 55 000 entries.

In most modern languages (if not all), internally arrays are created to have X entries by default, then have their size increased by X once they are filled. This while the addition to an entry at the end of the array is just the assignation of a pointer. And you don't need more time to do this if the array have a million entries, than you need if it only have three entries ; unless the implementation is shitty and don't keep track of the next free index.
The only point that can possibly slowdown the process because of the size of the array, is when the said size is extended. Yet it only apply if the whole array (the pointers) is copied to the newly allocated memory space. Even if the array is implemented as a chained list, in place of a more effective algorithm, it will only need the time necessary to the allocation of the new memory space and the update of the last segment of the array ; so it will be atomic even with older computers.

The size of the array is a cost only when you need to access a specific entries, or to perform manipulation on the array. But here it's a LIFO stack. Everything always happen at the end of the array, which is assured to be present on all the caches, and everything is just a writing or reading of a pointer. Therefore it's atomic with any CPU since the early 90's ; even slower algorithm should need less than 20 ticks to do this.
The only impacting point is, like I said above, the (shallow) copy made during the rollback.

And ? All this have nothing to do with the call statements.

A Ren'py game itself is purely 100% events free. You can't interrupt the flow of the game, you can just change it. The fact that the screens are updated more or less every 5 seconds when there's no changes in the game, and the fact that Ren'py pass its time to predict the future statements, have no influence on the size of the return stack.
As for the influence on the RAM user space, it happen only at the start of the game, before the cache is effectively filled up to its limit. Therefore, this influence happen before the size of the return stack start to possibly be a problem.

The said stack will grow only when a call statement is proceeded, and the said statement can only be proceeded after an interaction coming from the player. There can be more than one call behind this interaction, but they'll all stop once Ren'py end its "interaction" ; so once a dialog line will be displayed, a screen will be called, or the renpy.input will be used.

Absolutely not.
The problem implied by the rollback is that it (shallow) copy the variables, including the return stack. The time needed to make this copy is obviously 100% dependent of the size of the return stack, and 0% dependent of the number of copies you previously made.
You can have 1 maximal rollback points or 1000, you'll need the same amount of time to copy a 1000 entries list. A time that is bigger than the one needed to copy a 10 entries list.

This said, a smaller number of rollback point permit you to have a bigger return stack, since it occupy less space in RAM.

Or for those who don't want/can't log on Google, .
I didn't have any problems searching and finding it. I found it searching "call stack filled", "return stack filled", "Jump call stack" ... But I also use different browsers and accounts for searching. My chrome browser I only search programming related stuff on. I keep search for anything else including porn on firefox or tor ... Google's AI produces different results on those browsers even though they are at the same IP. I have no idea what your search history is like and how google's AI has generated it's search results for you. I even found it on the Lemmasoft forums. So not that hard to find.

Actually it's 4Gb max memory under 32bits. Not sure why Renpy is only using a medium size memory model. It could be a limitation of 32 bit python. The max memory size for both windows and linux 32 bit programs is 4GB.

Actaully arrays in C,C++, Pascal and most other compiled languages have fixed size arrays. They don't increase in size.
C++ has a vector that acts like an array but can increase in size.
Java has an array that acts from my understanding like the C++ vector.

Suggest you also look up the source for C++ vectors online, free Java and pythons array types. That way you might be able to understand why expandable arrays get slower over time.

You are miss understanding to much of what I am saying.

I'm going to leave it at we disagree. That and this is now taking more time than I wanted to put into it I have my own projects to get back to. sorry.
 
  • Like
Reactions: hiya02

riktor

Active Member
Nov 26, 2018
906
1,161
I can't recall a single renpy game that actually suffers from this, granted I've played fewer than 100. if i were to nitpick devs general use of the call function it would be that much of the time what they are using it for would be better accomplished (imo) with custom functions (or even screen calls in some cases) rather than renpy label calls.
 
Last edited:
  • Like
Reactions: sakosux

anne O'nymous

I'm not grumpy, I'm just coded that way.
Modder
Donor
Respected User
Jun 10, 2017
10,391
15,307
I didn't have any problems searching and finding it. I found it searching "call stack filled", "return stack filled", "Jump call stack" ... [...] I even found it on the Lemmasoft forums. So not that hard to find.
Then go, why don't you give some links ?


Actually it's 4Gb max memory under 32bits. Not sure why Renpy is only using a medium size memory model.
It could be a limitation of 32 bit python. The max memory size for both windows and linux 32 bit programs is 4GB.
Like I explicitly said in my comment, and like was said on the link I gave, the limit come from Python. So the "could be" is at least superfluous.

The 4GB is the physical limit due to the x86 CPU architecture. It's possible to over pass it by using the of the said CPU family, but not all version of Windows use them, the few firsts version of MacOS X don't used them, and not all Linux kernels have them enabled by default.
This limit is later in order to keep 2GB for itself and offer 2GB as user space for the process. This avoid the complications, slowdowns, and threats, that would inevitably happen (due to the system design) if the system was switching between to RAM blocks for a single process. With Linux kernels, the difference in design let you access the 4GB when the PEA support isn't enabled, same for MacOS X if I remember correctly.
Be noted that in fact many version of Windows can be told to perform a 1GB/3GB split, but Python follow the default 2GB/2GB one. This for the same reason than Ren'py use a 32 bits version of Python, in order to keep a maximal compatibility.


Actaully arrays in C,C++, Pascal and most other compiled languages have fixed size arrays. They don't increase in size.
We are talking about Ren'py, so about Python, and I explicitly talked about "most modern languages". Plus, by itself what I said explicitly limited the subject to dynamic arrays.


Suggest you also look up the source for C++ vectors online, free Java and pythons array types. That way you might be able to understand why expandable arrays get slower over time.
May I interest you in some reading about and ? It could be an interesting reading, since it explain why, for LIFO stacks, the cost is sensibly the same than with a fixed array.
But well, it's nothing new, just what I said, explained better ; including the fact that this slowdown is not effectively sensible before you reach values that will never be reach by Ren'py return stack in real life condition.



I'm going to leave it at we disagree.
Oh, but I don't disagree. Most of what you said is true. It's just that, when it's not outdated, or miss understood, knowledge, it don't apply to Ren'py, which is the subject you choose to talk about.