Memory optimisation ideas?

Posted on
  • Hello! I'm back (after continuing to not break my Pixl.js)...

    After about a month of development, I've been creating a project with my Pixl.js board which I'm pretty proud about, though there's a small (but currently not-a-big) problem in that the file size in my build folder (bearing in mind that it's all minified using terser) is about 26.7 KB, so it's getting close to the 40 KB limit! I don't expect there to be much more to write any time soon (my project's pretty complete, as far as the Espruino code goes), but it's something to consider.

    A bit of background: So yeah, my project is basically an educational OS/environment where people can write their own 'apps' with our IDE, using our API, in JavaScript. The code's minified (again, using Terser) before upload, which is a big bonus considering that one may have a few apps installed and minification definitely reduces the file size. The only problem that remains is if you want to write a more complex app, you run into the high chance of crashing the device the bigger your code gets. A ~1 kb minified app with around 5 numeric vars, 1 bool var, and a couple of ~30 char strings (in some test code/minigame I made) has previously caused the device to crash (not good!).

    If you must, the code's here: (The photo in the readme will give you a good idea as to how my project operates.)

    The problem: My problem is about RAM. How can I optimise using eval to 'open' these apps? The current architecture makes a call to eval once it has read from an app file stored in flash, and then retrieves functions that were instantiated in that eval for processing later. This pseudocode will explain it a bit better:

    class AppLoaderScreen extends Screen { // A component of my UI library which enables multi-screen/page navigation
            constructor(appCode) { // When the app is opened
                this.appFunctions = []; // Contains start function, followed by loop function
                this.appFunctions = eval(appCode + `; return [start, loop];`);
            onFirstRender() { // When my internal UI library starts rendering the app screen
                this.appFunctions[0](); // Make call to start function
            onTickRender() { // When my internal UI library renders the app screen from setInterval
                this.appFunctions[1](); // Make call to loop function

    The actual implementation of my app loader/runtime functionality is here. Specifically, the eval is on line 145.

    Enough of my code now! Are there any general optimisations that I can do to improve the memory so that there's plenty of free space for users who wish to write their own relatively large apps? Bearing in mind that my project's use case is kids/beginner programmers, so I don't expect there to be too many colossal projects (otherwise, they should move onto getting a Pixl.js/Arduino/removing my OS altogether if they want the free space!).

    Also, one thing that is probably a no, but with regards to the hardware (and specifically the nRF SoC), would there be any way to 'upgrade' the RAM and storage as a simple task of directly replacing/desoldering the nRF52832 and putting in a higher-spec chip (if any)? Then, I would think it's just a case of flashing the Espruino firmware onto the new chip, right? The nRF52833 would easily double the RAM (and the nRF52840 could double the flash and quadruple the RAM!), and I assume it'd just be a case of rerouting some of the PCB traces and then fabricating a PCB that accommodates the pin locations on the aforementioned chips. I'm also wildly guessing the Espruino firmware has some way of detecting the RAM size, and that the chip architecture is very similar across SoCs...

    I do admit that I am by no means an expert when it comes to hardware, though! I'm probably quite naïve with respect to the hardware architecture of the Pixl.js board.

    Any help with optimisation would be greatly appreciated (I've already tried to implement some of the optimisations on the Performance Notes), but if not, it's not the end of the world! There's a lot to unpick (including my bonkers idea/question at the end there), so sorry in advance if this is quite a long-winded post (probably a book at this point...)!


  • Hi - this looks like a fun project!

    The first (and really big) thing I see is actually this:

    this.appFunctions = eval(appCode + `; return [start, loop];`);

    This may seem really innocent - but if you could ensure appCode is a string loaded directly using require("Storage").read, but with ; return [start, loop]; already in it, then you eval it directly, you'll find your RAM usage is drastically lower.

    For instance:

    var appCode = require("Storage").read(appName);
    this.appFunctions = eval(appCode);

    Why? If you eval a string and that string is a string that's in flash memory, Espruino doesn't load any of the functions in that string into RAM - the function code stays in flash memory and is executed direct from there.

    However when you add the text ; return [start, loop]; onto the string, the whole thing has to be loaded into RAM first, and now all the function code stays in RAM.

    So I think that one change will likely fix the majority of your problems, but it's also worth looking at some of the suggestions for writing code on Espruino:

    Specifically the stuff about big blobs of data. Basically when you define stuff inside a function, it then doesn't take up any RAM until that function is executed - it just sits in Flash.

    I'm afraid you can't increase Pixl.js's RAM (I don't believe the other Bluetooth modules are pin compatible), but you could add external flash storage (even in the form of an SD card) to store the apps if you wanted.

    Having said that I'm looking into making a Pixl.js v2 with the nRF52840 that'll have bags more RAM and a few extra features (I have a prototype PCB by my side right now) so when those go into production you'd be able to run your code on them (probably with no changes at all) and take advantage of the extra flash and RAM you get

  • Hi Gordon! Thanks for the reply.

    That's a really good point ─ I should've just made the return inside the app file instead of forcing the Espruino to copy the whole code into RAM to concat a string on the end of it! Also your point on defining vars in functions instead of in the module-wide scope is a good one to take on board too.

    The changes shouldn't be too hard to implement ─ after all, my IDE also does a lot of work to ensure safety when encountering while and for loops so that an infinite loop doesn't happen (otherwise, an error is thrown/displayed). The same has been applied to the simulator, seeing that web JS is also single-threaded! I think that building the string on the IDE side is easy to do but really effective at freeing RAM as you say.

    I've heard that some people have connected an SD card reader to their Espruino. I think that's a great idea providing it fits into my little 3D-printed unit! I'm sure it'd make it easier to copy over app code from devices that don't support Bluetooth, too.

    As for the idea of a Pixl.js V2, that would be awesome! I love the idea of that extra flash and memory space... It'll be cool to see what others in the Espruino community do (and I'm sure I could implement a few extra features into my OS 😉)!

    Having bought 3 Pixl.js boards in total (one for me, two for some friends of mine), I may have to wait a bit before I consider buying the upcoming Pixl.js V2 so that I can give my wallet a rest, but it's certainly something worth purchasing!

    Thanks for the help so far,

  • :) Hope it helps!

    The Pixl.js v2 will still be a way off - I haven't even put the components on the test board yet and I'm sure there will be extra revisions and a lot of software work to do before I can release it!

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview

Memory optimisation ideas?

Posted by Avatar for James-Livesey @James-Livesey