Functions returning data vs changing global variable.

I would like some advice whether it is better to have a function return data to the loop or just have the function update a global variable. I know that it is tidier and uses less memory if a global variable is not used, but my question is more related to program speed and also sometimes more than 1 variable needs to be updated from a function.
With a lot of function calls, it seems to be slower with returns vs Global updates but I may be wrong.

Any advice is appreciated.

What specifically are you trying to accomplish? In general, good object oriented design would encourage using a function to read or update variables instead of using a global variable.

With a lot of function calls, it seems to be slower with returns vs Global updates but I may be wrong.

I'm not sure how speed can be compared between functions updating a variable and functions returning a value. I suggest reading this: http://c2.com/cgi/wiki?GlobalVariablesAreBad
Unless you have a good reason to use global variables, don't use them.

There are others who are better qualified to address the speed and memory issues (Nick?), but it's estimated that about three quarters of the cost of software development is debugging and testing. That said, encapsulating your data as much as possible makes those jobs a little easier. Encapsulation is one of the real benefits that Object Oriented Programming brings to the table and is well-worth practicing. Unless your application is really time sensitive, encapsulating your data with local variables makes things a little easier. Also, you can speed some I/O up using the Digital Fast library (Google Code Archive - Long-term storage for Google Code Project Hosting.) although it's easier to shoot yourself in the foot in the process.

I have two comments to make here.
The first: It could require a lot of static memory allocated to have a "return buffer" global for every single function you're using depending on the size of your sketch. You're aware of that, I know from your original post. I can almost guarantee you that you will see no appreciable performance increase when switching from returning a temporary value to using a global buffer. I don't know what evidence you have to suggest that this is more efficient, but if it is, it's a negligible amount of improvement. Using returned values is the standard convention.

The second: If you want to return multiple values, either use a pointer, a container object, or use reference variables.
Example (Container):

struct ExampleContainer{
  int i;
  char c;
  boolean g;
  ExampleContainer(int MyInt = 0, char MyChar = "\n"){ this->i = MyInt; this->c = MyChar; this->g = false;};
  ExampleContainer(const TempContainer& orig) {this->i = orig.i; this->c = orig.c; this->g = orig.g;};
}

ExampleContainer SomeFunction(boolean value){
  ExampleContainer t;
  t.g = value;
  if(value){
   t.i = 1;
   t.c = "C";
  }else{
    t.i = 2;
    t.c = "F";
  }
  return t;
}

Example (Pointer):

//Design by contract: Integer pointer must be at least of size two!
boolean SomeFunction(int* MyValuesWillChange, int value){
  if(value > 5){
    MyValuesWillChange[0] = 1; //This will change the value at the starting address of the pointer you passed in, and will affect the original scope.
    MyValuesWillChange[1] = 0;
    return true;
  }else{
    MyValuesWillChange[0] = 0;
    MyValuesWillChange[1] = 1;
    return false;
  }
}

Example (Reference Variables):

boolean SomeFunction(int& a, int&b, char& c){
  if(a > 5){
    a = 1; //The variable that you passed in for "a" is now equal to 1, even in the original scope.
    b = 0;
    c = "D";
    return true;
  }else{
    a = 0;
    b = 1;
    c = "E";
    return false;
  }
}

It all depends on what exactly your functions do and what data they manipulate.

There is nothing wrong per se with using global variables - it's just a matter of what is "better".

For instance, it may be preferable to modify a global variable if your function returns a value for immediate use, and sets a global variable with some data for later use. Yes, you could return both values (as a struct, pointer, etc), use one value immediately, and remember the other value to use later on in your program. But what if that value needs to be used by a function that's called by a function that's called by a function that may be called by one of three other functions, etc? Passing that one value down the function chain could be a bit of a waste of time, and storing it in a global variable may be more sensible.

As others have said there are pros and cons of each method but functions that return a value can make code much easier to read. Take the case of a function that determines which of 2 integers is largest and returns it to the calling program. To call this function you would use something like

