Author Topic: All variables should be Global  (Read 18086 times)

0 Members and 1 Guest are viewing this topic.

Offline @rtTopic starter

  • Super Contributor
  • ***
  • Posts: 1059
All variables should be Global
« on: February 27, 2017, 02:45:35 pm »
Hi Guys,
That, I believe, is what’s known in the biz as clickbait :)

It occurs to me that in microcontroller land, and in C,  that all functions should be void functions,
and have no arguments passed to them, or returned from them by anything other than global variables accessed directly within the function.

Why is anything other than the above, anything other than a waste of time using a stack for nothing?
.. other than some guy with a suit & tie said: “Variables should be private unless global in their nature”.
I know the obvious reason of the Human programmer making a mess, but is there a real reason?

 

Offline capt bullshot

  • Super Contributor
  • ***
  • Posts: 3033
  • Country: de
    • Mostly useless stuff, but nice to have: wunderkis.de
Re: All variables should be Global
« Reply #1 on: February 27, 2017, 02:49:36 pm »
There are many reasons to do so:

limited ressources (RAM), limited ressources (Flash memory), and limited ressources (processing speed)
Coding OOP style on a 8K Flash / 256B RAM micro is considered bloat ;-)
Safety devices hinder evolution
 

Offline @rtTopic starter

  • Super Contributor
  • ***
  • Posts: 1059
Re: All variables should be Global
« Reply #2 on: February 27, 2017, 03:04:33 pm »
Yes sure your variables can be reused, but if you can get away with it...
you could also do faster, and sometimes save memory.
I can’t see any way to get past the copy to some other memory or stack where the arguments are passed,
unless that’s where an optimiser comes in.

Code: [Select]
x = 0; y = 0; // passed as arguments
while (x < 100) {setpixel(x,y); x++;}

x = 0; y = 0; // global
while (x < 100) {setpixel(); x++;}
 

Offline cyr

  • Frequent Contributor
  • **
  • Posts: 252
  • Country: se
Re: All variables should be Global
« Reply #3 on: February 27, 2017, 03:19:26 pm »
Using global variables may stop the compiler from doing optimizations like sticking the values in registers...

Having code that is readable, maintainable and reusable are "real" reasons BTW.

 
The following users thanked this post: janoc, moz

Offline donotdespisethesnake

  • Super Contributor
  • ***
  • Posts: 1093
  • Country: gb
  • Embedded stuff
Re: All variables should be Global
« Reply #4 on: February 27, 2017, 03:25:25 pm »
I know the obvious reason of the Human programmer making a mess, but is there a real reason?

That is the real reason. Programmers are human.

BTW, you failed the interview, sorry :)
Bob
"All you said is just a bunch of opinions."
 

Offline @rtTopic starter

  • Super Contributor
  • ***
  • Posts: 1059
Re: All variables should be Global
« Reply #5 on: February 27, 2017, 03:40:52 pm »
Quote
That is the real reason. Programmers are human.

BTW, you failed the interview, sorry :)

You already know that I already know not to show that to an interviewer, but yeah, I know :D



 

Online hans

  • Super Contributor
  • ***
  • Posts: 1639
  • Country: nl
Re: All variables should be Global
« Reply #6 on: February 27, 2017, 03:47:44 pm »
I think these styles can be architecture specific. For example, on 8-bit PIC global variables is actually employed in the background, because that's basically the only efficient memory model it supports to run C.

But it is done for you, and the compiler can make choices on reusing certain bits of memory depending on the call nesting of your program and which local variables exist at what time. I would argue to not try to beat the compiler.


Yes sure your variables can be reused, but if you can get away with it...
you could also do faster, and sometimes save memory.
I can’t see any way to get past the copy to some other memory or stack where the arguments are passed,
unless that’s where an optimiser comes in.

Code: [Select]
x = 0; y = 0; // passed as arguments
while (x < 100) {setpixel(x,y); x++;}

x = 0; y = 0; // global
while (x < 100) {setpixel(); x++;}

I think you'll find neither is significantly quicker or memory efficient on something like ARM. The first has to push/pop variables onto the stack (which it does anyway as per the calling convention), and the other needs to store variables to fixed addresses (which is cumbersome on ARM) and load them too. I suppose both will have very similar levels of overhead.
But in all cases I would favour the first one. It is more readable, more maintainable, less of a mess , and most importantly probably a lot better inlineable by the compiler and thus quicker.
And useful for some: unit testing is a lot easier when you don't have tons of global variables that need housekeeping.
 

Offline cyberfish

  • Regular Contributor
  • *
  • Posts: 240
  • Country: gb
