How to deal with low-memory devices

Hi,

On my quest to support more and more devices I have taken on the FR745 this evening. It seems that on the FR745 there is less memory available for glances than on other glance devices I tested so far (Epix Gen 2, Venu 3S, FR265). My app makes web requests to a third party server, and the response is relatively large, too large for the FR745 to process it in the glance. So I need to implement the glance differently and only show static information.

How could I implement such different behavior. Is there a way to check in the code how much memory is available? Or have code execute only for certain device types? Only way I could think of right now is to have a property in the device-specific resource folder, that tells me I should do something different. Is that the way, or is there something better?

Regards, Robert

  • Yes, I have to say I was not aware enough of the memory limitations when I wrote my original code, and over time I think I'll improve it to use up less space.

  • Regardiong the (:glance): as far as I remember then the non-glance (foreground) app does see the functions, classes annotated with (:glance), so for that you don't need to duplicate the file IMHO.

    When you'll use Prettier Monkey C then it'll also remove the calls to log() :) If it's written right. I use VSC, so maybe you mixed me with someone else.

    Drop-down: no, when you have a launch.json then you can see the drop-down in the Run tab, and chose for example Run (without Prettier Optimizer, Debug) | Run (With Prettier, Debug) | Run (With Prettier, Release)

    Debug is always bigger, not only because of the code/data you might include for logging purposes, but also when there are no annotations,. because it includes some other strings, like human readable function, variable names etc. I'm not sure though if that also has an effect on the memory size, or just increases the prg size.

    It's most probably a debug build either way. The difference between the Run, and Run w/o debugging is not the build type, but whether it runs in the debugger (and you can add breakpoints, watch variables, etc) or it just runs.

  • Sorry but this is a stupid idea. It has all the disadvantages of the other solutions (need to create and maintain the jungle file) + instead of solving the low memory problem it makes it worse (every device has ALL the code + needs even more to read the json).

  • Regardiong the (:glance): as far as I remember then the non-glance (foreground) app does see the functions, classes annotated with (:glance), so for that you don't need to duplicate the file IMHO.

    Yes, it does, but the problem is there is only one built-in annotation :glance and I cannot replicate its behavior into another annotation for the tiny glances.

    So if I have four cases:

    a) normal glance
    b) normal widget
    c) tiny glance for low-memory devices
    d) normal widget for low-memory devices

    For a) and c) I have two different source directories, containing the glance view (glance and tinyglance), and monkey.jungle defines which one shall be used.

    Now, let's say a class X is needed in a), b) and d) but should not be loaded for c).

    If I use :glance for X, it will be included in a) and c) but not d). If I use an exclude annotation for low-memory devices, it will be loaded for a) and b) but not c) and d). If I combine :glance with an exclude annotation it will be loaded for a) only.

    But there seems no way I get it to load for a), b) and d).

    Except if I duplicate the source file for X, put one in the glance and tinyglance source directories each, and the one in glance has the :glance annotation, the one in tinyglance doesn't. Then the class will be loaded for a) and b) and d) but not for c).

    Or am I missing something?

    When you'll use Prettier Monkey C then it'll also remove the calls to log() :) If it's written right. I use VSC, so maybe you mixed me with someone else.

    That would be cool, thanks, I'll take a closer look at Prettier Monkey C.

  • One thing to understand is how (:background) and (:glance) work (or it did last time I looked)

    The prg file is organized like this:

    header

    (:background) code

    (:glance) code

    the rest of the main app

    ---

    So, when the background runs it loads up to the end of the (:background) code.

    When a glance runs, it loads up to then end of the (:glance) code.

    With the main app, it loads everything

    So, if you have 32kb for both background and glance, the background has 32kb or if you only have a glance, it has 32kb, but if an app has both, the amount of background code limits the glance.  This is why I tend to keep my glance view as minimal as makes sense.

  • 1. Start to use Prettier! There's an annotation: (:inline), when you use it the function will be inlined. So you can do something like:

    (:inline)
    function a() {}

    function b() {
      a();
    }

    You can also pass parameters to a, and return something. There are some constrains, but in most cases it can be used as is and in some cases a small refactoring can fix it.


    It can be especially useful for "aliases". For example if you want to save memory then you don't want to use has (though with SDK 7 this is better, but in some cases you don't want it to be in compile time, on the other had you want for the old devices as little code as possible), so instead of:

    function setConfig(key as PropertyKeyType, val as PropertyValueType) as Void {
        if (Application has :Properties) {
            Properties.setValue(key, val);
        } else {
            ((MyApp as Object?) as MyApp).setProperty(key, val);
        }
    }
    

    you could use:

    (:no_ciq_2_4_0, :inline)
    function setConfig(key as PropertyKeyType, val as PropertyValueType) as Void {
        ((MyApp as Object?) as MyApp).setProperty(key, val);
    }
    (:ciq_2_4_0, :inline)
    function setConfig(key as PropertyKeyType, val as PropertyValueType) as Void {
        Properties.setValue(key, val);
    }
    

    the annotations ciq_2_4_0 and no_ciq_2_4_0 need to be "generated" in the monkey.jungle file (I have this script for that: https://github.com/flocsy/garmin-dev-tools/blob/main/monkey-generator/monkey-generator.py)

    2. Do you know that you can combine multiple annotations? It's a bit confusing, because the built in annotations (:background, :glance, :test, etc) are functioning more like "includeAnnotation", and the ones you define are excludeAnnotations, and there are cases where it really can't be done without copy&pasting (I opened some feature-requests, i.e: https://forums.garmin.com/developer/connect-iq/i/bug-reports/feature-request-add-positive-annotations-to-source-folders ) But you can look at them as different "dimensions" and do things like:

    (:glance, :lowMem)
    function foo() {some code}
    (:glance, :high)
    function foo() {more code}

    (:glance) // but if you need this in the widget as weel it's also OK, just remove the annotation
    function common() {} // the above foo-s can both call bar, and thus duplicate as few code as possible

    3. You can build your code like LEGO. The real stuff is not duplicated, and is either extended or called from where it's needed.

  • I understand your concerns.

    No, you actually don't. I'm not sure what your motive is but your suggestion is neither correct nor is it using the proper language.

    UPDATE: I found the motive; this post: https://forums.garmin.com/developer/connect-iq/f/discussion/373593/integrating-a-grade-calculator-for-enhanced-training-and-educational-apps -- I suggest everyone on this thread go there and report the post as abusive.

  • 2. Do you know that you can combine multiple annotations? It's a bit confusing, because the built in annotations (:background, :glance, :test, etc) are functioning more like "includeAnnotation", and the ones you define are excludeAnnotations

    Yes, I know that. But like I wrote in my example, also combining :glance with an exclude annotation will not lead to the desired result:

    If I use :glance for X, it will be included in a) and c) but not d). If I use an exclude annotation for low-memory devices, it will be loaded for a) and b) but not c) and d). If I combine :glance with an exclude annotation it will be loaded for a) only.

    and there are cases where it really can't be done without copy&pasting (I opened some feature-requests, i.e: https://forums.garmin.com/developer/connect-iq/i/bug-reports/feature-request-add-positive-annotations-to-source-folders )

    I think for my case, even the include annotations you propose would not help. They could again only include code for the whole device, i.e. c) and d) in my example, but not define device-specific code that is only included for the glance.

    I think one way to solve it would be to have boolean operators for annotations. The jungle file would only include a list of annotations per qualifier. Exclude or include would be controlled via the NOT operator in the source code. Built-in would continue to work a bit differently. :glance not being present would equal to NOT :glance, but a custom annotation not being present would mean it is simply not being evaluated.

    Then I could define: ( :glance AND NOT :device ) and my problem would be solved.

    Another way would be to define a qualifier-specific annotation that matches the functionality of the built-in annotation. If I in jungle could define something like:

    fr745.glance.includeAnnotation = tinyglance

    And then annotate all the classes I need for my tiny glance with :tinyglance, I could solve it as well. My class X would be (:glance) only, but classes I need for both the normal and tiny glance would be (:glance :tinyglance).

    1. Start to use Prettier!

    I read the documentation on it a bit, but I could not find how it would help me with the code duplication in my case. Or am I missing something?

    There's an annotation: (:inline), when you use it the function will be inlined.

    Is making them :inline is how you excluded your debug log calls?