int largestNumber = findLargestNumber(x, y);

It is then obvious which variable holds the result.
If you use a global variable the function call would be
findLargestNumber(x, y);Now which variable is the largest number in ?

Thank you for excellent answers.
My sketch is basically polling 4 sensors through I2C, Each one has a conversion computation involved so each is in a function. Another 2 functions do some other calculations.
All 6 these values are being sent to a 4D Systems LCD which is where the bottleneck is I guess. Because they dont handle -ve values i am converting each value to Char via dtostrf() and sending that via serial.

I was just wondering if speeding up the functions could help but you have answered my question.

Thank you very much. I learn every day

I only use global variables, and I write embedded programs for performance.
SPI for fast hardware interfacing, direct port manipulation for fast IO, data stored in arrays that any code can access, and I have yet to write a function - all code is inlined.
I do use a couple of libraries, mainly Serial and SPI, occasionally Wire if I use I2C devices.
But even with SPI I will turn off interrupts and inline the code, for example:

direct port write the SS pin;
SPDR = array[x]; with 15 nop; following it
SPDR = array[x+1]; with 15 nop; following it
SPDR = array[x+2]; with 15 nop; following it
SPDR = array[x+3]; with 15 nop; following it
direct port write the SS pin;

but that is a bit of an extreme. However nearly 1 byte/uS performance can be achieved.
Using for:loops adds time. Making jumps b ack & forth to functions adds time (and I would argue makes for hard to follow code, from this hardware designers perspective). I use blink without delay programming a lot, and to make things more responsive I use flags to let different sections of code know something needs to be done; I suppose that is function-like in effect. The result is that the loop code is just basically performing a few flag checks, and mostly does not much else.
For example, consider a remote controlled countdown timer with a couple of local buttons:
did time interval pass? Yes, update the to be displayed data, set updateDisplay flag.
did a remote command come in? Yes, update the to be displayed data, set updateDisplay flag.
was a button pushed? Yes, update the to be displayed data, set updateDisplay flag.
Is the updateDisplay flag set? Yes, clear it and update the display (MAX7219 writes, shift register writes).
Alternately, the display might be multiplexed, in which case a 2nd time check is implemented to change to the next digit for instance, and the updateDisplay flag is not needed.
This leaves lots of time for other stuff to occur in the times when none of the above have occurred, which is most of the time.

It's a big pond, lots of way to implement just about anything.

roybrus:
I can almost guarantee you that you will see no appreciable performance increase when switching from returning a temporary value to using a global buffer. I don't know what evidence you have to suggest that this is more efficient, but if it is, it's a negligible amount of improvement. Using returned values is the standard convention.

roybrus,
This along with the examples provided are misleading.
The examples you show are all using pointers and not directly using globals.
Even the C++ call by reference is going to use pointers under the hood
since it can't be guaranteed that the arguments will only be particular globals.

Using globals can be significantly faster than using more standard approaches, when using the AVR.
The reason being that the AVR has pretty weak pointer capabilities.
As a result it has do jump through quite a few hoops to handle pointer references.
When using a global the final address of the variable is calculated at link time vs the pointers
address calculated at run time. Anytime you can shift something away from being done at
runtime you pick up performance.

As majenko said it is a matter of what is "better".
And as far as "better" goes, "it depends".
"better" can vary depending on what is preferred (performance, code size, maintainability) and
the specific situation.

To some "better" may mean easier to maintain and add new features,
but to others, balls to the wall speed is important.

When it comes to speed/performance
depending on the specific function, using a global can even be faster
than passing down a local/automatic and returning a value.
Normally this would be quite efficient on the AVR as the values would remain in registers;
however, if the function has many local variables,
it may have save & restore those locals before calling the subfunction and then the called
function may also have to save & restore registers as well. Whereas using a global may
potentially avoid that scenario.

Pointers and array indexing is can create significant overhead on the AVR
vs using a global particularly if there are multiple references to the elements.

