Massive memory optimization: flash functions! (+SPI SSD1306)
Posted: Sun Mar 08, 2015 2:06 am
EDIT: There is now a simplified version of this script with even better memory performance. I have attached it to this post (flashmod.zip). See my post several posts down for usage and information.
EDIT2: Added oledv2.zip using the new flashmod approach. See my post on page 2 for details.
It seems like the primary heap usage on my apps so far is the functions themselves. It seems like that is the primary drawback of lua compared to native C apps. C functions can be executed directly from flash, while lua functions must be loaded into the heap to run. So, I decided to create a module to fix that.
Enter flashmodule.lua. Here's how it works. You define 3 tables in your module: public, private, and flash. Then you put data and functions on public and private, and functions only on flash. The private data/functions will remain in memory, but only be available in the environment of your public and flash functions. The public data/functions will remain in memory, but your module will return public, so these functions will also be available to users. The flash table is where the magic happens. These functions will be serialized to a file on flash, named <modName>_<funcName>.lc. Then, through the magic of metatables, flashmodule makes these methods appear to be on the public table, except they are dynamically loaded from flash at the moment they are called.
Note: local variables in your module file are only available on the closure of private and public functions. Flash functions can access private, public, and global data, but if they try to access local data, it represents an upvalue that makes the function not serializable, and will not work.
flashmodule.lua is written as a generic file that can be executed as:
Fill in your 3 tables, place this at the bottom of the file, and your done.
Then you can require your module, and treat it just like any other module, with the only difference between flash and public functions being flash functions require a little more time to call. So, place time-critical functions or functions which are called a lot in public or private. But if your app is not time-critical but is memory-constrained, put all the functions in flash and you will see a huge savings on heap.
One other note: you can separate flash functions into their own file, and call flashmodule like this:
Then, in the actual module file that you will require in, call it like this:
By placing them all in the same file, it uses a lot of heap during the require... most of that heap can be immediately garbage collected back, but if you are memory-constrained even at require time, then this technique can help. You can run the flash file once, while your main app is not running, then reset and run your main app and the flash functions will still be there waiting to be called. You can even separate your flash functions across multiple files if you are having trouble running a large module. This can give you the illusion of having a large module with an extensive api without hitting the memory ceiling so quickly.
I'll refactor my lhttpd implementation to use this approach next, to provide a more generally useful example. SSD1306 just provided a nice simple project that people have already been having memory issues with. If I modify this example slightly to use all flash functions and read both font sizes from flash instead of keeping the small font in memory, I can get heap usage down near 2k. It's still under 5k with a sensible selection of in-memory functions, where it was over 9k with the standard module approach.
EDIT2: Added oledv2.zip using the new flashmod approach. See my post on page 2 for details.
It seems like the primary heap usage on my apps so far is the functions themselves. It seems like that is the primary drawback of lua compared to native C apps. C functions can be executed directly from flash, while lua functions must be loaded into the heap to run. So, I decided to create a module to fix that.
Enter flashmodule.lua. Here's how it works. You define 3 tables in your module: public, private, and flash. Then you put data and functions on public and private, and functions only on flash. The private data/functions will remain in memory, but only be available in the environment of your public and flash functions. The public data/functions will remain in memory, but your module will return public, so these functions will also be available to users. The flash table is where the magic happens. These functions will be serialized to a file on flash, named <modName>_<funcName>.lc. Then, through the magic of metatables, flashmodule makes these methods appear to be on the public table, except they are dynamically loaded from flash at the moment they are called.
Note: local variables in your module file are only available on the closure of private and public functions. Flash functions can access private, public, and global data, but if they try to access local data, it represents an upvalue that makes the function not serializable, and will not work.
flashmodule.lua is written as a generic file that can be executed as:
Code: Select all
return loadfile("flashmodule.lc")("oled", public, private, flash)
Fill in your 3 tables, place this at the bottom of the file, and your done.
Then you can require your module, and treat it just like any other module, with the only difference between flash and public functions being flash functions require a little more time to call. So, place time-critical functions or functions which are called a lot in public or private. But if your app is not time-critical but is memory-constrained, put all the functions in flash and you will see a huge savings on heap.
One other note: you can separate flash functions into their own file, and call flashmodule like this:
Code: Select all
loadfile("flashmodule.lc")("oled", {}, {}, flash)
Then, in the actual module file that you will require in, call it like this:
Code: Select all
return loadfile("flashmodule.lc")("oled", public, private, {})
By placing them all in the same file, it uses a lot of heap during the require... most of that heap can be immediately garbage collected back, but if you are memory-constrained even at require time, then this technique can help. You can run the flash file once, while your main app is not running, then reset and run your main app and the flash functions will still be there waiting to be called. You can even separate your flash functions across multiple files if you are having trouble running a large module. This can give you the illusion of having a large module with an extensive api without hitting the memory ceiling so quickly.
I'll refactor my lhttpd implementation to use this approach next, to provide a more generally useful example. SSD1306 just provided a nice simple project that people have already been having memory issues with. If I modify this example slightly to use all flash functions and read both font sizes from flash instead of keeping the small font in memory, I can get heap usage down near 2k. It's still under 5k with a sensible selection of in-memory functions, where it was over 9k with the standard module approach.