Re: All variables should be Global
« Reply #7 on: February 27, 2017, 04:34:19 pm »
It occurs to me that in microcontroller land, and in C,  that all functions should be void functions,
and have no arguments passed to them, or returned from them by anything other than global variables accessed directly within the function.

If you set arguments as globals and return values as globals, you are just doing the compiler's job for it, and it can probably do a better job than you at passing and returning values to/from function.

When you have a modern compiler, on many architectures, arguments and return values aren't actually passed using the stack. Registers are used for the first few arguments instead. Return values are often not copied at all (return-value optimization). Even if the stack has to be used, many architectures have dedicated instructions for using the stack, that will be faster than doing indirect load/stores to globals. On ARM for example, there are instructions to push and pop multiple registers in a single instruction. On higher end microcontrollers with cache (like some Cortex-M7-based ones), stack also has very good temporal AND spatial locality.

Function call mechanism has been optimized to death already by both hardware designers and compiler writers. It's unlikely that you can beat it.
« Last Edit: February 27, 2017, 04:37:44 pm by cyberfish »
 

Online T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 21686
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: All variables should be Global
« Reply #8 on: February 27, 2017, 05:21:15 pm »
One word: interrupts.

Reentrant and recursive programming requires stack (or if not hardware stack, then a software emulation of it).

That's still no hard requirement -- interrupts can usually be serviced quickly, and usually one at a time.  But it's a lot easier to deal with things if they can pile up a modest amount.

You can design a system so that interrupts are used more like events, where multiple concurrent events (even from the same source) can be executing.  An example might be a global timer interrupt, which fires off housekeeping activities periodically (reading inputs, swapping tasks in an RToS, etc.).  Each of those events is stored on the stack, on top of the interrupt that started it.  That interrupt might fire again before the first one has finished, in which case, a non-reentrant version gets its variables trashed!

Not to say functions should be exclusively reentrant, either: it can be very useful to keep a global state, to say for example: if an optional function is still waiting on the stack, skip over it this time.  This might be implemented by setting a static (global) "busy" flag within the function (and clearing it at the end of the function), and checking that flag before jumping into the function again.

A universally applicable case of this: checking stack size/position/usage.  If you have an arbitrarily recursive program, with no provable limit on stack requirements, and you outright ignore how much stack is being used, then some day, your program is going to steamroll through other memory.  Madness ensues.  >:D

Which relates to the theoretical concept: a Turing-complete machine has unlimited memory, and can recurse [computably] infinitely.  An ideal Turing machine does not need to check for stack size, because it won't run out.  All our so-called "Turing-complete" machines are semantically complete, but finite in storage, so they cannot truly compute any computable function.  (Example: no one has calculated what Ackermann(999, 999) is, but it's proven computable.  Oh, also... we only have finite time to compute something.  So, say, while it's absolutely possible to find collisions in any hash function, a SHA-1 collision has been realized only recently.)

So, as programmers of not-quite-Turing-complete machines, we must be aware of their limitations, and dirty our theoretically ideal functions with necessary precautions.

Tim
« Last Edit: February 27, 2017, 05:24:11 pm by T3sl4co1l »
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 
The following users thanked this post: janoc, 3db

Offline Sal Ammoniac

  • Super Contributor
  • ***
  • Posts: 1670
  • Country: us
Re: All variables should be Global
« Reply #9 on: February 27, 2017, 05:25:15 pm »
Complexity is the number-one enemy of high-quality code.
 

Offline mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 13747
  • Country: gb
    • Mike's Electric Stuff
Re: All variables should be Global
« Reply #10 on: February 27, 2017, 05:32:58 pm »
Unless you're doing high volumes, dev &debug time costs more than silicon, so using a slightly bigger part to make coding quicker or more reliable is a no-brainer.
By making things unnecessarily global you are depriving the compiler of info that could be used for optimisation.
One thing I feel is missing from C is the concept of local procedures, like Pascal, so you have a mid-way point between local and global vars.
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 
The following users thanked this post: Frost

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9890
  • Country: us
Re: All variables should be Global
« Reply #11 on: February 27, 2017, 05:35:43 pm »
Sounds a lot like Fortran COMMON to me!

For very large programs, including those with overlays, COMMON blocks were used to statically allocate variables.  Whether or not that is a good idea is something I'll leave to the computer scientists.

The first computer I ever programmed was an IBM 1130 with 8k words of memory.  It wasn't unusual to have user subroutines loaded on call.  Even system routines could be loaded on call, the Fortran compiler had 27 overlays (called phases).  This resulted in a lot of disk activity but memory was expensive.  Mainline programs often had a massive COMMON statement that carried through to all the subroutines.  The only good news is that it was possible to duplicate the cards and simply add them to the deck.

