View Issue Details

IDCategoryLast Update
0002678Bug Report2021-10-26 22:10
Reporterskrskrskr 
SeveritycrashReproducibilityalways
Status newResolutionopen
Summary0002678: Repeatedly saving and loading a game increases save fiile size dramatically
DescriptionHi,

I had a strange non-reproducable crash, which turns out to be reproducable quite well actually. What makes it worse is the fact that it tends to happen when we follow the official recommendation to save often.

Problem: Loading any save file, then save it again in a new slot without advancing too far in-game (say, load and save right away), will increase the size of the produced save by roughly 1.2 MB each time. This means, doing this three times will bring the file from it's normal size of about 300 KB to 4 MB. Doing this two more times will lead to an out-of memory error on my machine.

This can be reproduced right after starting a new game with a fresh installation, having deleted all persistent data before. After the funeral, when you are in MC's room for the first time, save the game in slot 1. Load from slot 1 and save to slot 2. Load from slot 2 and save to slot 3 and so on. You will see that each file is 1.2 MB larger.

If you advance in-game for a while, e.g. by doing quests or sleeping, eventually the size of newly created saves will decrease again to the normal value. It will take sleeping about three to six times before the size is reduced again.

This means that it is not a good idea to save too soon after you run the game and load the latest save.

I reproduced this in game version 0.19.5 as well as 0.19.1. Earlier versions also increase the file size following my description, but only by about 100 KB each time.

I did some research and took a look into the save_dump.txt files produced when setting config.save_dump = True. In the case that a file of normal size is saved, the dump has about 94 MB. Saving the second time produces a dump of about 190 MB already. Comparing the two with WinMerge 64bit (32bit version will crash) reveals that at the bottom, the larger file has about 4 million lines like that:

      0 log.log[126].objects[0][0][0].parents[0].can_leave = alias True
      0 log.log[126].objects[0][0][0].parents[0].var_name = alias 'L_school_hall'
      0 log.log[126].objects[0][0][0].parents[0].parents[0]._label = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].is_door = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].name = alias u'School Frontyard'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].sounds._ambiance = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].sounds._ambiance_night = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].sounds.location = alias <Location>
      7 log.log[126].objects[0][0][0].parents[0].parents[0].sounds = <LocationSounds>
      0 log.log[126].objects[0][0][0].parents[0].parents[0]._display_name = alias None
      0 log.log[126].objects[0][0][0].parents[0].parents[0]._locked = alias True
      0 log.log[126].objects[0][0][0].parents[0].parents[0].can_leave = alias True
      0 log.log[126].objects[0][0][0].parents[0].parents[0].var_name = alias 'L_school_front'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0]._label = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].is_door = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].name = alias u'Town Map'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].sounds._ambiance = alias u'ambience_suburb.ogg'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].sounds._ambiance_night = alias u'ambience_suburb_night.ogg'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].sounds.location = alias <Location>
      7 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].sounds = <LocationSounds>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0]._display_name = alias None
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0]._locked = alias True
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].can_leave = alias True
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].var_name = alias 'L_map'
      1 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].parents = <RevertableList>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].background_fn = alias <function>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0]._bg = alias u'ground'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].temporary_locked = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[0] = alias <Location>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1]._label = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].is_door = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].name = alias u"Diane's Front Yard"
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].sounds._ambiance = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].sounds._ambiance_night = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].sounds.location = alias <Location>
      7 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].sounds = <LocationSounds>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1]._display_name = alias None
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1]._locked = alias True
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].can_leave = alias True
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].var_name = alias 'L_diane_yard'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].parents[0] = alias <Location>
      2 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].parents = <RevertableList>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].background_fn = alias <function>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1]._bg = alias u'diane_front'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].temporary_locked = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0]._label = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].is_door = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].name = alias u"Diane's Garden"
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].sounds._ambiance = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].sounds._ambiance_night = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].sounds.location = alias <Location>
      7 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].sounds = <LocationSounds>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0]._display_name = alias None
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0]._locked = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].can_leave = alias True
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].var_name = alias 'L_diane_garden'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].parents[0] = alias <Location>
      2 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].parents = <RevertableList>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].background_fn = alias <function>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0]._bg = alias u'diane_garden'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].temporary_locked = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0]._label = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].is_door = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].name = alias u"Diane's Kitchen"
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].sounds._ambiance = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].sounds._ambiance_night = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].sounds.location = alias <Location>
      7 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].sounds = <LocationSounds>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0]._display_name = alias None
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0]._locked = alias True
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].can_leave = alias True
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].var_name = alias 'L_diane_kitchen'
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].parents[0] = alias <Location>
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].parents[1]._label = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].parents[1].is_door = alias False
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].parents[1].name = alias u"Diane's Lobby"
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].parents[1].sounds._ambiance = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].parents[1].sounds._ambiance_night = alias u''
      0 log.log[126].objects[0][0][0].parents[0].parents[0].parents[0].children[1].children[0].children[0].parents[1].sounds.location = alias <Location>

Those lines seem to be missing in the smaller dump.
Platform/OSWindows
Version0.19.5

Activities

skrskrskr

skrskrskr

2020-05-24 00:06

reporter   ~0004920

Problem still present in 0.20.1
skrskrskr

skrskrskr

2020-11-21 16:35

reporter   ~0005145

Problem still present in version 0.20.7
skrskrskr

skrskrskr

2021-04-06 16:01

reporter   ~0005255

Problem still present in 0.20.9

I investigated a bit from another side. In file state_machine.rpy, in method Machine.__init__, there is at the end the initialization of two class members:

            self.backup_default_loc = deepcopy(default_loc)
            self.backup_vars = copy(self._vars)

Those beckup_* variables are used in only one place: method Machine.set_state. And this method set_state is not used at all.

This means that generally, the (deep)copies of those variables are not needed at all. I patched this file, replacing with the following:

            self.backup_default_loc = default_loc
            self.backup_vars = self._vars

This means that the backup_* variables are only references to their templates instead of copies, but since they are never used, it does not make a difference (at least if I am not overseeing something).

As a result, the saves being produced (see initial problem description) do no longer grow by 2 Megabytes each time but only by some few hundred Kilobytes, i.e. out-of-mamory situations are much less likely to occur.

I really hope the dev team would look into this issue. I would think the coming tech update would be a good point to give this serious consideration. I read in the forums many times of players running into this or related problems.
skrskrskr

skrskrskr

2021-10-26 22:09

reporter   ~0005385

OK, version 0.20.12 finally fixes this bug. I am glad you implemented the fix exactly as I outlined it here. You could have at least commented in this bug report, however.

Ticket can be closed now.
skrskrskr

skrskrskr

2021-10-26 22:10

reporter   ~0005386

OK, version 0.20.12 finally fixes this bug. I am glad you implemented the fix exactly as I outlined it here. You could have at least commented in this bug report, however.

Ticket can be closed now.

Issue History

Date Modified Username Field Change
2020-04-02 22:28 skrskrskr New Issue
2020-05-24 00:06 skrskrskr Note Added: 0004920
2020-11-21 16:35 skrskrskr Note Added: 0005145
2021-04-06 16:01 skrskrskr Note Added: 0005255
2021-10-26 22:09 skrskrskr Note Added: 0005385
2021-10-26 22:10 skrskrskr Note Added: 0005386