nodemcu / nodemcu-firmware

Lua based interactive firmware for ESP8266, ESP8285 and ESP32
https://nodemcu.readthedocs.io
MIT License
7.66k stars 3.12k forks source link

RFC What is missing from the Lua Developer FAQ #2332

Open TerryE opened 6 years ago

TerryE commented 6 years ago

Background

We are about to implement the LFS and this fundamentally changes best practice for how Lua developers code their NodeMCU applications. Incidentally, I have just updated an issue (#1119) with a view to issuing the PR for this immediately after we release LFS and I realised that it is 2 years since we created this issue. This got me thinking about two questions that it is worth my posing, answering and our subsequenty discussing, because I want to reflect the conclusions of such discussion in our FAQ.

Differences in best practice 2 years ago

The lifecycle of GCOjects

Pretty much all of the RAM used by the LVM are what runtime referes to as Garbage Colllectable Objects (GCObjects): Tables, Arrays, Functions, Strings, Userdata, ertc. The Lua Garbage Collector (LGC) handles all of these in a standard way: they are created during the program execution of the application. Once they have been fully dereferenced, then the LCG will collect and reuse the storage: birth, life and death.

The developer has very limited RAM in ESP applications, and to get decent scalability, then some care has to be put into ensuring that oject has as short a life as possible; that they are created in as lazy a way as possible and that they are dereferenced immediately after their use is done.

Because the LGC does is its in-use scan from a number of roots: the _G table, the Lua registry, and the Lua stack, it is very easy to leave dangling references to objects should be dead (for example storing the reference in a global or in the registry, or in a table which contains other live entries

Another aspect of GCOjects is that are all stored in variables by reference, so assigning a GCOject to anothe Lua variable is a very lightweight operation; referencing the variable doesn't dereference the underlying object if other references exit elsewhere.

Local and upval variables

Lua applications should always make maximum use of local and upval variables in preference to global refences etc. This is for two primary reasons:

Upvals are also an incredibly cheap and effective way of privately sharing variables between functions and across invocations of the function(s). You just have to a little careful and remember than an upval isn't avaliable for collection until all of the closures which use it are themselves dereferenced.

Floating vs Integer builds.

Many developers chose Integer build option over Floating Point(FP) for two reasons: RAM usage and performance, but then they also have to complicate their coding to do everything by integer evaluations. However:

However note the with Lua 5.3 there are no performance or storage diffrences between the integer and FP implementations so I will be recommnedning dropping support for integer builds.

Differences in my coding practices over this last two years

Standard vs Emergency Gargage collection

Out default setting runs the EGC full collection after every storage operation. If the standard LGC has been properly configured then this is unnecessary and slugs kills the performance of most applications. It is very easy to set up the standard LGC to do everything that your applications needs with a low-impact incremental collection, that has perhaps 10× less overhead than node.egc.ALWAYS. In my view, the EGC should be just that and set to node.egc.ON_ALLOC_FAILURE.

Cross compiling

Even though the Lua RTS supports on-device compilation, the RAM overheads of doing this are significant, and so the maximum size of source compilation unit is limited. This is too much of a PITA. It is just a lot easier to use luac.cross and this enables you to have larger loadable lc files.

SPIFFS imaging

Using tools like ESPlorer to bootstap loading a PSIFFS image is also a PITA. Most of my applications make very limited use of SPIFFS if any for R/W data. My standard build use a SPIFFS that is "big enough", and I just image it down on the device for first use using esptool. Simple and fast.

Using a decent provisioning system and server-side compilation

I now handle udpates using my provisioning system. This maintains a shadow directory on the provisioning host. If I need any change a source component, then I just make the change in the shadow directory using my favourite editor, and then issue a reprovision command to my ESP applicaiton: maybe 30s later the ESP has rebooted into the new application version.

LFS

This is in many ways "more of the same" moving Protos and Strings out of RAM and into Flash. Now coding 2,000 line applications is simple and straightforward.

Having a decent remote gdb to develop new libraries and core components

This isn't relevant to pure Lua developers, but this represents a step change in capability for library developers.

Node Red

This one is perhaps a strange one for most contributors and merits some explanation. Node Red is an IBM graphical IDE for built around a node.js framework. I use this for implementing all my server-side Home Automation / IoT integration. node.js is archtecturally very similar to NodeMCU Lua.

So what I have found is that I now I to code my ESP Lua apps in a more node.js-like style and v.v. And this does materially improve the layout and maintainability of my Lua code.

It's a shame that we don't have a Node red-style wrapper for our Lua develoeprs as this would make it a lot easier easier for them to grasp this event driven non-blocking cding style.

marcelstoer commented 6 years ago

Since the issue title asks for "What is missing from ... FAQ" I thought I'd reference #1861 and #1862 here.

TerryE commented 6 years ago

I am again overwhelmed by the rich response from the other committers, so it looks like the next round of updates is going to be another solo effort. :unamused:

One area where we do need more detail is in the Build Options discussion. We need to explain what the user_*.h files are and why a developer might need to change them. I also think that the sort of summary that I discussed in marcelstoer/nodemcu-custom-build#27 would be useful.

marcelstoer commented 6 years ago

it looks like the next round of updates is going to be another solo effort.

Maybe to lessen the disappointment...did you notice that I ironed out all the (Markdown) formatting problems just before the recent master drop? No material changes but improved legibility.

TerryE commented 6 years ago

@marcelstoer thanks for this. Perhaps you can do a similar editorial filter on my next update? Much appreciated :smile:

marcelstoer commented 6 years ago

Sure, just tag me when you think there's something I should review.