OK, I performed some tests. Here's a brief(?) summary of the tests, the results, etc. I have attached my test projects, which also has more detailed run captures, on the off chance anyone might want to fiddle with it some more without redoing the test code from scratch.
This was run on NP2 firmware 4.2.2.1, and compiled with DevStudio 2010 Ultimate.
Four tests ('static' means 'not dynamicallly loaded'):
1) baseline static, trivial operation, get final approximate free memory with GC(true)
2) baseline dynamic, same trivial operation, but in a separate assembly loaded from SD card
3) nontrivial static, has reference to hefty module, use it minimally
4) nontrivial dynamic, same thing from SD card. This has three flavors; more on that later.
OK, right as I started out constructing test 2 it becme obvious that I personally was not going to be able to use this technique for my application because this does indeed load the assembly into RAM, and currently my assembly is 58k in release build (with deps it gets to 90k deployed), but my app currently quiesces with about 50k bytes free, so in my case I have a RAM shortage. So much for me and my application.
Still the technique seems interesting so I pressed on with the tests. Here is a summary of the final free memory as reported by GC(true), which is fairly approximate as it turns out; more on that later.
Test01 -- 105012
Test02 -- 87708
Test03 -- 99468
Test04 wontwork -- n/a
Test04 explicit -- 59172
Test04 implicit -- 82080
(the doc in the project has more detailed sampling points throughout the execution if you're at all curious)
Test 4 has three flavors because it has a reference to another assembly. There is a 'wontwork' project that, well, wont work, because it does not satisfy that dependency. It just there to prove the point, and if you want to see how the system behaves in that case. The other two satisfy that dependency by either having the host (loading) module reference the dependent assembly itself (implicit), or having that modules load the dependency from the SD card (explicit).
Some Observations:
* you need the 'le' version of your built module. Assembly.Load() will fail if you pick the wrong one. Not lots of helpful debug messages, though.
* scary debugger message when doing Assembly.Load()
"Invalid address 0 and range 40 Ram Start 20000000, Ram end 20020000"
I grepped in the firmware source and found it as part of Debugger.cpp Interestingly, this same line is commented out in 4.2.1 stuff. So you may or may not get it depending on your firmware version.
I think it is confined to the debugger and benign in the real world. To be sure I would have to attach the jtag and analyze the call stack and I was not in the mood for that mishegas so I skipped looking into it further.
* GC(true) is pretty approximate. In one run I had memory increasing with each alloc! I put in a 10 second sleep and the numbers made more sense, so I guess that there some internal framework-allocated resources that get released in a manner outside of your control. I mention this so that in general folks don't get too caught up in GC(true) numbers or attribute more vercity to them than they are worth. They're useful but can be misleading if you don't double check sanity.
* When explicitly loading SystemTextRegularExpression in test 4 'explicit' the app would hang at the Assembly.Load(). However, it would immediately unhang if I set a breakpoint while it was hung! I don't know if the hang is debugger related (and not happen with no debugger attached). To find out I one would need to change all the Debug.Prints() to spew to a file or something else and run it without the debugger to see what happens. But this doesn't inspire confidence in stability. Note, no hangs of this sort were experienced with the 'implicit' load test.
What can you do with it?
* Well, in my case I can't use it as-is just because my project is too big. But if your particular application isn't strapped for RAM, then this may be a viable approach.
* Also, maybe with planning you can use it more effectively. For instance, my project has three different communications mechanisms, but really in practice only one of them will be used depending on the site requirements. So maybe I could break up my project so that it only loads the one it needs, like an old-school 'overlay'.
* It could be very useful in certain niche cases, loading some sort of 'personality' from the SD card. For instance, I have a site specific config file on the SD card, and I have code I wrote to parse it. But alternatively I could have an assembly that consists of the string values and dynamically load that instead of parsing the config file. Those values would have wound up in RAM anyway, and I save all the parsing code, so it could be a memory saver in the end.
* a writeable byte array with an executable assembly in it? hmm, all sorts of self-modifying code shenanigans come to mind!
Hope this is of interest to someone!
-dave
p.s. Fabien Royer did all the work to figure out how to load the assemblies, as per the link Dave VanderWekke provided above -- I just made these tests. There's a bunch of other interesting things on that site, so it's worth a visit irrespective of this dynamically loading thing.
NP2DynLoadTest.zip 36.25KB
17 downloads