coder-mike / microvium

A compact, embeddable scripting engine for applications and microcontrollers for executing programs written in a subset of the JavaScript language.
MIT License
569 stars 25 forks source link

Code size growth #79

Closed davidchisnall closed 6 months ago

davidchisnall commented 7 months ago

Upgrading from a version from March to today makes the code size of the microvium library jump from just under 14 KiB to just under 17.5 KiB.

It's not clear to me what the correct model is for long-term deployments. Picking up bug fixes may require moving to a newer version. Even without that, the fact that the new version of the host part can't generate bytecode for an older version means that things like Node.js support lifetimes may require upgrading to be able to still produce working bytecode.

I presume the code won't continue to grow at a rate of 25% every six months, but it would be nice to understand what strategy people should use for devices with a long support life (even if it's 'buy a support contract').

coder-mike commented 7 months ago

Yeah. The size increase you're referring to is due to the inclusion of async-await, which is one of the biggest and most challenging changes I've made to the engine since the beginning and was part of a parallel branch for over a year. I think this kind of big change won't be a common thing.

I haven't formalized the growth strategy, but roughly these are my thoughts:

  1. On the main readme, I've said Microvium is "less than 16kB". It's currently about 12kB on the platform I'm using to measure it, so I see that as giving me another 33% or so space for growth. But now that I think about it, I should more explicitly say that users should expect it to grow by about 33% from its current size, since of course their compile size may not be the same as mine, depending on the platform and compiler.

  2. I don't intend to keep up with all the latest ECMAScript spec advances. At some point, hopefully soon, I'm going to consider the engine "sufficient" and mostly only include bug fixes or minor tweaks. I believe that engines like mJS have taken this approach, and basically just stop adding new features after a point.

  3. If I get time to work on it again, my intention is to introduce a data type I call "virtual" (there is a type code reserved for it) which will be like a Proxy in that its implementation can be defined in user code, but more powerful than a proxy. The intention is that with this type, user-level code can extend Microvium's type system with things like BigInt, Regexp, Proxy, or anything else. This is possibly the last invasive thing I hope to add to Microvium, but it would open the door for opt-in libraries that provide much more functionality. So much of the expansion of Microvium from that point could be in the form of opt-in libraries rather than the core itself. At least that is the dream, but I'm not getting much time these days to work on Microvium.

  4. Up until recently, I've basically been treating Microvium as a pre-release version, so I've been pretty aggressive with things like instruction set refactoring, which causes incompatibility between versions. I believe it's transitioned now to a more stable state, where I'm not likely to make changes of that nature, but I may still need to add new op-codes to access new engine capability, which necessarily break compatibility. I don't think Microvium has yet reached a point where I can guarantee that there won't be new instructions. Certainly, things like the "virtual" type will require corresponding engine support. I can't see a way around requiring that users update their runtime engine when they update their compiler, at least for the moment.

Something that might help a little is that I'm hoping to improve the support for compile-time code generation, and in particular the ability for compile-time JavaScript code to emit the microvium.c and microvium.h files which would match the current compiler version. Depending on your project structure, this would automatical update the host firmware project when you build the JavaScript project, which makes migration easier. But it doesn't solve the case where you have existing firmware running in the field and want to use a newer compiler version on the back-end.

What are your thoughts on the matter?

davidchisnall commented 7 months ago

On the main readme, I've said Microvium is "less than 16kB". It's currently about 12kB on the platform I'm using to measure it, so I see that as giving me another 33% or so space for growth.

Is that something 16-bit? I didn't think we were 50% worse for code size than Arm M-profile, but we possibly are relative to PIC. More work for us to do on the compiler, either way.

But now that I think about it, I should more explicitly say that users should expect it to grow by about 33% from its current size, since of course their compile size may not be the same as mine, depending on the platform and compiler.

That would be useful.

If I get time to work on it again, my intention is to introduce a data type I call "virtual" (there is a type code reserved for it) which will be like a Proxy in that its implementation can be defined in user code, but more powerful than a proxy. The intention is that with this type, user-level code can extend Microvium's type system with things like BigInt, Regexp, Proxy, or anything else.

That sounds great. Looking forward to it. I probably don't want most of these things but being able to add 64-bit integer support via something like this would be very nice. It would also be nice to be able to expose things like MMIO device registers in a more direct way.

Depending on your project structure, this would automatical update the host firmware project when you build the JavaScript project, which makes migration easier. But it doesn't solve the case where you have existing firmware running in the field and want to use a newer compiler version on the back-end.

Yup, for our demo we put the compiler in the cloud and shipped code to the device. I can imagine wanting to upgrade the cloud service independent of the firmware builds being quite a common requirement.

What are your thoughts on the matter?

Well, currently we have no customers actually asking for JavaScript, but I want to encourage people to use Microvium because the more people not writing C the better a place the world is. I think your proposed policy looks sensible and people can always add some padding to make sure that their images have space for a newer version.

coder-mike commented 7 months ago

the more people not writing C the better a place the world is.

Yes! 😄

Is that something 16-bit? I didn't think we were 50% worse for code size than Arm M-profile

The size check uses this compile command: https://github.com/coder-mike/microvium/blob/main/size-test/build.sh That's compiling for a Cortex M0 using the thumb instruction set. It uses this port file with floating point enabled but snapshot and debug API disabled. Perhaps importantly, it has MVM_USE_SINGLE_RAM_PAGE enabled and MVM_RAM_PAGE_ADDR 0x20000000 which is reasonable for such a device, which allows efficient translation between Microvium's 16-bit pointer and the native ARM 32-bit pointer by just applying an offset. I do something similar with my Windows tests with an offset of 0x55550000 just to make debugging easier (e.g. the native addresses are consistent across multiple runs) and also the WASM build.

I'll add that the size check includes only the object code for microvium.c, so it doesn't include any cost associated with the features of the standard library that microvium uses, or any integration glue code.