• A New Engine for your Tessel

    Tuesday, November 11, 2014

    11/11/2014– Tim Ryan

    When designing Tessel, we grappled with some very unique constraints for a developer platform. Most microcontrollers weigh in at perhaps a few dozen kilobytes of RAM, and a small factor more Flash. The average amount of JavaScript in a single webpage exceeds the amount of space in most microcontrollers, including the Tessel’s Cortex-M3, which has only 200kb(!) of memory on-chip.

    We didn’t need to make a full PC to make Tessel an interesting prototyping platform for web developers. There are plenty of ways to have JavaScript co-exist and communicate with embedded devices, for example Firmata or Raspberry Pi’s GPIO ports, but few products allow you to use existing software development tools to write code that directly interfaces at the level of a microcontroller or signal interface to other ICs. (An idea we’re pushing further with Fractal. Read more.)

    To push JavaScript as a language down to this level, our major hurdle was getting an embedded platform to run code at all. Despite its surface similarity to C, dynamic languages like JavaScript, Python, or Ruby require a lot of overhead just to run a line of code. Usually this takes place in a “virtual machine”, a language interpreter that provides the standard libraries, handles dynamic typing (5 + 5 and "5" + 5 mean different things in JavaScript), and of course memory allocation like garbage collection. Emulating a language in this way can naturally be more CPU and memory-intensive than compiling.

    The problem with running JavaScript on a target like Tessel is that every commonly used JavaScript engine has been refined and tuned for Desktop PCs, with gigabytes of RAM and fast network connections. But not all VMs are created equal; they are tailored for wildly different use cases, including embedded platforms.

    Fortunately, JavaScript has some essential similarities to the programming language Lua. Lua is a dynamic, lightweight programming language with extensible semantics that has seen broad use as scripting language in video games, routers, servers, and much more. Much of its popularity is due to how simple it is to embed. Lua is composed of a few dozen files written in portable C and has no external dependencies. Thus with Lua, adding scripting to a project becomes as simple a proposition as including another C library. That Lua supports many similar properties to JavaScript—first-class functions, null/boolean/string/double/object/array types, and can be changed at runtime to act differently—made it an amazing starting point for exploring how to make a JavaScript-compatible, embedded runtime.

    Heavyweight JavaScript engines like V8 and Spidermonkey are also embeddable, but where Lua shines is its low memory profile (taking only a few dozen kilobytes to start up instead of several megabytes) and simplicity. Thus, our first real demonstration of Tessel was in summer of 2013 getting Lua running on the LPC1830 (Tessel’s CPU). After that, it was “straightforwardly” a matter of closing the gap between Lua and JavaScript.

    Because Lua is extremely extensible, allowing modification of everything from basic add operators to whether primitives act like objects, a year of effort has resulted in a runtime that emulates many real features of Node.js with only a fraction of the resources Node requires on PC.

    Just in Time

    One year later, the landscape is much improved: several stellar alternative JavaScript engines are now freely available and freely licensed. Among these are projects like the Espruino VM, optimized for size and memory conservation, and Duktape, a JS engine designed for embedding into C. Meanwhile, Tessel’s JavaScript engine, dubbed “Colony”, leverages Lua’s VM and ecosystem to provide a Node-like environment that works across PC and embedded targets. But while Tessel is not lacking in memory, it is lacking in one of JavaScript’s biggest modern advantages: speed. So over the past few months, I’ve experimented in what would bring Tessel up to speed (literally) with other modern dynamic languages, starting with a foundation lauded for its speed and minimal codebase, LuaJIT.

    LuaJIT is a combination of a few components: a parser/compiler converting Lua source into (LuaJIT’s own) bytecode, an interpreter for running this bytecode, and then a JIT (just-in-time compiler) for optimizing this bytecode into machine code at the lowest level. If you’re not familiar with a JIT, imagine a compiler that runs alongside your code, observing your code as it’s running. If it sees any loops that look intensive, are called often, and use similar types of variables on each iteration, it can bundle these assumptions up as compiler “guards” and generate low-level machine code that acts much more like C than a high level language. If, while your code is running, one of those guards fails (the variable i is no longer a number, because we suddenly assigned it to be an array!) we jump backward into interpreting your code one line at a time. This is one of many optimizations that happen while your code is running inside a VM with just-in-time compilation.

    But why pursue LuaJIT? We’ve reached the limits of what our old runtime can do while sacrificing speed to emulate JavaScript compatibility. We’d like to implement many pedantic features—getters/setters/enumerable/writable/configurable properties, proper null values to arrays, and speedy execution in modifying binary data—while making code run much faster, and to top it all off, keep a freely MIT-licensed codebase. By building on top of some of the best technology that exists today, we can make it even more useful and flexible to programmers of all kinds, including Lua programmers interested in targeting microcontrollers.

    LuaJIT on Thumb

    As of today, you can update Tessel to a new build built on LuaJIT’s codebase instead of the classic Lua VM from PUC-Rio by running the usual tessel update. This doesn’t enable the just-in-time compilation I described; we are working on porting this. For now, LuaJIT’s codebase and interpreter (the first step toward speeding up your code) are enabled and yield a pleasant 2-3x speed improvement. We’re working on porting the JIT component itself to leverage interpreter and speed up code execution by orders of magnitude, so stay tuned.

    What does this mean for Tessel users? Today, a small speed improvement. Next, we can address large incompatibilities in how Tessel runs JavaScript in a rigorous way. We can speed up all code by leveraging LuaJIT’s interpreter, fast internal function paths, and eventually JIT to perform machine code assembly for JavaScript code. In addition, compilation of JavaScript on Tessel (enabling things like Function constructors and eval()) can be written to target LuaJIT bytecode directly, shortcutting our process of compiling to JavaScript to Lua source.

    Outside of Tessel, this throws Colony’s hat into the ring as a JavaScript engine with a focus on embeddability and performance. (You can test out Colony on PC today by cloning https://github.com/tessel/runtime and following the instructions. Colony’s JIT is enabled on PC, so benchmarks run quite fast considering its size.) Alongside the other great engines mentioned above, this furthers JavaScript’s inroads in enabling web programmers to prototype high-level ideas in standardized languages for every part of the stack. In 2014, I can’t think of anything cooler than that!

    I’ll be writing more on the technical details of this LuaJIT port in my next few blog posts, so stay tuned.

    #Tim Ryan #Tessel #Technical Machine #JavaScript #Hardware #Lua #LuaJIT #Colony #Performance #Speed #Bytecode #Embedded #Microcontrollers

August 2018

January 2018

July 2017

February 2017

November 2016

October 2016

September 2016

August 2016

July 2016

June 2016

April 2016

March 2016

February 2016

November 2015

September 2015

August 2015

July 2015

June 2015

May 2015

March 2015

February 2015

January 2015

December 2014

November 2014

October 2014

September 2014

August 2014

July 2014

June 2014

May 2014

April 2014

March 2014

February 2014

January 2014

December 2013

November 2013

October 2013

September 2013

August 2013

July 2013