0

I am adding (yet) another sub-module to my fairly large project written in C, which I want to run on my little processor that I have. And now I wonder : my new module my be small, but it requires to pile up quite some intermediary information every cycle in order to compute its stuff. I created a data structure to hold all this intermediary data, and that's what I wonder about :

  • I could simply create a global instance of this data structure in my module source file, available at all times from everywhere in my module. But this means a constant RAM occupation of the size of this intermediary data, even when other modules are running. That's what I originally had, and that cannot be optimal in any way.
  • I could create a local instance of this data structure at the beginning of my update cycle, and pass a pointer to it all the time to each function. But it's horrible from a coding point of view (long function prototypes), and it means everytime I call another function, I have to add another copy of that pointer on top of the stack (right ?)
  • I could also create a global variable (scoped to my module) which is simply a pointer to my intermediary data. This means one 8-bit integer constantly there (even when all modules are running), but not more. And then the data is accessible from everywhere in the module without stack overhead and long function prototypes.

Some info as to my system : modules run one after the other, re-entrance only occurs to get results, but not perform further calculations. Intermediary data is really only needed during the update calls of each module, and those always come in the same order.

Which solution is best ? In which light ? Where does the compiler come into play ? Is there any general memory mechanism I do not know about here ?

Thanks in advance for the clarifications !

Charles
  • 988
  • 1
  • 11
  • 28
  • Sounds like you think too your third solution is best. – Paul Ogilvie Dec 02 '16 at 09:36
  • Yes. I wanna make sure and thus I present the problem impartially and completely. Also I want to know if long function prototypes and stacking up of the one same pointer through all function calls are real problems. – Charles Dec 02 '16 at 09:38
  • For my point of view, I prefer second solution. Both first and third have issue with re-entrance. From my experience, create a module based on global will have some issue with it at any moment, specially if you use an embedded RTOS. And impact on stack of passing a pointer is not really important (in general cas)e. For me, difference between first and third solution is really small (only ram occupation), you have all inconvenient of [global](http://stackoverflow.com/questions/484635/are-global-variables-bad). (all I said is for general case, on specific case, of course, my arguments fail) – Garf365 Dec 02 '16 at 10:01
  • 1
    You mention in another comment that your modules run one after another. If the global data is only needed for the life time of one module, and if other modules have similar global data requirements, then you could look at something like linker overlays. For example the GNU linker (https://sourceware.org/binutils/docs-2.19/ld/Overlay-Description.html#Overlay-Description) but whatever linker you're using might have something similar. This would give you an approach like #1 in your list, but without the lifetime concerns that you have. – Andrew Dec 02 '16 at 10:22

1 Answers1

2

This depends quite a lot on what kind of system you are working with, and what's most important, execution speed or memory consumption. I would assume that "little processor" means a microcontroller. You don't mention re-entrancy so I'll assume that's not an issue.

In that case, you are just fooling yourself if you think that having a file scope struct wastes memory. Because you always have to design for the worst case, and the worst case here will be when a function is called that needs to use the struct. At that time, your total, worst-case memory consumption will involve the struct, and it will always be the same no matter if the struct is allocated at file scope or on the stack.

If you don't have enough memory to handle the worst case, then no allocation method in the world will save you. It doesn't make any sense to "temporarily save some memory". Saving some memory when not running the worst case is irrelevant. You will still need to have enough memory for the worst case - either you have this or you don't.

Therefore, the best option is to leave the struct at file scope, because that doesn't affect memory consumption but saves execution time.

If your module needs to be thread-safe or allow multiple instances, then that's another story entirely.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thanks for the reply. Following clarifications must ensue : Yes, it's a 180Mhz, 198 kB microcontroller. Re-entrancy ? I don't know. Worst case is actually : not just this new module, but all modules need max data. But since they run one after the other, and each module frees memory after finishing its cycle update (for example when all of its intermediary data was on the stack) then it's a lot better ! – Charles Dec 02 '16 at 10:08
  • @Charles How can you not know if you need to handle re-entrancy? Start there - if you don't know what your own program is doing, then no amount of program design will help you. And how exactly does freeing memory help you? What do you do with the freed memory when not executing a module? – Lundin Dec 02 '16 at 10:46
  • I didn't know what the expression "re-entrancy" was pointing at. Now I do and edited my original post accordingly. Basically I don't have re-entrancy. My update methods performs once in one go. And so for each module. – Charles Dec 02 '16 at 12:25
  • 1
    @Charles Re-entrancy is a programming term used when a function needs to be called from multiple sources: callbacks, threads, interrupts, processes. It is similar to "thread-safe" - a thread-safe function is a function which protects against race condition with mutex/semaphore locks, while a re-entrant function is one that doesn't need such protection since it doesn't alter shared resources. This is somewhat basic stuff nowadays, when multi-processing is becoming everyday practice. – Lundin Dec 02 '16 at 12:59
  • Yes yes thanks. I am thread-safe (I have no threads) and modules execute sequentially. So no such problems. – Charles Dec 02 '16 at 13:21