Espruino Performance Notes

tosh1 pts0 comments

Espruino Performance Notes

Toggle navigation

Espruino

Get Espruino

 Espruino Shop

 Distributors

Download firmware

Donate

Documentation

Quick Start

API Reference

Tutorials & Examples

Modules

Tips & Tricks

Videos

Bangle.js 2

Bangle.js 1

Pixl.js

Puck.js

Jolt.js

Espruino WiFi

Espruino Pico

Original Espruino

MDBT42Q Module

Other Boards

Support

Forums

Getting Started

FAQ

Troubleshooting

Bangle.js App Loader

Espruino.js App Loader

Espruino for Business

Contact Us

Press Info

Espruino Performance Notes

Please see Internals for a more technical description of the interpreter's implementation.

Espruino is designed to run on devices with very small amounts of RAM available (under 8kB) while still keeping a copy of the source code so you can debug and edit on the device. As such, it makes some compromises that affect the performance in ways you may not expect.

Is it too slow for you?

Check out some easy Code Style changes to make your code run better.

JavaScript on Embedded devices will never be as efficient as compiled code, so we've made it easy for you to add extremely fast code where it is needed:

Add "ram" to the top of your function to tell Espruino to keep it in RAM and pretokenise it.

Add "jit" to the top of your function to tell Espruino to JIT compile it on the device (2v16 firmware and later)

Use the Web IDE to compile JavaScript into optimised Thumb Code using an Online service

Create functions with Inline C or Inline Assembler

Recompile the Espruino firmware with your own C code

ESPRUINO EXECUTES CODE DIRECTLY FROM SOURCE

Why not compile to native/bytecode?

Memory is scarce on microcontrollers, and we want the source code on the device itself so we can edit and debug it without external tools.

There isn't enough room on the microcontroller for source code and compiled code, but luckily source code is surprisingly memory-efficient.

For instance take this code that draws a Mandelbrot fractal:

var x,y,line;<br>for (y=0;yIt's 301 bytes long.

When compiled with SpiderMonkey, the following bytecode is created (obtained by running a debug build and dbg(function() { .... }):

loc op<br>main:<br>00000: getlocal 0<br>00003: pop<br>00004: getlocal 1<br>00007: pop<br>00008: getlocal 2<br>00011: pop<br>00012: zero<br>...<br>00256: loopentry 1<br>00258: getlocal 1<br>00261: int8 32<br>00263: lt<br>00264: ifne 22 (-242)<br>00269: stop<br>It's 270 bytes long. So you've saved 31 bytes over the original code, but now your code is totally uneditable and unreadable.

If you compiled this into native code with the Espruino Compiler, the size of the binary would be 1136 bytes.

But what if you rewrote it in C and compiled it in GCC with size optimisation turned on. That'll be efficient, right?

void main() {<br>int x,y;<br>char line[33];<br>line[32] = 0;<br>for (y=0;y$arm-none-eabi-gcc mandel.c -mthumb -Os -c -o mandel.o<br>$arm-none-eabi-objdump -S mandel.o

00000000 :<br>0: b5f0 push {r4, r5, r6, r7, lr}<br>2: 2400 movs r4, #0<br>4: b097 sub sp, #92 ; 0x5c<br>6: ab0c add r3, sp, #48 ; 0x30<br>...<br>116: bc01 pop {r0}<br>118: 4700 bx r0<br>11a: 46c0 nop ; (mov r8, r8)<br>11c: 3fa00000 .word 0x3fa00000<br>120: 40100000 .word 0x40100000<br>Nope. 290 bytes.

However, if you minified your code with the closure compiler you'd get:

var a,b,c;for(b=0;32>b;b++){c="";for(a=0;32>a;a++){for(var<br>d=0,e=0,f=0,g=4*a/32-2,h=4*b/32-2;8>f&4>d*d+e*e;)var k=d*d<br>-e*e+g,e=2*d*e+h,d=k,f=f+1;c+=" *"[f&1]}print(c)};<br>It's editable, just about readable, and it's only 167 bytes - so it is smaller than bytecode and even highly optimised native code!

Type<br>Size (bytes)

Original JS Code<br>301

Spidermonkey bytecode<br>270

Espruino Compiled JS<br>1136

GCC Compiled C code (-Os)<br>290

Minified JS<br>167

Minified and pretokenised JS<br>149

So by executing from source, we use around the same amount of memory as we would if we compiled or used bytecode, while still having everything we need to edit and debug the code on-chip.

However, if you need to make things smaller, you can minify the JavaScript functions you don't need to edit, which will use around half the RAM of even size-optimised C code!

What does executing from source mean?

The size of your source code will affect the code execution speed.

On the Espruino board a simple loop will create roughly a 4kHz square wave (so 4000 iterations of the loop per second) using code like this:

while (1) {A0.set();A0.reset();}<br>While code like this will toggle a pin at around 3.5kHz.

while (1) { A0.set(); A0.reset(); }<br>This applies equally to comments - so it pays to keep comments above or below a function declaration or loop, not inside it.

Note: You can turn on 'pretokenisation' globally with E.setFlags({pretokenise:1}) or<br>by beginning a function's code with the string "ram". Pretokenised functions have all whitespace<br>removed, and any tokens (such as this, function, for, etc) will be turned<br>into numeric values. This means that the above (whitespace slowing down<br>execution) will not apply - however your original code formatting will be lost,<br>which will make...

code espruino source compiled bytes function

Related Articles