Unused constants are kept in resulting prg file

I am starting to define some often needed constants, like colors. But even when the specified constants are never used, they reside in the compiled prg.

You can easily check this by adding a dummy constant and watch how your app will increase:
const abcdefghijklmnopqrstuvwxyz="ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";

It would be fine if the compiler/linker would be able to strip unneeded data...
  • Every line of code uses space. The monkey language interpretes as it's going, so doesn't know before-hand what is going to come up next.

    Perhaps it could be a compiler optimization to remove unused vars/consts maybe in a 2-pass process, but ultimately you shouldn't define consts or vars that you're not going to use.
    Your code will look cleaner too.
  • Former Member
    Former Member over 8 years ago
    Is this a bit like MS-DOS and BASIC?
  • No there's a compilation process, so interpreting was a poorly chosen word from me.
    you can read all about it here: https://developer.garmin.com/connect-iq/monkey-c/
  • I wanted to post this information because you have to decide to do readable codes or create larger executables...

    Of course simple optimizations like that can be done by doing a single pass, but I won't have problems if enhancements are done by supporting pre processing or macros etc.

    Once again, keep in mind, that...
    ...not only defining constants, but also using them makes your programs larger (and slower) :p

    const c1=1000;
    const c2=100;
    const c3=10;
    const c4=1;
    const csum=c1+c2+c3+c4;

    z=c1+c2+c3+c4; // ... needs more bytes than...
    z=csum; // ... but this needs still more than...
    z=1111;


    So be careful when using things like flags (because expressions like "flag1 | flag2 | flag3" won't be optimized) very excessively or you started to create a huge table of constants (font types, colors, coordinates etc.)
  • Yes. I think the takeaway here should be that the MonkeyC compiler is not really an optimizing compiler like you get with a languages that compiles from source to machine code (like C/C++). It is more like the Java compiler; it compiles source code into byte codes that are later interpreted.

    If you want your MonkeyC code to be tight (waste as little memory and cpu as possible), you'll need to make those optimizations in the source code. You may find that compiling with -g produces debug output that can be useful to see what is generated when you compile your code... For example, the code you posted above boils out to...

    globals:
    CLASSDEF
    APPTYPE 15
    MODULEID globals
    ZZZView 1 CLASS @globals_ZZZView;
    c1 1 INT 1000;
    c2 1 INT 100;
    c3 1 INT 10;
    c4 1 INT 1;
    csum 1 NULL;
    ZZZApp 1 CLASS @globals_ZZZApp;
    <init> 2 METHOD @globals_<init>;
    Rez 1 MODULE globals_Rez;
    <init> 2 METHOD @globals_<init>;
    Toybox 0 NULL;
    END



    # z=c1+c2+c3+c4;
    source_ZZZApp_mc_188_8:
    lgetv 0
    spush z
    lgetv 0
    spush c1
    getv
    lgetv 0
    spush c2
    getv
    addv
    lgetv 0
    spush c3
    getv
    addv
    lgetv 0
    spush c4
    getv
    addv
    putv


    # z=csum;
    source_ZZZApp_mc_189_8:
    lgetv 0
    spush z
    lgetv 0
    spush csum
    getv
    putv


    # z=1111;
    source_ZZZApp_mc_190_8:
    lgetv 0
    spush z
    ipush 1111
    putv


    It is worthwhile to note that they did add a constant folding optimization a while back. You can see it in action...

    # z=1000+100+10+1;
    source_ZZZApp_mc_193_8:
    lgetv 0
    spush z
    ipush 1111
    putv


    One other thing that may be useful to know when optimizing is that there are runtime costs associated with finding a variable. As an example, if you have code that refers to a global variable by just the variable name, the runtime must look for that variable in at least three places before it is found (function scope, class scope, and finally the global scope). These lookups are not free, so the special bling identifier ($) was added as a way to reference the global module and avoid these costs. To take advantage, you can write $.varname instead of just varname when referring to global variables.

    That said, I believe that you can do better than just using the bling operator in some cases.

    It is cheapest to access a local variables, more expensive to access those at class scope, and more expensive again at global scope (there may be other scopes at play, but the result is the same). If you can trade an access for a variable at an outer scope for a access at local scope, it might be a win. Consider...

    # z=c1+c2+c3+c4;
    source_ZZZApp_mc_188_8:
    lgetv 0
    spush z
    lgetv 0
    spush c1
    getv # lookup c1 in local scope, class scope, and then global scope
    lgetv 0
    spush c2
    getv # lookup c2 in local scope, class scope, and then global scope
    addv
    lgetv 0
    spush c3
    getv # lookup c3 in local scope, class scope, and then global scope
    addv
    lgetv 0
    spush c4
    getv # lookup c4 in local scope, class scope, and then global scope
    addv
    putv


    This version should be cheaper at runtime (as explained above)

    # z=$.c1+$.c2+$.c3+$.c4;
    source_ZZZApp_mc_197_8:
    lgetv 0
    spush z
    spush globals
    getm # search for the module
    spush c1
    getv # look for c1 in the $ module
    spush globals
    getm # search for the module
    spush c2
    getv # look for c2 in the $ module
    addv
    spush globals
    getm # search for the module
    spush c3
    getv # look for c3 in the $ module
    addv
    spush globals
    getm # search for the module
    spush c4
    getv # look for c4 in the $ module
    addv
    putv


    I believe this code should be even cheaper since the lookup of the module is done only once and then cached...

    # var gg=$;
    source_ZZZApp_mc_199_8:
    source_ZZZApp_mc_199_12:
    spush globals
    getm # search for the module
    lputv 1 # cache the result
    # z=gg.c1+gg.c2+gg.c3+gg.c4;
    source_ZZZApp_mc_200_8:
    lgetv 0
    spush z
    lgetv 1 # access the $ module by accessing the stored value (no lookup required)
    spush c1
    getv # look for c1 in the $ module
    lgetv 1 # access the $ module by accessing the stored value (no lookup required)
    spush c2
    getv # look for c2 in the $ module
    addv
    lgetv 1 # access the $ module by accessing the stored value (no lookup required)
    spush c3
    getv # look for c3 in the $ module
    addv
    lgetv 1 # access the $ module by accessing the stored value (no lookup required)
    spush c4
    getv # look for c4 in the $ module
    addv
    putv


    The number of instructions is the same, but it seems that it is more optimal since the module lookup only happens once and is cached.

    Obviously all of this is less optimal than just using the literal constant directly, or taking advantage of constant folding. But if you are optimizing your application and want to keep using constants for maintenance/readability reasons, this might be something you should take into consideration.

    Travis
  • Travis, thanks for this interesting information, that will definitely helps me a lot!

    Hey, is there a simple way to redirect the printed debug information to a file? I have already added a code (':') to show the debug information, something like 'make test bs9.:' will set the '--debug' flag, but the output is only seen on the screen and the limited console buffer does not allow to show all lines. I added '> monkey.out' to the java line in 'monkeyc.bat' with no success.

    If I have some spare time, I'll write a tool to replace all constants within a source code automatically - I'd like to see how this will has any impact in the resulting programs.





    *******************************************************
    This has been my 100000000 posting in the garmin forum, binary spoken.
    *******************************************************
  • Hey, is there a simple way to redirect the printed debug information to a file?

    Yes. The debug output is sent to standard error (file descriptor 2). If you want to redirect it, you need to do this...

    monkeyc ... -g ... 2> program.asm
  • Genious!

    I modified my make.bat to include the following lines...
    if /%check%==/: (
    set flags=%flags% --debug
    set erout=2^> "%~dp0monkey.out"
    )


    ...and...
    java -cp "%~dp0bin\monkeybrains.jar"; com.garmin.monkeybrains.Monkeybrains -a "%~dp0bin\api.db" -u "%~dp0bin\devices.xml" -o %sample%.prg -m %loc%manifest.xml%flags% -z %all% %src% -y %mykey% %erout%