-
• #2
what are "Short" variable names?
Good spot :) I'll update the docs, but it's max 4 characters. Any more and another variable will be created (which will get you up to 16 characters), and more means another variable, and so on.
So if you keep your function names and variable names at 4 characters or less, you may see some improvements even without minification...
Is there any benefit (or overhead!) in having all functions against the objects prototype?
At the moment, there is... This is related to your question about defining functions inside functions.
The problem with putting one function inside another is that because Espruino preserves the text inside the function, it's actually got one copy of the function as text, and then another copy as an actual function when the code is executed.
Even if you delete the reference to the original function, I believe it will still be referenced from the functions that were defined inside it, so it won't get removed.
So defining in the prototype uses a few more variables, but the actual text definition of the function is thrown away after the function is put in the prototype so you end up saving space.
It's something I have plans to fix in the future, by implementing function hoisting which would scan a function (on definition) for the functions defined inside it, and would then pull them out as separate variables. It's a bit of a pain though, because Espruino would have to reconstruct what the code when it had to convert the function to a string :)
So I'm afraid for the moment you're a bit stuck. At some point I have to stop using the online closure compiler for minification, and stick one inside the Web IDE. When I do that then I'd really like to use something that understood about modules, and could actually optimise over that boundary (removing functions that weren't being used and shortening all names). If anyone's got any ideas about that then I'd love to hear them though :)
-
• #3
Ok, quick update - I was wrong. As an example:
var b = (function(a) { // code I don't care about for (var i=0;i<10;i++); // ........... return function() { console.log("Hello "+a); } })(); trace(); // <--- this is handy, it outputs what is in memory in a human-readable (ish) format #1[r2,l1] Object { #2[r1,l2] Name String [1 blocks] ">" #3[r1,l2] Object { // we don't care about stuff in here } #22[r1,l2] Name String [1 blocks] "b" #40[r1,l1] Function { #41[r1,l2] Name String [1 blocks] ">cod" #38[r1,l1] String [2 blocks] "{ console.log(\"Hello \"+a); }" #42[r1,l2] Name String [1 blocks] ">sco" #34[r1,l1] Function { #35[r1,l2] Name Param undefined #36[r1,l2] Name String [1 blocks] "return" undefined #37[r1,l2] Name String [1 blocks] "i"= int 10 } } }
So the function that has been executed gets completely removed (even though its execution context remains). This means that it's best to do the following (which will allow the minifier to work its magic):
var onInit = (function() { var aaa, bbb, ccc; function one(){ } function two(){ } function three(){ } return function() { // stuff you actually want to execute on initialisation one(); }; })();
That doesn't work so well for modules though I'm afraid, because the functions that are defined still need the 'module scope', which references the function that defined them.
-
• #4
You might be able to do:
// my module exports.foo = function() { var s = { bar : function() { }, .... } delete exports.foo; return s; };
I haven't tried it - but that really is quite nasty!
-
• #5
Thanks for such a quick, comprehensive answer - I'll try some of this out later.
I actually quite enjoy having limited ram- it forces me to think about every function in detail and can result in a much better end product. With unlimited resources code can become bloated really quickly.
-
• #6
@Gordon : That worked out perfectly, just what I needed: A couple of extra lines of code saved me about 100 variable blocks!! AND i can use long/descriptive variable & function names since the minifier is now free to do its magic!!
Anyone having "out of memory" issues should try this pattern:
var onInit = function(){ var aaa, bbb, ccc; function one(){ } function two(){ } function three(){ } //all your normal onInit code here one(); delete onInit; //This is the key to saving memory //onInit = unassigned; //also works } onInit();
I am now going to try it on a few of my modules to see if it helps there too.
-
• #7
Quick Update - I'm now up o 950 free variable blocks (from 700) with much more readable code!
"Kludge" or not it definitely works!!
-
• #8
The
delete onInit()
stuff is potentially a bit tricky. You'll have to be sure that yousave()
you code before runningonInit()
-
• #9
Is this still best practice for maximising memory efficiency?
-
• #10
Yes and no... I'd:
- Define any functions you're going to keep using outside
onInit
. - Do any one-time initiailisation code in
onInit
, and delete it afterwards
The Web IDE also lets you minify code that you're written in the right-hand pane, which should help a lot (although it makes it less debuggable)
- Define any functions you're going to keep using outside
-
• #11
Relatedly, I still haven't tracked my memory leaks down, so I am using this pattern for those devices (sensors) which only poll occasionally and do not require realtime:
//simplified example with all the logic in init. E.on('init', function () { setDeepSleep(0); //only want to sleep after I finish my actions setTimeout(load, 600000); //restart every 10 minutes to poll again doPolls(function() { //callback function called when complete setDeepSleep(1); //now I can go to sleep until time to reboot }); });
Okay, I should find and fix my code leaks, but this is a common and/or reasonable pattern in embedded?
-
• #12
Well, it seems a bit hacky - but yes - I don't see anything wrong with that at all.
Another thing you could do if you really want to 'hard' reset the chip is :
E.enableWatchdog(0.1);while(1);
The
setDeepSleep
thing you're doing is really good if using serial-based sensors though.... what I should do at some point is have a
E.shutdownFor(seconds)
command that puts the device into proper power-down, but restarts in in X seconds. That'd do what you want but would also be super power-efficient. -
• #13
Correct practice is to find and fix the bugs - reloading the script every time it runs is a really painful solution, though I guess it is a solution... I've had Espruinii running for over a month without them dying, so it is possible. Just gotta find out where the memory is going.
-
• #14
In your code, do you require modules? They may hold on to some memory...
The other 'wild goose chase' is: do you 'write' to something that buffers but cannot empty the buffer?
I do not know if this is still true when writing to console in (IDE) disconnected case, a buffer will fill up and Espruino will eventually freeze... but you did not mention freeze, but memory loss.
Can you share some of the memory-destroying-suspicious code?
-
• #15
when writing to console in (IDE) disconnected case, a buffer will fill up and Espruino will eventually freeze
That doesn't happen if you're disconnected from USB, so you're ok...
The best thing to do is to use
E.getSizeOf(global, 2)
which will drill down into your datastructures and will tell you how much memory each one uses. You can then call it, do some stuff that eats up your memory, and then call it again and compare the result.
I have been really pleased with how much code I can load/run within 48K ram. My current project is getting quite large yet when using minification I still have room for 700 variables.
I would still like to ensure I use as little memory as possible (just in case I hit the limit).
Minification works really well but for those variables that the minifier cannot rename i.e. those that interface with the object (at root level) what length should I try to keep variable names to?
i.e. what are "Short" variable names - I saw this mentioned somewhere but can't find it again?
If I have a module defining an object/class of which there will only ever be one instance, is there any benefit (or overhead!) in having all functions against the objects prototype?
I also tried placing all my main code within onInit(). i.e. instead of:
I used:
This allowed the minifier to rename most of my variables/functions but resulted in more memory being used - why did this happen?
I have read http://www.espruino.com/Internals but not 100% sure about the best approach.
Any other ideas on how to minimise memory usage yet keep my code readable/maintainable would be appreciated.