Reducing memory use SD card, sscanf, etc.
geo_leeman
Posts: 190
in Propeller 1
Hi all - I'm doing one of those "worst case" projects for memory use on the prop. I'm getting a unit running that logs data from a serial instrument at ~10 Hz and writes it to an SD card. I'm checking that the received packet has the appropriate number of tabs (is at least roughly complete) and parsing it out in a packet struct that goes into a small ring buffer to prevent missed packets during SD block jumps (tested, and it does happen). I'm sitting at ~32180k sadly.
I think the main issues are in handle_state_write_data (line 108 TML.cpp), get_filename (line 240 TML.cpp), and read_new_data (line 52 ptm100.cpp). I've searched around on the forum, but didn't find a solution that appeared to help me much. Any ideas to help get this down a bit? I've got a bit of cleanup and minor implementation left to do, but it's pretty much all here now.
I switched to parsing the data out into the ptmpacket struct (ptm100.h) instead of just putting serial characters into the ring buffer to make it easier to only write full sentences, and maybe do some basic math on it in the future.
I've also attached a picture of the actual logging unit in case you're curious
I think the main issues are in handle_state_write_data (line 108 TML.cpp), get_filename (line 240 TML.cpp), and read_new_data (line 52 ptm100.cpp). I've searched around on the forum, but didn't find a solution that appeared to help me much. Any ideas to help get this down a bit? I've got a bit of cleanup and minor implementation left to do, but it's pretty much all here now.
I switched to parsing the data out into the ptmpacket struct (ptm100.h) instead of just putting serial characters into the ring buffer to make it easier to only write full sentences, and maybe do some basic math on it in the future.
I've also attached a picture of the actual logging unit in case you're curious
Comments
Are you already using CMM mode (as opposed to LMM)? (EDIT: It seems you do. The compiler command was inside the .side file)
If you have a 64k EEPROM, I think I recall there being an option to more-or-less automatically put cog images in the upper 32k and load those at runtime.
Is there anything that you could do in PASM to reduce the footprint, or use an existing (smaller) object(s)???
As Wuerfel_21 suggested, you could try breaking your program up into multiple programs, and store them in the upper portion of a 64K EEPROM.
All of the Simple objects are great, but some of them are more space efficient than others. Their primary objective is to be easy (think education) - not efficient.
Based on the filenames, I'm guessing you're using the SD driver shipped with PropGCC & Simple. You might try switching to libpropeller's SD class. No promises that it will be smaller, but it might be.
For once, I specifically do not recommend PropWare's SD/FAT library because it is actually a little bit larger than PropGCC + Simple's version (though many times faster).
It's not. It is an interesting coincidence though! But based on the fact that Dave Hein is recommending you switch to CMM, I'm guessing he compiled as LMM. I can see in your .side file that you're compiling as CMM already, and indeed, it does compile for me. I'm using a different compiler (built March of '17) and PropWare's build system, but without any code modifications I get the following:
.
Pretty darned close to your numbers.
I see a lot of usage of functions with the name "print" and "scan" in them. Unfortunately, all of those are very heavy weight. For the print functions, there are usually printi alternatives. For instance, replacing dprint and sprintf with dprinti and sprinti should drop your code usage significantly. For the scan functions, try replacing them with your own scan function utilizing atoi. The reason all those print/scan functions are bloating your code size is because they include support for floating point types. PropGCC includes the special *i functions where the "i" stands for "integer" (presumably?), implying that these special functions exclude floating point support.
Post another version of your code and an update on your code size once you have that done and we can go from there if further changes are needed.
You could try converting all the code to C, and maybe that would reduce the size.
Also, forgive my suggestion of sscani... despite it being listed in the Simple header, there is no implementation for it (that PropWare's build system can find). If I try to build a program that uses it, I get a linker error. If I look through the Simple source code, no such file exists and I couldn't find the function in any similarly named files. I guess it's a bug in Simple? Should the implementation be added or the prototype removed from the header?
In any case, @geo_leeman, my recommendation stands: replace dprintf with dprinti and write your own scan function that utilizes atoi instead of sscanf. I've been working on your code to convert the sscanf function calls to use PropWare's Scanner class (mostly just for fun.. I don't expect you to use it...) and will let you know if that does anything for your code size soon. Once I'm done with that, I'll take a look at the rest of the code and see if there are any other low hanging fruit.
- Replace dprint with dprinti - done and saved about 4k
- Replace sprintf - actually make the memory situation worse
- Replace sscanf with atoi implementation. I commented out sscanf everywhere and saved some memory but that's not the biggest bloat.
- The struct is ~70 bytes. Changing the ring buffer size verifies this.
The biggest offender is.... static ptmpacket data_packet in the read_new_data function. If you take the static out the code drops to 21k total. If you move it out of the function to a global - same result. Any ideas there? Sure, moving it out solves the issue, but why is putting it in the method bloating things up so much?
You'll need to download PropGCC separately. That's easy enough to get from here: https://david.zemon.name/PropWare/#/related-links
Keep in mind that using PropWare as your build system does not force you to use PropWare's objects. The installation packages include PropWare's classes as well as Simple, libpropeller, and libArduino. And to get you started, here's the CMake script I've been using to build your code:
I don't want to get this thread too far off topic though, so if you have any questions about how to install/use it, we can either take it over to the PropWare thread or privately via email or PM.
So yes, the end solution here is that it's a compiler bug and moving that struct from a static to a global solves the size issue.
I'm sitting at about 25-26k total now, which should be workable if I understand the 32k limitations. Not a ton of headroom, but some. Any guidance on the max advised?
Results in:
Code size = 4368
Total size = 4540
But if I move those led variables to global:
Code size = 4392
Total size = 4588
I get a larger code size. Now, I haven't looked through the generated assembly, but I would bet this isn't any less efficient. I bet it's just letting the compiler do more static init and less at runtime.
Now, with that knowledge, I'll leave it to you to find an appropriate balance between knowing space requirements for your program and "globals are evil"