What is the cost of calling many functions?

Asked

Viewed 302 times

10

Recently, faced with a discussion about Clean Code and programming best practices, a co-worker commented that in his previous job he had a lot of resistance on the part of the other programmers to start dividing the code and make it more readable.

The main reason for resistance has always been that many function calls would stack a lot of data in the stack and consequently lose a lot of performance.

Personally, I don’t see how a function call can generate such a negative impact, but I say this based on an experience mostly of high-level languages like Java, C# and PHP. Considering the use of a low-level language (where I can actually manage memory) such as C or C++:

What is the cost of calling 10 more functions?

How can I calculate the amount of memory spent on this operation?

Detail: In my view, the difference in performance is extremely negligible, and therefore I am merely interested in knowing how to calculate the cost of calls.

  • 2

    Idiot definition: Person who uses performance/performance as a lame excuse for not following good programming practices and writing rogue code.

  • 2

    Ah, and those people who don’t follow good practices by insisting on hitting the performance/performance key invariably don’t have the slightest idea of what the process should be to measure what they stand for.

  • @Victorstafusa, I agree with you. Performance is something important, but it should not prevent you from writing a minimally legible code.

  • I gave +1 in all the answers, because they were all satisfactory. I marked Bigown’s as the correct one, because it seemed to me to be the most complete

  • Performance is something relative, you have to look closer, without a scenario, you can’t tell if it makes a difference or not. 10ms in a google indexing routine is one thing, 10ms in a book register that has 10 users is another. Generally, this discussion of valley or not to do is done in legacy code and the guys are not very keen to refatorar (have work). But, there are many situations where code written following 'patterns' will actually be slower than code that only has a main :P (generalizing). In your case, memory management seems to be the most glaring.

3 answers

12


Performance

The cost of calling function is small or large depending on how you look. In general the cost is to save some registers in memory (most likely will be in the L1 cache which is very fast) and then recover the original state of the registers before the end of it.

In addition there may be copies of data because of parameter passing. If this would not occur if it were not a function, it depends on the situation, some cases the copy is mandatory even without the function.

Under certain circumstances the compiler can linearize the function, that is, instead of calling her (call) it copies the function code to the place you were calling. You will achieve readability and performance.

If the compiler does not do this it is because it cannot or does not compensate to do the inline of the function (ok, there are situations that the human knows can or compensates, and the compiler does not).

In the question linked above I speak of it. If the function is not lowercase and run for a very short time does not compensate. Nor does it pay to optimize what will be called a few times, but this is not always possible for the compiler.

The programmer may know until something is called many times but actually in a lot of place there is natural waiting, so performance does not matter.

It is true that on devices with reduced capabilities it can be useful to do a little more optimization. Optimization can be much worse in these cases, because if you gain performance you lose memory and little memory greatly affects performance. And one of the scarce features is battery. Faster code is usually more economical code.

In the past, when computers were less powerful, this was far more important.

Performance is negligible in most cases and where it is not, the compiler helps you. Of course there may be case that the performance is bad, but the most likely is that the algorithm is bad and not the call of the function that is disturbing.

There are cases in which the linearization of the function can cause extra costs and what seemed a certain performance gain, the opposite is true. And as the difference is very small, the programmer "smart guy" who did what he thought would give better performance ends up not even realizing that he did the worst, although this worst one also will not disturb much.

Readability

I will never say forever prefer to reproduce the code than to call it, but rarely is this necessary and should only do so after verifying that the performance is not expected (has to measure). The cost of optimizing may not compensate for the gain. Because maintenance becomes more difficult. You break the DRY.

Of course also between the readable with less performance and the readable with more performance I prefer the more performance. But copying the code to avoid the call will hardly be more readable than calling it.

Languages

If one uses PHP, mainly, or Java or C# it does not want the maximum performance. These languages can be fast, they can benefit from this optimization, but it’s not so common to need it. When you need a lot of performance in general, C or C++ is preferred. In these languages it makes but sense to eliminate function calls, but it depends on the application, depends on the point of the application.

Memoriam

The question talks about memory consumption as well. The function will use more memory to save the registers and it is possible that it will also spend a little more because of the copy of parameters, but not always. If the code was done right it doesn’t matter, because the memory used is from stack which is already allocated, using or not.

Profilling

You need a profiler to check how the code is running and indicate the costs. Valgrind is the best known for Linux and for Windows should probably be using Visual Studio which has a profiler.

There are other ways, but this is the most accurate and correct.

7

Significant performance differences because of the size of the stack would only make sense in a few cases. The ones I can name who are realistic are those:

  1. Intensive use of recursion at deep levels.

  2. Code that runs on devices with very little available memory and very low computational power.

  3. Calls to functions within loops that perform intense computations.

There are scenarios that combine more than one of these cases above, but the greater probability is that you are not falling in any of these cases, and therefore staying doing code micro-optimizations without having a solid knowledge base based on concrete evidence turns out to be stupidity. Often these micro-optimizations end up is surtindo the opposite effect, IE, leave the code slower.

Also, C++ compilers are very smart, very smart. Modern C++ compilers have been developed by the collaboration and hard work of thousands of extremely competent and intelligent people in what they did and they are able to see opportunities and possibilities for optimizations far beyond that of most programmers experienced in C++ and Assembler would be able. There are a lot of optimizations that apply to these cases and related cases, such as inlining, allocating registers for most used variables, loop unwinding, tail recursion, caches, and a lot of other things.

Even in the above 3 scenarios, if you fall into cases 1 and 3, the problem is probably not the use of functions but how they are organized.

If you fall back on case 2, you probably won’t be thinking about optimizations related to stack size, but rather about squeezing as many bits as possible in the smallest possible space by looking at the hardware level project, considering things like positioning transistors on a microchip, electricity consumption, heat generation, etc.

Again, your case is probably none of those.

And to be sure, if the conversation actually comes to performance/performance as a crutch or lame excuse to move or not move the code, it’s important to take measurements. Estimating performance and performing optimizations is something that requires evidence, evidence and experiments performed with methodological rigor, ie is science. Going simply into the achism of disobeying good practices because that would supposedly produce a performance is fallacious and reckless.

In addition, most of the performance problems that exist in the real world (and not the imaginary problems invented by those who do not want to follow good programming practices) have nothing to do with whether or not you are performing function calls or procedures. These are problems that usually involve designing algorithms, organizing data in memory or disk, threads, caching of information, distributed systems, synchronous vs asynchronous processes, and other things that refer to the architecture of the project, and not small details regarding the size of the stack when making 10 calls to a function.

5

I suggest doing looping tests measuring the total time of at least 1 million iterations.

Even in high-level languages, as mentioned, such as Java, in which I have reasonable experience, the total call time without I/O operations should not come close to 1 second.

About stacking a lot of data in the stack, with the current computational power this is irrelevant. I have already developed in languages where the concern with the size of recorded data records was very great, since in the past the storage offerings were much lower. But to this day, had never seen anyone pay attention to the cost of calling functions to the detriment of the quality of the code and, mainly, ease of maintenance and evolution.

Browser other questions tagged

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