Out-of-memory errors that only happen on the "real" device, but are fine in the simulator. Any suggestions?

I have a DataField that shows many different things and does some calculations as well. I have been jumping through hoops keeping the memory down, and I am right now targetting it for my watch (FR630) with has a memory budget of 28.6kB per DataField (as reported in the simulator). The simulator reports a usage of 26.2kb at the bottom of the screen and  27.3kB as Peak Memory in the Active Memory window. It does not fail. When I load it onto my watch (sideload or as beta from the store, doesn't seem to make a difference) it fails and the CIQ_LOG.YML file reports out of memory error. 

The errors seem to be related to the DataField "re-drawing" itself, as they typically happen when I switch to a different screen or when I hit the Lap button and the Lap Summary screen appears. 

Any suggestions of how to do any (or all!) of the following:

- get the simulator to force a redraw event so I can see the same behavior. 

- in general, get the simulator to match the behavior of the watch. It is very time consuming to sideload the app repeatedly for debugging.

- reduce memory. In particular, are there any "best practices" for minimizing the layout and graphics (I create a "drawable" that is composed of a bunch of rectangles to create separator lines. I could not find a different way to draw a simple line...). I saw that there was a way to draw directly onto the screen (without using layouts and drawables, but it seemed much more complicated. Is it more memory efficient and therefor worthwhile to figure it out?

- I already minimized my class variables by doing most of my calculations in the onUpdate() call. Are there some other tricks to minimize memory usage? 

Thanks for any help and suggestions!

Gerald

  • The 630 is a CIQ 1 device and the max for a data field is 16k.

    Are you sure you're not using the 645 in the sim but sideloading to a 630?

  • Sorry, Jim, just a typo. I meant to type 645, and I did sideload to a 645. I also have a 630, but have nit used it in a while...

  • One thing I'd look into (and this is back from the 16k DF days) is eliminate using layouts and just do dc calls.

    Using dc calls really isn't that complicated once you understand the basics.  That's how I do all my apps.

  • I'll give it a try! Hopefully it is not going to drive me too crazy. I am already placing every item in the layout manually anyway, so it might not be that bad. How does this work once you want to port it to different screen sizes/shapes? 

  • What I do (in general) is in onLayout(), I get the dc width and height, and using that, compute the x and y for things, and then in onUpdate, do dc.drawText() calls for those x and y's

  • First of all thank you Jim for your patience and suggestions, and thank you FlowState for the link to the best practices thread! I recoded my Datafield to draw directly to the dc, and the memory drop was phenomenal (19.2kB peak down from ~28kB). I might need to add more features to make up for it ;). For now it will only work for screens of the same size and shape as the FR645, but I see how to extend it to other shapes and sizes. I'm just not quite ready to put in that work until I work out a few more bugs. I am not sure I'll want to calculate all the x's and y's as I am displaying a lot of different values (16, between actual values and labels, I'm using the full screen), so I'm afraid if I add those as class variables, my memory usage is going to blow up again, but I think I can precompute a few anchor points, and then offset the locations from there. 

  • In my experience, dynamically calculating all the points can be super memory intensive, depending on the use case. For example, there's a nice data field in the store which uses dynamic calculations with DC calls in order to apply a multiple datafield layout to many different kinds of devices. Unfortunately, the code for doing this is so big that it can't be ported to old 16 KB devices like FR230.

    When I had a similar problem, I used a hybrid solution which had draw-to-DC calls with hardcoded device-specific coordinates/layout data. For really old devices (like FR235), JSON data was not available so I returned the layout data in a function. For newer devices, I put the layout data in JSON data which saved even more memory.

    TL;DR I wrote my own layout engine and hardcoded the input data.

    - Advantages: was able to optimize the code and data representation to maximize memory savings (e.g. by using bitpacking, arrays of UINT32, etc.)

    - Disadvantages: had to create the data by hand. (Although I should've written a tool do so by now haha.)

    Of course another way to do it would be to just have different versions of your draw-to-DC function for each class of device (depending on how specific it is to this particular app).

    My use case was where I needed something generic that would support multiple layouts (selectable by config) and also be portable to different apps.