How to organize code without losing performance?

Asked

Viewed 1,444 times

7

Using functions instead of putting code directly influences a program’s performance ? For example:

void minhafuncao(){
  printf("Funcao");
}
main(){
  minhafuncao();
}

in place of

main(){
  printf("Funcao");
}

I know macros perform better than functions, but so far I’ve only seen macros with arithmetic operations. How to put blocks of code with multiple commands in a macro ? Or, if not, some other way to make it perform better ?

  • 3

    Having the code in a separate function or directly within another function does not influence performance observably.

  • Actually, pmg, depends on the case. Functions that only do, for example, one or two arithmetic operations and that are called loop to heaps can consume a greater and noticeable percentage of time.

2 answers

11


Using functions instead of putting code directly influences a program’s performance ?

No! Function calls, especially in C, are quite fast. There are many other things that will make your program slower than a function call:

latências para diversas operações

As you can see in this picture, a single hard drive access takes an equivalent amount of time to millions of function calls. Access to a remote computer won’t even speak.

And even if function calls were slow, (which they are not) the benefit they offer in terms of abstraction and code organization is very great and it wouldn’t be worth giving it up in the name of "performance". When it comes to optimizing a program, what makes a difference is the performance of the "bottlenecks": if you have an internal loop consuming 90% of your running time, doubling its speed will have a significant effect. On the other hand, if an excerpt of your code only runs a few hundred times, even if you make it instant you won’t save more than a few milliseconds. That’s why, always measure the performance of your program before you start optimising it! It is difficult to predict beforehand which parts of your program are the real bottlenecks and any optimization outside the bottleneck is a waste of time that only serves to make your code more complicated. Not to mention that some optimizations may end up slowing down your program than before. The only way to protect yourself from this is to always measure before and after any optimization you do.

I know macros perform better than functions

Not always! Macros are a manual form of inlining and the excessive use of this technique greatly increases the size of the executable. In addition to wasting memory, this can increase the rate of "cache miss", which is quite detrimental to performance (an access to RAM requires hundreds of cycles).

In addition, the real performance gain of inlining comes from the cost of the function call but from the opportunities that the optimizer now has to specialize the subroutine code for the call location (better allocation of registers, dead code removal, etc.). This doesn’t always end up worthwhile and so I recommend trusting your compiler instead of going inlining at hand.

  • Worse, that sometimes these "bottlenecks" in the code sometimes become necessary when the stretch without such a bottleneck proves to be too complicated a task. Ex: Sometimes it becomes better for understanding to go through an array - with for and an internal if - than to try to find very complicated expressions in for and satisfy the same conditions without going through an array.

  • It’s true, Rafael, and it’s always important to take into account the cost of making the program more complicated. But even worse is doing optimization tricks on something that isn’t even a bottleneck :)

7

At first it influences performance. But it may not influence by a number of factors.

A call to a function, in addition to the instructions of deviation and return of the program flow to another address, may exist the copy of the parameters, either from memory to memory, or through registers. So it costs, of course.

If the function is simple and depending on the options used to compile the program the function will be optimized through the call inline Expansion. That is, if advantageous, the compiler deletes the call from the function and places its code where the call was made. Note that the compiler knows that there are cases that using this technique will produce otherwise.

This does the same thing that the macro would do but much better. At first compilers were not able to do this and macros brought this advantage, despite the disadvantages that this feature has. Today there are no more major reasons for using the macro, only very specific situations require its use in C code and even less in C++ code that has even more better features.

It is possible to make macros more complex than simple arithmetic operations but its use is not recommended today. But as I said before macros do not understand the context where they are being executed, this can cause problems if care is not taken in their creation and/or use.

Example:

#define exemplo() \
  do { \
    faz alguma coisa aqui \
  } while (0)

You have no reason to do that today. Today even in C, most of the time the programmer should be concerned with the readability of the code and not with the performance. The compiler will make the code as fast as possible in the vast majority of situations when the code is well written. It is not necessary to look for the best ways to write thinking primarily about performance. When the performance is not enough, then one should think about what to do to improve that stretch. rarely the macro will be the solution to this case.

In almost 100% of cases the compiler will know if a function should be "linearized" for better performance. If he doesn’t do this it’s because he understands that there will be no win and he hits more than the programmers usually do.

It seems to me that some compilers might force the inline, through flag specific, even when not recommended. Almost always when the programmer wants to outsmart the compiler, he misses.

His example is certain to be "linearized" unless the compiler is instructed not to. But a small change that will still make the function short will probably prevent this from happening:

void minhafuncao(){
  for (int i = 0; i < 100; i++) printf("Funcao");
}

Probably the time spent within the function is large enough for the compiler to understand that there will be no gain. But other factors may be taken into consideration to make the assessment.

I will not go into details of what is done or not because I do not know deeply and depends on implementation, IE, can change in each version or mark of the compiler.

A test can be applied to see if there is a difference or not:

#include <stdio.h>
#include <time.h>

int minhafuncao(){
    int x = 0;
    x++;
    return x;
}

int main () {
    clock_t begin, end;
    double time_spent;
    int x;
    begin = clock();
    for (int i = 0; i < 300000000; i++);
    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf ("Tempo gasto %.2lf segundos.\n", time_spent);
    begin = clock();
    for (int i = 0; i < 300000000; i++) minhafuncao();
    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf ("Tempo gasto %.2lf segundos.\n", time_spent);
    begin = clock();
    for (int i = 0; i < 300000000; i++) x++;
    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf ("Tempo gasto %.2lf segundos.\n", time_spent);
    x++;
}

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

The ideone or other site of the type is not the best environment to take the test but you can have an idea. I recommend taking your test. The result may be different under different conditions.

Another test without returning a value. Note how it changes. See working in the ideone. And in the repl it.. Also put on the Github for future reference.

  • gcc -Wall -ansi -The speed. c -the speed -> On execution: Time spent 0.10 seconds. Time spent 0.10 seconds. Time spent 0.10 seconds. No -O flag (0.7, 0.83, 0.7)

  • 1

    Exactly, that’s what I said, everything will depend on how you compile.

  • In the code why does my trust return a value? Without the return time without -O is also similar.

  • To show that anything can make a difference. That you can’t talk about the speed of hypothetical things. A small detail changes everything. Surely he wants to know whether to use any function or not. And this depends on, even a simple change can produce a different result. And I talk more about this here: http://answall.com/a/56838/101 It is silly to generalize comparison of performance, it can only be done in a very specific way, in real cases.

  • Very good code, I’ve always heard of performance, and that’s succinct in this little code. I will test some flags, other compilers, some statics, inlines etc. Mutio good example!

  • 1

    I don’t even think it’s very good, it just shows how easy it is to see that it even depends on simple things. In complex, the depends gets huge. There are other optimizations that can occur there. You can even have Dead code elimination. What can justify equal times. It is not the case but it may be that one optimization provides another, as a unrolling loop or Constant Folding, just to name a few obvious optimizations.

  • Both answers were very enlightening.

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.