Maximum .PRG file size error

I am trying to recompile one Application from github from one watch model to another. When I build it I get this error

ERROR: PRG generated exceeds the memory limit of app type 'watch-app' for device id 'instinct2x': 218282 bytes.

What does it exactly mean? Is it max file size of prg file 218282 bytes or it is bigger than maximum allowed by 218282 bytes or what?

When I build it I get 363980 bytes prg file.

Can I find somewhere the list of max oprg files for each watch?

Also, how can I decrease the size of prg file? Maybe change SDK version or some compiler option?

  • So to circle back to the big challenges of porting this app to instinct2x:

    1) The PRG is so much bigger for CIQ 3 devices (instinct2x) compared to CIQ 4 devices (instinct 3 solar) because the code to initialize arrays like TAMA_PROGRAM and OPS is so much more inefficient in CIQ 3. 

    2) There is an additional 80 KB - 100 KB runtime memory hit for the bitmaps on CIQ 3 devices, due to the lack of a shared graphics pool (only present in CIQ 4 and higher)

    3) As if 1 and 2 weren't enough, instinct2x has 30 KB less memory available for watchApps compared to instinct3 

    Possible solutions:

    Challenges 1) and 3): optimize the representation and initialization/loading of TAMA_PROGRAM and OPS. (We can already save 30 KB by rewriting OPS to be more efficient)

    Challenge 2):

    - load/unload bitmaps on demand (probably not possible, but I haven't looked into it too closely)

    - replace bitmaps with code that draws pictures (will this look good and/or even save enough memory?)

     replace bitmaps with plain text / emojis (the worst possible solution but maybe the most realistic?)

    EDIT: it's also possible that the runtime hit for the bitmaps would be far less on instinct2x (a 2 color device) as opposed to fr945lte (a 64 colour device) Maybe it's not the best idea to test on fr945lte haha.

    EDIT2: the bitmaps are actually only about 20 KB on instinct2x (this can be seen by artificially increasing the app memory limit)

  • Thank you all the research you did. I do not have time to try it now and I will try later. Optimizing Ops as an array might already be enough.  I need to try.

    For the bitmaps I think it is not a big problems. Bitmaps are black and white and PNG files can be converted to Black and white BMP. The size is almost the same (b/w bmp uses 1 bit per pixel, not bytes). I already converted a few and it is like 500 bytes in BMP, instead of 400 in PNG.

  • Thank you all the research you did

    No problem, it's a very interesting problem! (It's been mentioned before that the best way to write memory-efficient code in Monkey C is to avoid using any of the language features, like classes, switch statements, etc., which kind of says a lot.)

    For the bitmaps I think it is not a big problems

    Yeah, I made a mistake by testing with fr945lte. On fr945lte the bitmaps are 100 KB, but on instinct2x, they are only about 20 KB. But 20 KB is still a lot!

    I don't think converting the png to bitmap will help at all. The resource compiler does this anyway. (Note that instinct2x does not support png natively)

    Optimizing Ops as an array might already be enough.  I need to try.

    It's not enough. Even if TAMA_PROGRAM is completely commented out and OPS is optimized, instinct2x still crashes with out of memory when loading the icons.

    If I artificially increase the device memory limit, peak memory for the app is 95 KB (per the memory viewer). (Max available memory per the sim is 91.9 KB.) 

    So even without TAMA_PROGRAM or a running emulator, the app is still 3.1 KB over the limit.

    What still needs to happen:

    - the loading of TAMA_PROGRAM needs to be optimized. this is crucial, as the existing initialization in source code wastes 150 KB of code for a 12 KB array on CIQ 3 devices, which makes the PRG too big in the first place. 

    - about 50 KB total [*] of memory savings (not counting the optimization of TAMA_PROGRAM) have to be found. (I already found about 33 KB by optimizing OPS, so you need at least to find 17 KB more savings)

    [*] instinct2x has ~30 KB less available memory for watchApps compared to instinct 3 solar, plus there is the ~20 KB overhead of loading the bitmaps into app memory

  • Found another 13 KB of savings by eliminating a problem where the icon bitmaps were defined both in the layout (as invisible items) *and* loaded dynamically in code (which meant twice as much RAM was used).

    Might be able to save more by getting rid of layouts completely.

    Maybe this isn't so crazy after all.

    EDIT: also, even if storing the 12 KB TAMA_PROGRAM array as JSON requires 2X (or more) the memory to load/parse the JSON resource, we can try to get around this by loading it before other memory intensive things are created/loaded, such as the view, delegate, bitmaps and emulator.

    IOW, the overhead for loading TAMA_PROGRAM from JSON should only be a temporary memory spike, and we should be able to strategically arrange things so this spike happens at a time when we have lots of free memory (at init time, before everything is loaded).

    (I think one problem here is that JSON data can't be directly loaded into a byte array. The easiest way to get around this might be to take the original byte array and create an array of 32-bit values, packing every 4 bytes into a single 32-bit value. EDIT: unfortunately this isn't ideal, since a byte array literally takes 1 byte per element, but an array of numbers takes 5 bytes per 4-byte element - there's an extra 1 byte to signify the object type of each element)

    If we can load TAMA_PROGRAM successfully, we might just need to free up 4-5 KB.

    EDIT: I've got it to the point where I think I only need to free up about 4 KB (peak memory 93.6 KB)

    - How I loaded TAMA_PROGRAM:

    -- encoded TAMA_PROGRAM as 4 JSON arrays of integers (each int is 4 bytes from the program)

    -- allocate byte array for TAMA_PROGRAM on init

    -- load 1 JSON array at a time and copy data to TAMA_PROGRAM byte array (2 JSON arrays can be processed without tripping watchdog timer, so we use a Timer to postpone additional work)

    - How I got peak memory down to 93.6 KB:

    -- only load 1 icon bitmap at a time

    - Additional notes:

    -- had to set MAX_RUN_SIZE to 40 (1/4 of the original value)

    -- removed logging-related code

  • I got it working on instinct2x (without changing the app memory limit). Worst-case peak memory is just under the limit.

    In my testing, the app was functionally unchanged afaik, except for one deliberate test change I temporarily made to ensure an icon is always displayed in the subscreen (this tests the worst case peak memory scenario):

    The final memory saving steps were:

    - convert SEG_POS to hex-encoded string to avoid the huge array initialization penalty

    - change storage keys from strings to integers

    - get rid of all the redundant GET_*_MEMORY and SET_*_MEMORY functions

    - get rid of some unused/redundant code

    I legit didn't think it was possible, but here it is. Only thing is I have no idea how slow this will be on a real device.

    The code is a huge mess tho. I manually transformed big data to hardcoded JSON resources using one-off scripts, but in a perfect world, it would all be automated (so that the JSON resources would be generated from the original source for TAMA_PROGRAM).

    If I have time later, I might fork the project and publish my very rough changes.

  • You are the man! Let me give it a try.

  • I also use the systemStats. It gives you the "big picture" but it won't give you the exact picture exactly when it's the most interesting, because even when there's no crash and you do something like:

    logMemoryStats();
    doSomeMemoryIntenseWork();
    logMemoryStats();

    But this itself won't tell you how much memory the doSomeMemoryIntenseWork() used.

  • logMemoryStats();
    doSomeMemoryIntenseWork();
    logMemoryStats();

    I used this pattern for this particular "project" to determine how much headroom at init time I had when loading a large array from JSON resources. (It was tricky because the JSON data had to be transferred to a preallocated byte array for max efficiency after init). I ended up chopping the JSON data into 4 pieces, just to avoid running out of memory.

    Technically I didn't *need* to to look at the stats - I could've done that part through trial and error

    But this itself won't tell you how much memory the doSomeMemoryIntenseWork() used.

    Sure but if doSomeMemoryIntenseWork() normally crashes, then you can artificially increase the memory limit and look at the peak, assuming you are testing in the sim.

    And if it doesn't crash, you can still look at the peak. If there's other code (before or after) that you think might be the cause of the peak, you can always comment that code out (or otherwise avoid it).

    I think artificially increasing the memory limit is probably one of the most powerful tools for diagnosing certain types of memory issues. (Ofc it doesn't seem to work for the original problem in the OP, where the PRG itself - or at least the code and data sections in the PRG - are too collectively large to be loaded into memory. Perhaps there's a separate memory limit which is defined somewhere else.)

  • Let me give it a try.

    Something I noticed is that the game doesn't really seem to work properly? The tamagotchi animates and everything, and the app menu works, but the game doesn't seem to work properly (e.g. respond to inputs as expected) on either instinct3solar45mm (with the original or modded code) or instinct2x (with the modded code).

    Not sure if I did something wrong or not. I haven't tried the test ROM yet (which would need to be manually converted to work with the modded code, in the same way that the tama ROM was converted.)

    EDIT

    - the emulator seems very slow (perhaps freezing completely and permanently in some cases)

    - the test ROM doesn't even work with the original code (running it results in a watchdog timeout)