foo->bar = 1;
foo->bar2 = 2;
foo->bar3 = 3;

will generate more code than using a global:

foo.bar = 1;
foo.bar2 = 2;
foo.bar3 = 3;

which will tend to generate more code than individual globals:

foobar  = 1;
foobar2 = 2;
foobar3 = 3;

All that said, when it comes to performance tuning, profiling is the name of the game.
You must actually profile the code to see where the overheads really are.
Without doing this, it is simply guessing, and that can lead to wasting tons of time
optimizing the wrong areas.

Another thing to keep in mind is that when profiling you must take into
consideration the system as a whole. It does no good to profile individual components
until you know where the biggest overheads are.

i.e. in order to make a difference you must attack the largest overheads.

This may seem obvious, but it is the single biggest mistake I see people make
when trying to optimize code/performance.

There are many ways to profile a system depending on what is desired.
(code size vs speed).
When looking for improvements, the fist things that must
be understood is just where the overhead is.
For code size improvements, you will have to look at the actual assembly code
to see which routines are generating the large areas.

For speed improvements, you will need to profile the live system using
additional tools. A scope and logic analyzer are often quite useful when used
with specially inserted strobing code. This allows you to see the actual timing
of the running code.

Another thing to keep in mind is what I call "the algorithms".
In other words, the actual approach of how the code/system works.
In many cases simply optimizing the way to code works, can yield better
results than optimizing the underlying code itself.
My favorite saying that I have stressed on other developers over the decades
is "better code beats faster code every time".
i.e. if you can do things more efficiently, it often picks up more performance
than pulling out your hair speeding up a less efficient way of doing things.
Again this seems obvious, but it is another thing that is often overlooked.

Sometimes little things can make a big difference.
Like shifting overhead in time. By that I mean, there are often ways of shifting
certain overheads to different places in the code.
For example, sometimes you can pre-calculate things.
If those pre-calculations can be done during initialization, then
that overhead is removed from critical run-time path which
will speed up the normal run-time path of the code.


So my recommendations for optimization is:

  1. Understand where the overheads are
  2. See if there is a different approach that could be better
  3. attack the largest overheads first

These steps work for any type of optimization.
(size or speed)


My observation in this specific case would be that there may be some
unwarranted initial concern or at least concern for the wrong areas.
The system is using i2c. i2c at its default 100khz clock rate is pretty slow.
So my guess ( and I HATE guessing) would be that i2c and how the i2c bus
is handled will be the bottleneck vs using globals, vs locals, etc....
Sometimes, the way you use the i2c bus & wire lib can dramatically affect
performance. i.e. I was able to double the speed of a i2c LCD library
by slightly modifying the way the library talked to the PCF8574 chip.

But my first step would be going back the steps I showed above
and profiling the system to see where the actual overheads are and
then seeing if there is some other way to do things better, and then
finally optimizing code.

I think it was Knuth, that said:
"Make it work. Then make it fast".

--- bill

Thank you -
Mostly you guys are talking over this noob's head, but I do understand.
I did just change 1 function to use pointers instead of return, and you are correct, it totally bogged down the speed in my application. I changed all functions to use globals and didnt see a major difference compared to using returns, but I believe there is a little.

Thanks again for the advice.

Lsnyman,
Here is something you can use to help profile the code for speed
that doesn't need any additional test equipment.
You can use micros() and millis() to time the code.

example:
(make sure to call Serial.begin(BAUDRATE); first up in setup() )

void loop(void)
{
unsigned long start, stop;

	start = micros();
	// Code you want to time goes here
       // you can put code inline here or even function or library API calls.
        stop = micros();
        Serial.print("Time: ");
        Serial.print(stop - start);
        Serial.println("us");
}

If you use millis() instead it will give you the
time in milliseconds vs microseconds.
This will allow you to actually time portions of the code
so you can profile where the actual overheads are
and then home in on biggest contributors.

I'm still guessing some of your biggest overheads will
be with the i2c bus.

--- bill