Data hiding and protection seems like a much better approach.  At least keep variables file static.  The whole world doesn't need to know.
 

Online tszaboo

  • Super Contributor
  • ***
  • Posts: 7383
  • Country: nl
  • Current job: ATEX product design
Re: All variables should be Global
« Reply #12 on: February 27, 2017, 05:42:29 pm »
One word: interrupts.
The keyword "volatile" should be used by all data, which is acessed by interrupts, and then magically it will work.
To OP: Avoid "new" and "malloc" (or use malloc once and never free it) define everything compile time, dont use pointer list and silly things like that. Otherwise you can return an int for a function. What you must avoid is memory fragmentation, and running out of memory. Dont be so strict, it is unnecessary, unless some automotive silliness requires it. So yes. Use local variables, it is fine. Just give the stack some space.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4036
  • Country: nz
Re: All variables should be Global
« Reply #13 on: February 27, 2017, 06:04:02 pm »
Hi Guys,
That, I believe, is what’s known in the biz as clickbait :)

It occurs to me that in microcontroller land, and in C,  that all functions should be void functions,
and have no arguments passed to them, or returned from them by anything other than global variables accessed directly within the function.

Why is anything other than the above, anything other than a waste of time using a stack for nothing?
.. other than some guy with a suit & tie said: “Variables should be private unless global in their nature”.
I know the obvious reason of the Human programmer making a mess, but is there a real reason?

Well, that's just wrong.

In AVR land wth a C compiler, the first 18 bytes of function arguments are in registers. The first 8 bytes of those you just go ahead and use, the next 10 you have to make sure you save and restore before you yourself return.

8 bytes is usually plenty, if your arguments are pointers or 8/16 bit integers. Why on earth would you want to use extra code and time to copy those to global variables, and then time and code to read them from the global variables in the called function? Decent modern compilers can usually have those values generated into the right place for a subsequent function call, so you don't even have to use code and time to move them there.

On ARM, the first 16 bytes (up to four arguments if 32 bits or smaller) are passed in registers. Again, usually enough.

On RISC-V (which I have in my "HiFive1" Arduino clone, and may well be found in increasing numbers in embedded work in future) the first 32 bytes of arguments (up to 8 arguments) are passed in registers. And the called function had all those eight registers to play with, plus another seven "temporary" registers, before it even has to think about touching memory.

