Connect IQ compiler tricks?

Former Member
Former Member
Hi everyone!

I guess this question is directed primarily to AlphaMonkeyC, but maybe someone else can shed some light too?

Quite simply, I was wondering how clever the MonkeyC compiler is?
I've been writing a bit of code while playing around with Connect IQ, and in doing that I've wondered, where a simple operation is needed for which there is no method available in the Connect IQ SDK and that is called repetitively in a loop, whether I should call my own function that implements this operation, or if rather I should repeat the implementation in my loop, from a performance perspective.
In other words: is the compiler clever enough to do function inlining?

Furthermore, there are some other things I would like to know concerning compiler intelligence & performance.
E.g.: does the compiler use tricks like bit shifts for instance? Or can we devs gain substantial profit by coding a multiplication * 2 as var twenty = 10<<1 instead of var twenty = 10*2?

Thanks!
  • In other words: is the compiler clever enough to do function inlining?

    The compiler doesn't do function inlining.

    does the compiler use tricks like bit shifts for instance? Or can we devs gain substantial profit by coding a multiplication * 2 as var twenty = 10<<1 instead of var twenty = 10*2?

    Since Monkey C is a dynamically typed language, the compiler doesn't know if it's working with a Float or a Number. There are cases where it could know, such as
    var i = 4;
    i = i * 2;

    but we're not currently checking for these cases as a majority of the time the case would probably be more complex and not easily detected by the compiler without running through all the code paths.
  • Former Member
    Former Member over 10 years ago
    Thanks Kyle!

    It didn't seem unreasonable to think that the compiler would apply tricks wherever possible by inferring a variable's type and then optimizing stuff. But it's ok to know that this doesn't happen so that we can do this ourselves.
    I'm not trying to be an a$$h* here. It's just that I feel like I'm writing code for a platform/devices with very limited resources, and so I want to squeeze out every last bit of performance I can to improve the user experience and to improve battery life and memory usage on the device. Knowing what the compiler can and cannot do helps in this regard.

    Speaking of performance... I started this post with the intention of knowing more about how to improve performance, but I still have no real means to test that performance. Testing in the debugger by using System.Stats and System.getTimer() doesn't yield any usable data, and testing by means of writing to a debug log to a real device doesn't help much either, because the writes to the log take much more time (expectedly) than some call handling in loops, and I only have a Vivoactive watch to test with and no other watch models.
    Do you think there is any chance that a profiling tool will become available in the future that will allow us to test what the performance for our code will be on the real devices?

    BR,
    Sven
  • I think it's a reasonable assumption that the compiler would optimize what it can, and we'll be working on such improvements in the future. For profiling, you could take your timings and then write everything at the end after all the code has ran. That should eliminate any timing issues introduced by using Sys.println().
  • Former Member
    Former Member over 10 years ago
    That profiling using timings isn't working for me :-/
    Maybe I'm doing it wrong?

    Here's some example code:
    using Toybox.WatchUi as Ui;
    using Toybox.System as Sys;

    class IQTestView extends Ui.View {

    var i, j, k, l, jMax, tStart, timer1;

    function onLayout(dc) {
    setLayout(Rez.Layouts.MainLayout(dc));

    k = 0;
    jMax = 6000;
    timer1 = new Timer.Timer();
    timer1.start( method(:callback1), 2000, true );
    Sys.println("Calculating j=0 to j<"+jMax);
    }

    function callback1()
    {
    Ui.requestUpdate();
    }

    function onUpdate(dc) {
    k += 1;
    i = 1;
    j = 0;
    l = 0;
    tStart = Sys.getTimer();
    while (j<jMax) {
    j = k%2 != 0 ? i*2 : i<<1;
    i += 2;
    l +=1;
    }

    if (k>=10) {
    timer1.stop();
    }

    var tEnd = Sys.getTimer();
    if(k%2 == 0) {
    Sys.println("Time: " + tEnd + "::Run " + k + ": " + l + " calculations using i*2 took " + (tEnd - tStart) + " millis.");
    } else {
    Sys.println("Time: " + tEnd + "::Run " + k + ": " + l + " calculations using i<<1 took " + (tEnd - tStart) + " millis.");
    }
    }
    }


    This yields the following output:
    Found Transport: tcp
    Connecting...
    Connecting to device...
    Device Version 0.1.0
    Device id 1 name "A garmin device"
    Shell Version 0.1.0

    Device is a Garmin Epix;
    Device geometry is: square;
    Device has 64 colors.
    Device has 7 buttons
    Calculating j=0 to j<6000
    Time: 543331561::Run 1: 1501 calculations using i<<1 took 0 millis.
    Time: 543333604::Run 2: 1501 calculations using i*2 took 0 millis.
    Time: 543335601::Run 3: 1501 calculations using i<<1 took 0 millis.
    Time: 543337598::Run 4: 1501 calculations using i*2 took 0 millis.
    Time: 543339595::Run 5: 1501 calculations using i<<1 took 0 millis.
    Time: 543341591::Run 6: 1501 calculations using i*2 took 0 millis.
    Time: 543343588::Run 7: 1501 calculations using i<<1 took 0 millis.
    Time: 543345585::Run 8: 1501 calculations using i*2 took 0 millis.
    Time: 543347582::Run 9: 1501 calculations using i<<1 took 0 millis.
    Time: 543349579::Run 10: 1501 calculations using i*2 took 0 millis.
    Complete
    Connection Finished
    Closing shell and port


    The code always takes 0 milliseconds to run... :-/
    If I print the value of j to Sys.println in the while loop, then my "Run x Sys.println" reports values between 30 and 65 milliseconds, which is BS too.
    BUT: if I change the loop to make it run for j < 8000, then I get a Watchdog error saying the code has executed for too long?!
    Found Transport: tcp
    Connecting...
    Connecting to device...
    Device Version 0.1.0
    Device id 1 name "A garmin device"
    Shell Version 0.1.0

    Device is a Garmin Epix;
    Device geometry is: square;
    Device has 64 colors.
    Device has 7 buttons
    Calculating j=0 to j<8000
    Failed invoking <symbol>
    Watchdog Tripped Error - Code Executed Too Long
    in onUpdate (D:\workspaces\connectiq\IQTest\source\IQTestView.mc:29)
    Watchdog Tripped Error - Code Executed Too Long
    Complete
    Connection Finished
    Closing shell and port



    So printing after the code has run doesn't seem to help me for profiling performance... :-/
  • Former Member
    Former Member over 10 years ago
    *bump

    Can anyone tell me how they're profiling their code? How do you guys measure the time it takes to run your code, and how much memory you're using and battery drain you're causing?
    I'm only seeing BS numbers in the simulator :-/
    Are you guys writing everything to debug logs on real devices?

    Thanks!
  • If you're wanting performance numbers, you'll have to do it on hardware and write it to the logs using Sys.println(). You'll want to make sure that you don't call println() inside a loop and that you don't trip the watchdog timer.

    Memory statistics are available with System.getSystemStats(). There's a bug right now where it's reporting based on the total memory available to the device instead of what's available to the app (will be fixed in next SDK release).
  • Former Member
    Former Member over 10 years ago
    Thanks Kyle!

    If you're wanting performance numbers, you'll have to do it on hardware and write it to the logs using Sys.println(). You'll want to make sure that you don't call println() inside a loop and that you don't trip the watchdog timer.


    Hmm, that's a shame. Given the fact that exception handling doesn't work yet either and that the whole point of the performance testing is to see how much you can do before you trip the watchdog (and also to benchmark different ways of solving a problem), there actually is no way to do any performance testing at all at this point in time. Let alone the fact that few people will actually own all the different devices that support & will support Connect IQ.

    Memory statistics are available with System.getSystemStats(). There's a bug right now where it's reporting based on the total memory available to the device instead of what's available to the app (will be fixed in next SDK release).


    I'm very much looking forward to the next SDK release! :)
  • Hmm, that's a shame. Given the fact that exception handling doesn't work yet either and that the whole point of the performance testing is to see how much you can do before you trip the watchdog


    If you trip the watchdog on the sim you'll trip it on every watch as well.
  • Former Member
    Former Member over 10 years ago
    That's understood, thanks! I can't say that I'm over the moon with that answer, but it's something I can work with.
    I would like to be able to test and explore much & much more of the API and it's inner workings to learn how to exploit all the potential that it has to offer, but I guess that's just my OCD talking, as I don't see any other devs asking themselves the questions that I'm asking.

    So, while I'm not entirely happy with your answer, I'd still like to thank you for your activity and involvement in these forums Kyle. Thanks!

    BR,
    Sven