Best practices for reducing memory usage?

Former Member
Former Member

Hi all,

I am currently sitting at 86kb / 91.8kb for my first Garmin watchface, and I'm looking to reduce memory usage. 

So far, I've found the following has helped quite a bit:

  • Reducing the amount of bitmaps being used, when possible (by using fonts or drawing shapes instead)
  • Reducing or removing unused code, such as calls to System.println() and others
  • Only loading fonts necessary to the appropriate screen resolution 

What other memory reduction tips do you have?

Thank you!

  • If you use app settings, be sure to test that in the sim, (you can by way of eclipse) as that will cause an increase in your peak memory..

    Not sure where the stuff from the 2017 summit is anymore.  The whole forum SW has changed since then so there are links that no longer work.

  • I've come across this problem a lot, especially when developing data fields for older / midrange devices with 16 - 32 KB of available memory.

    None of these is "official" advice, it's just stuff I've used for developing my own personal apps.

    Keep in mind that most or all of these tips will make your code very hard to maintain and read. A lot of them are anti-patterns and worst practices, under normal circumstances.

    - Use if instead of switch

    - Where possible, use an array lookup table instead of if. (e.g. Design your "enums" so they're contiguous numbers from 0..MAX, so you can use array lookups)

    - Where possible, use an array lookup table instead of a dictionary. (Dictionaries are very expensive)

    - Use hardcoded constants instead of enums (I put the enum name in a comment next to every usage of a hardcoded constant)

    - Manually inline function calls where the function is a single line of code

    - Avoid using classes when possible (Class instances are expensive). For example, if I want to implement a stack or a queue, I'll just use static functions and pass in the "this" reference.

      - Similarly, when a function needs to return multiple items, return an array instead of an object

      - Similarly, instead of throwing a custom exception, I use a global error flag variable

    - Flatten nested arrays where possible

    - Eliminate local variables where possible

    - Use short property names

    - Reduce the number of string resources in your app, even if they're only used for app settings. (Unfortunately, strings used for Connect IQ app settings still take up memory at run-time, even if the app itself never uses them. This is assuming that you call loadResource() at least once)

    - As you pointed out, remove System.println() calls. Not only is this is a waste of code, but just calling System.println() causes a memory spike which can push your app over the limit

  • Former Member
    Former Member over 4 years ago in reply to FlowState

    Fantastic advice, thank you very much. Going to spend some solid time today going through all of these. I can already see tonnes of mistakes that I'm making.

    Also, just a note to anyone else reading, I found the 2017 CIQ Developer Summit memory practices video here.

  • No worries!

    Well, I wouldn't call what you're doing now "mistakes", as I'm sure every dev's default instinct is to write code that's as beautiful, well-designed and maintainable as possible. It just so happens that writing nice code wastes a lot of memory in Connect IQ's very constrained environment.

    Another trick that I've used is to store static app data in bit-packed arrays of (32-bit) integers. If your data is only 8 bits wide, for example, this represents a 4X memory savings. For devices that support JSON resources (CIQ 2+), I load this kind of data from a resource. which saves even more memory. One example of this kind of data is static and dynamic screen layouts (for full-screen data fields). (Connect IQ's own layout system consumes a lot of memory, so I wrote my own layout system.)

    Another note about resources: there's a big difference between calling loadResource() 0 times, and loadResource() 1 or more times. If you call loadResource() at least once, then all the resource tables will be permanently loaded into application RAM, including settings strings. The resource contents themselves don't consume RAM (until you load them), but each entry in a resource table will consume RAM.

    So if you have a data field with app settings and non-localized strings, it may actually be more memory-efficient to simply hardcode strings within the app, instead of loading them from resources. (Assuming you have no other resources to load.)

    More memory-saving tips:
    - eliminate array / member variable accesses where possible. In this case, using a local variable to store the initial access actually saves memory.

    - Avoid using Longs and Doubles where possible, as these have additional overhead compared to Number and Float IIRC

    EDIT:

    - if you have a large block of static/const data in your app which is infrequently accessed, consider creating a function to return the data, so that the associated object(s) don't consume memory all the time. (Again, for CIQ 2+ devices, this is something that would probably be best stored in a JSON resource.)

  • but...

    - switch is faster then if... I'm not sure but test some examples in my code...

    - hiding variable in function may produce stack overflow because  there is peek of memory when enter to function

    my tricks

    - use the same var in function

      var temp;.

      temp = ...;  draw(temp)

      temp=something else;  print(temp)

      temp=value; temp = array[temp];

      for(temp=....)

    code is ugly but... memory

    - the same code for globals if I'm sure that it' can use this way

    - use $. for performance and looking fast for globals

    - filter chars from fonts

    - remove empty pixel form fonts use x/y offsets

    Name var/const to can replace it automaticly for example

    const ICON_X = "X";

    const ICON_y = "y";

    you can write macro replace(ICON_?;?) and change code before compile

    - load big data before system load big data and free everything what is needed in future (for example getDeviceSettings but it's not good example because it contain dynamic values )

    - use system object for example fonts

    gMyFont = (gWidthDC < 250) ?  GRA.FONT_SYSTEM_NUMBER_MILD :  WatchUi.loadResource(Rez.Fonts.MyFont);

    - use as to short  names using

      using Toybox.Graphics as GRA;

      GRA.FONT_SYSTEM_NUMBER_MILD instead of Graphics .FONT_SYSTEM_NUMBER_MILD (not sure if it help for memory but I like to have "order" in code)

    - use "hidden" system var locX, locY in dravable

    - destroy unused var

      I have in table all drawable and destroy mLayout because  I draw myself not by system

     - use set/getproperty func to save data that not use frequently

    - use flags and pack it into integer

    - name good property to read it in loop

      for(temp....){ field[temp] = getProperty("Fld"+temp);}

  • - hiding variable in function may produce stack overflow because  there is peek of memory when enter to function

    Yes that could be true if you're not careful (except instead of a stack overflow it would just be an out of memory condition.) The trick is to reduce your *minimum* memory footprint and carefully track your peak memory. None of these are one-size-fits-all solutions, you have to selectively apply them when it makes sense.

    For example, I have some const array data for my own custom layout scheme, which is only required at app init time. If I didn't wrap the var in a function, then that array would take up memory for the entire lifetime of the app. By wrapping it in a function, it only takes up (peak) memory when the app starts up. So while the app is running, I can use that memory for something else. (Of course, the code itself always consumes memory, either way.)

    Another personal example is data for a series of nested settings menus for a device app. I only need that data when the user is in the setting menu, and not at all times while the app is running.

    - A const still consumes memory. Using hardcoded constants still takes less memory than a const.

    EDIT: Sorry, I missed the part where you said you replace the macros before compilation. Yeah, you can use the C preprocessor for this.

    - use flags and pack it into integer

    Yeah, that's what I meant by bit-packing.

  • if you use BufferedBitmap

    - set palette with colors you use in bitmap, but

    -- you can't draw text with anti-aliased fonts on bitmap with palette

    -- you have to recreate bitmap if you need other colour to draw on bitmap

    - count every pixel, every - i draw bitmap on different than background colour and observe if it is not bigger than need

    - destroy bitmap if it not longer needed (for example user hide drawable) - simple mB=null;

  • Also, instead of 

    GRA.FONT_SYSTEM_NUMBER_MILD

    You can use :

    14

  • but is hard core coding Slight smile

    we can go back in time and code in assembler as well

    MOV AL, 1h        ; Load AL with immediate value 1
    MOV CL, 2h        ; Load CL with immediate value 2
    MOV DL, 3h        ; Load DL with immediate value 3
  • I do agree but it saves a lot of memory if you do so. 

    The same for every enum 

    And like FlowState said

    You can put 14 /*FONT_SYSTEM_NUMBER_MILD*/ 

    I did it on a wf and I saved around 2kb