As for global variables vs local variables in the main function ... it makes no difference. Most systems are allocating global variables starting from some low address (just above the code, if code isn't in another address space). First the initialized variables (copied from flash or wherever) and then the zero-inited variables. Then you have the "program break" and space after that is allocated by sbrk() (or malloc() calling sbrk()). The stack starts from the top of memory and works its way down towards the program break. If they ever meet .. bad news. It makes basically zero difference whether something is a global, allocated by malloc() at program startup (and never released), or or in the stack frame of main(). You've got the same memory use either way, with the only difference being exactly where the stack and program break will meet (if they do).

Systems don't have to be organized that way, but these days most that use C have converged on the same layout.

For functions other than main() ... if several functions share some data, and there's only every one copy of it .. sure, make it a global rather than passing around a pointer. Things such as counting how many times something happened in a function, spread over many function calls, should be globals.

Temporary data, used only by one function (or by functions it calls) should be on the stack. The stack gives you automatic overlays of the memory areas used by different functions that are called at different times (and don't call each other). The *only* valid reason not to use data on the stack is if your CPU has poor support for a stack. That's the case for some older CPUs such as 6502 (and PIC?), but it's most definitely not true for AVR or ARM or even anything derived from 8080/Z80.
 

Online hans

  • Super Contributor
  • ***
  • Posts: 1639
  • Country: nl
Re: All variables should be Global
« Reply #14 on: February 27, 2017, 09:20:18 pm »
The 8-bit PICs only have a HW stack for program counter only if I recall. All data must be passed through RAM, with perhaps the exception of 1 argument and return which can be passed through the processor register.

That's why I said in my previous comment that this style of coding could be architecture dependent, if indeed a programmer tries to target such a platform. But a modern compiler like XC8 will have stack emulation techniques built in (take a moment to browse in the compiler settings). I recall the default one tries to achieve the most compact variable layout with most amount of sharing depending on the call tree, but there were 2 others one as well.
On PICs it's not uncommon to have functions compiled twice, in particular when it needs to be reentrant from ISR and main code. Some algorithms may also have some kind of recursiveness (maybe only 1-2 calls deep) which will make trouble using globals. Having the compiler handle this for you, instead of manually coding this using globals, is a big relief.

Trying to beat that level of compiler ingenuity is not worth it IMO, but I'm not surprised it's being done. It depends on what you need I suppose, maybe automotive that demands simple CPUs use these chips and also wants to eliminate any "intelligence" not in control of a human between build cycles. But then I would say: write assembler. If C is truly a portable assembler, I wouldn't want to worry that much about the memory model of my target architecture.

I think it's also common censuses on this forum that we rather code things well than code things for mass production (qty: >100k-1M) devices.
 

Offline snarkysparky

  • Frequent Contributor
  • **
  • Posts: 414
  • Country: us
Re: All variables should be Global
« Reply #15 on: February 28, 2017, 04:25:07 pm »
Execution efficiency should matter.  Trust me it will one day.  Remember extra cycles used in the name of clean code use extra energy.

How much energy is wasted by CPU's having to clock faster to process suboptimal code. 

It's my pet peeve because i write code for micros running on batteries so I like to be stingy with the cpu cycles for battery life.

Would somebody care to explain how globals can be less efficient in terms of execution time.

Oh and please let me have it regarding my uninformed and silly ideas.
 

Offline cyberfish

  • Regular Contributor
  • *
  • Posts: 240
  • Country: gb
Re: All variables should be Global
« Reply #16 on: February 28, 2017, 04:27:33 pm »
Execution efficiency should matter.  Trust me it will one day.  Remember extra cycles used in the name of clean code use extra energy.

How much energy is wasted by CPU's having to clock faster to process suboptimal code. 

It's my pet peeve because i write code for micros running on batteries so I like to be stingy with the cpu cycles for battery life.

Would somebody care to explain how globals can be less efficient in terms of execution time.

Oh and please let me have it regarding my uninformed and silly ideas.

A few people (including myself) have already. Read replies above.

Using function arguments is faster, uses less RAM, and also cleaner. Also more flexible (allows recursion, and calling from interrupt handlers). Main reason is that most arguments are passed using registers anyways, which is faster than globals.
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: All variables should be Global
« Reply #17 on: February 28, 2017, 04:45:24 pm »
Functions are also a complete waste of time. You should put everything in assembly!

Now serious. C and C++ are all about keeping data where is belongs.
If you put everything global you will have a lot of data where it does not belong. This will impede other coders, and eventually also you, in understanding what is going on.

C has a few scopes where data is relevant.
1. Global, referenced from everywhere. No protection from mistakes whatsoever.
2. File, referenced only from file.c, use the static keyword to enforce this.
3. Static function scope, this variable has the static keyword used on it in a function. It can only be referenced from function scope. Between the {}. However, it is allocated permantently. Meaning it's like a global, but private.
4. Function scope, these variables are only allocated for function duration. Such as the arguments. You'd be making a mess with "tempvar" if you insist in making these global.
5. Block scope. Wherever in the code you can nest a block {}. You can do this for the editor the collapse the block. But in strict ansi c you can use it to create another level of temporary veriables only allocated during the block. (Since you can only do this at the start a of block) Useful hints for you and the compiler. An for with {} has such block. There are often iterators inside.

Ignoring the implementation of ancient or crap compilers, it would be a waste not using the tools on hand in C to create more structured and segmented code.
You can put everything in 1 file, or even in main. Or all assembly. But it wastes the most expensive commodity on hand. developer time.
« Last Edit: February 28, 2017, 04:51:11 pm by Jeroen3 »
 

Offline snarkysparky

  • Frequent Contributor
  • **
  • Posts: 414
  • Country: us
Re: All variables should be Global
« Reply #18 on: February 28, 2017, 04:51:25 pm »
Well what I read there concerns using the registers which are faster.  But doesn't this need to assume that the values are already in the registers when the function is called.  That certainly would be the case many times but not always.  Some CPU operations can act directly on memory without loading to a register so this would seem to be a plus for globals.  But I guess the main idea is that variables passed through the function interface are not necessarily copied to the stack if they are small enough for registers.  I get it.
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 4036
  • Country: nz
Re: All variables should be Global
« Reply #19 on: February 28, 2017, 05:05:21 pm »
Well what I read there concerns using the registers which are faster.  But doesn't this need to assume that the values are already in the registers when the function is called.  That certainly would be the case many times but not always.  Some CPU operations can act directly on memory without loading to a register so this would seem to be a plus for globals.  But I guess the main idea is that variables passed through the function interface are not necessarily copied to the stack if they are small enough for registers.  I get it.

No CPU acts on memory without copying it to a register. It's just that the temporary register used doesn't have a name and you can't see it.

Accessing memory takes probably ten times the energy of accessing a register that already has the value.
 

Offline cyberfish

  • Regular Contributor
  • *
  • Posts: 240
  • Country: gb
Re: All variables should be Global
« Reply #20 on: February 28, 2017, 05:09:11 pm »
Well what I read there concerns using the registers which are faster.  But doesn't this need to assume that the values are already in the registers when the function is called.  That certainly would be the case many times but not always.  Some CPU operations can act directly on memory without loading to a register so this would seem to be a plus for globals.  But I guess the main idea is that variables passed through the function interface are not necessarily copied to the stack if they are small enough for registers.  I get it.

If the values are in memory and not in registers, using globals would require copying those values from memory to memory first. m-2-m is slower than m-2-r on most architectures, and maybe equal on some.

The only time it can be faster is if the argument is already in the global you need. This is almost never going to be the case, unless you have a chain of functions you know you'll be calling one by one. In this case using arguments and return values still beat it because it allows the optimizer to do return-value optimization which eliminates all those copies.

Using globals also stop the compiler from doing optimizations like function inlining, which even eliminates the function call overhead, and allows more optimizations to be done following the inlining.

Modern optimizers are very smart. A lot of times the best thing you can do is to write your code in the simplest and cleanest way, to make the optimizer's job easier. Unless you know exactly what you are doing, and you know the architecture better than the compiler, being clever will often just confuse the optimizer and you get sub-optimal code.

Don't make your code ugly for no reason. In this case it's even slower.
 

Offline grumpydoc

  • Super Contributor
  • ***
  • Posts: 2905
  • Country: gb
Re: All variables should be Global
« Reply #21 on: February 28, 2017, 05:30:59 pm »
Unless you're doing high volumes, dev &debug time costs more than silicon, so using a slightly bigger part to make coding quicker or more reliable is a no-brainer.
By making things unnecessarily global you are depriving the compiler of info that could be used for optimisation.
One thing I feel is missing from C is the concept of local procedures, like Pascal, so you have a mid-way point between local and global vars.
Nested functions are available in GCC as a extension.

AFAIK they are not yet in ANSI standard C.

As to the original question - why do (some) embedded programmers seem to feel that they can abandon all good coding practices as suits their whim?

If everything is a global your code will not be re-entrant, and your compiler might miss optimisations but more importantly anything which is longer than a single A4 page will simply become unreadable and unmaintainable. You will also invariably create bugs by using the wrong variable accidentally. Don't do it.
 

Offline cyberfish

  • Regular Contributor
  • *
  • Posts: 240
  • Country: gb
Re: All variables should be Global
« Reply #22 on: February 28, 2017, 05:35:21 pm »
Nested functions are available in GCC as a extension.

It has been available in C++ since C++11 though!

Code: [Select]
int main() {
  auto increment = [](int x) -> int {
    return x + 1;
  }

  int a = increment(2);
}
 

Offline slicendice

  • Frequent Contributor
  • **
  • Posts: 365
  • Country: fi
Re: All variables should be Global
« Reply #23 on: February 28, 2017, 05:43:56 pm »
Functions are also a complete waste of time. You should put everything in assembly!

LOL, writing Assembly the correct way (the most memory conservant and efficient) would be the equivalent of a C-function with input parameters and a return value (if you need them, depends of application complexity), and local variables for just enough values to get the function job done. The simpler the function, the better. You want to stick to the registers only, as far as possible, and if the code must jump, it should do a short jump (calling the stack) and try to avoid long jumps (getting new instructions from memory) as much as possible.

A variable is just a memory address, the fewer addresses you need at one point in time, the better. Defining everything global would require huge amounts of memory, or you would have a hard time making sure variables are not overwritten.

Using ANY modern C compiler today, should produce really good Assembly code for the most basic functions. There should not be any big difference between writing Assembly or C. I am not sure how the ARM compilers behave, as I have not tested it, but all C compilers has produced exactly the same Assembly code as I have written manually in Assembly. There are however a lot of exceptions to be aware of, and this is where writing pure Assembly comes in play. The difference in size can be many many bytes for just one single instruction. Sometimes there are multiple solutions(in Assembly) for the same simple problem, but the size of your code can differ a lot between them.

 

Offline grumpydoc

  • Super Contributor
  • ***
  • Posts: 2905
  • Country: gb
Re: All variables should be Global
« Reply #24 on: February 28, 2017, 05:44:44 pm »
Nested functions are available in GCC as a extension.

It has been available in C++ since C++11 though!

As if C++ was not a deep enough quagmire of features ready to drown yourself in!
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf