4.1.4 Compiler 2 Beta code size comparsion

I compared the old compiler (4.1.4) with the new compiler (4.1.4 Compiler 2 Beta - https://forums.garmin.com/developer/connect-iq/b/news-announcements/posts/optimal-monkey-c)
                                    curr,peak,global,code,data,entry,settings,
old compiler, old sim:   26.0,27.2,1926,10960,4597,4094,1152
old compiler, new sim: 26.2,27.4,1614,10960,4597,4094,1023
new without params:   26.1,27.3,1614,10932,4600,3998,1023
new with -O 0:             26.2,27.4,1614,10960,4597,4094,1023
new with -O 1:             26.1,27.3,1614,10932,4600,3998,1023
new with -O 2:             24.8,26.1,1553,9988,4384,3963,1023

Some things to note:
- I was using -l 3 in all cases
- I had to "fix" or maybe "hack" some errors that appeared with the new compiler
- I'm not sure about the differences between old compiler old sim, new compiler new sim (it was probably a mistake, it happened because I tried the new compiler, new sdk, and it opened the sim, but later I switched back to 4.1.4 and forgot to close the sim)
- the default for the new compiler is -O 1 (not -O 0 !!!)
- I can hardly imagine any reason (unless we find some bug in the compiler that happens only in -O 2) why anyone would not use always -O 2
  • Frequently "the obvious" is well worth stating. I spent many hours a couple of years ago splitting my main processing function into a slew of smaller, single-call functions to handle independent cases.

    I'm not sure that it cost any processing cycles, but it saved a heap of memory as there were a lot of local variables which were able to be defined in their own function and so quickly released.

    It certainly made the code readable.

  •  it saved a heap of memory as there were a lot of local variables which were able to be defined in their own function and so quickly released.

    But couldn't you achieve the same effect and save a tiny bit more memory simply by assigning null to a local variable when you were done with it? Or by simply creating a new block scope for each smaller conceptual function?

    I'm not saying it would be worth the hit in readability or maintainability.

    Anyway, you'll just have to take my word for it that I was able to save lots of memory by inlining stuff in some of my apps. ¯\_(ツ)_/¯

  • Or by simply creating a new block scope

    What's a "block scope" (in MonkeyC)?

    assigning null to a local variable

    I can't imagine anything more mind numbing!

    But anyway my primary objective was to make the code more readable, and that worked 100%

  • Anyway, you'll just have to take my word for it

    Trust me, I'm from the government!

  • assigning null to a local variable

    I can't imagine anything more mind numbing!

    But anyway my primary objective was to make the code more readable, and that worked 100%

    I never said it was fun or a good practice, I just said it would be a way to achieve the same effect (saving memory) without making a new function.

    What's a "block scope" (in MonkeyC)?

    The same thing it is in most C-style languages: a region of code that's created with an opening "{"  and terminated by a closing "}".

    https://www.geeksforgeeks.org/scope-rules-in-c/

    Scope of a Identifier begins at opening of the block / ‘{‘ and ends at the end of the block / ‘}’. Identifiers with block scope are local to their block

    e.g.

        function foo() {
            { // beginning of new block scope
                var x = 42;
                System.println("x = " + x);
            } // end of new block scope
            System.println("x = " + x); // Compile error, as x is unavailable here
        }
    

  • Anyway, you'll just have to take my word for it

    Trust me, I'm from the government!

    Since you asked so nicely, I'll give you a hybrid example from 5 years ago where I converted a stack class to one function and a bunch of manually inlined functions (like peek()), which saved a bunch of memory both in code and data (since objects are expensive).

    If you need something more than that, sorry you'll just have to use your imagination, look at your own code for inspiration, or ask google/stackoverflow.

    https://stackoverflow.com/questions/280173/c-inline-functions-and-memory-use

    in general, inlining code will increase how much memory is used to load your program. This is because there will be multiple copies of the generated code scattered around your program. However, this isn't always true -- if your inlined function was only used once, there's little change, and if the inlined function is very small, you could get a net reduction in code size by removing the function call overhead.

    Sorry for the images, but the forum literally won't let me paste my code and I don't feel like using pastebin.

    Original code:

    New code:

    In my specific app, I would've seen gains just by inlining peek() and pop(), but obviously I saved a lot more by getting rid of the Stack class itself. (Since I saved on all the overhead for the class and object stuff.)

  • Not to mention that another obvious type of candidate for inlining is "accessor/mutator type" functions in classes, which are often dead simple in terms of implementation.

    e.g. Good design:

    class Stack {
      private var _size;
      public function size() {
        return _size;
      }
      // ...
    }

    Bad design, but saves precious bytes in Monkey C

    class Stack {
      public var size;
      // ...
    }

    In well-designed programs, we often have very trivial functions which only exist abstract/hide/prevent access to the internal state of the object, to prevent coding errors, to allow the external interface of a class to stay the same while the internal implementation changes, etc.

    From a memory efficiency POV, when every byte counts, good design is a waste of memory.

  • Thank you for your detailed response, but I think any response will inexorably entrench us on opposite sides of the debate over the value of Object Oriented program design. And that won't benefit either of us.

  • Thank you for your detailed response, but I think any response will inexorably entrench us on opposite sides of the debate over the value of Object Oriented program design. And that won't benefit either of us.

    I never said I cared or didn't care about OOP design one way or another. I literally removed as many classes from my monkey C apps as possible in order to save on code. And in my day job, I mostly work with functional code where OOP is either a secondary concern or completely absent. On the other hand, in previous jobs I wrote a lot of heavily object-oriented code. And maybe 20-30% of my current job still involves OOP.

    I do think that if you're going to do OOP (e.g. you're using Monkey C where it's impossible to not use a few classes), you may as well do it the right way. But of course Monkey C doesn't let you go all-in on OOP without consuming lots of memory.

    But I'm not married to the idea of OOP, nor do I reject it out of hand. It has its uses, but it's not appropriate everywhere. (The modern trend is obviously to move away from OOP for a lot of reasons.) If I had much larger amounts of memory available for my CIQ apps, I'd have no problems always writing Monkey C code the "right way" (as intended by the designers).

    I just pointed out that there's some situations where there could be functions that are simple enough to inline in a way that saves memory, since you demanded it. If I really wanted to, I could find stuff that has nothing to do with OOP at all.

    Like I'm not literally gonna go through my entire git history for all my monkey C apps to find some salient, non-trivial, non OOP pieces of code that I inlined in order to prove a point to you. Even if I did that, what would it prove? Who's to say you would write that kind of code in the first place?

    If you really want to believe that inlining can never save memory except for some contrived examples that I made up, that's fine with me.

    I simply made the statement that for *me*, I was able to save lots of memory by inlining code in Monkey C. This is code that was originally written in a readable and maintainable way, but wasted memory in code. I reclaimed that wasted memory with manual inlining (which made the code much harder to read and maintain.)

    This was in response to your blanket statement that you believe inlining increases memory usage.

    So there's at least 3 exceptions:

    1) My personal experience

    2) The experience of the people who wrote the wikipedia article on optimization who claimed that inlining usually wastes memory but sometimes saves it

    3) The experience of the person who wrote the monkey C prettier tool which includes an inlining feature

    I just wanted to mention that because inlining was very useful for me, specifically for Connect IQ Monkey C development, and specifically for saving memory on memory-limited devices. The key is obviously you have to identify which functions are good candidates for inlining, and verify your guesses through trial and error.

  • No! Try this:
    1. compile your app, run, write down the code and data size

    2. add 1 or few lines of "dummy" core to some of your existing functions, compile, run, write down the code,data size. This will tell you how much the added code actually uses.

    3. add a new function and move your added code there. Don't add any calls to the function yet. compile, run, write down. Now you'll see that just defining a function adds something like 20+ bytes

    4. now add 1 call to your new function somewhere. coompile, run, write down... Now you'll see that calling your function adds also some more bytes.

    5. if you're interested then now add some parameters to the function, and also test how much that adds to the code (also declaring, but even more passing them when you call it. compile, run, write down...

    6. now "inline" your new parameterized function and remove the function, and compile, run, write down

    7. compare the code, data sizes between the different steps. You'll see that from 5 to 6 you decrease the code+data size a lot.

    From all the above you'll also see that it depends on many things, but in general: the more parameters you have you'll gain more by inlining it. Of course it depends very much on how many places from the code the function is called. If it's only 1 place and the only reason you have the separate function then inlining it is 100% a gain. If the reason is because you have 2 excludeAnnotations for this function then it's a bit more complicated:

    (:memory16K)
    function a() {
        //do less things
    }

    (:memory32K)
    function a() {
        //do less things
    }

    function common() {
         // long code
        a(); // and this is the only place in your code you use a()

        // and even more
    }

    Now you have 2 options: either you copy common and split it to (:memory16K) and (:memory23K) and inline the respective a() into them, so you get rid of the additional cost of declaring and calling a function, or you start to use something like: https://marketplace.visualstudio.com/items?itemName=markw65.prettier-extension-monkeyc