10

Extracting functionality into methods or functions is a must for code modularity, readability and interoperability, especially in OOP.

But this means more functions calls will be made.

How does splitting our code into methods or functions actually impact performance in modern* languages?

*The most popular ones: C, Java, C++, C#, Python, JavaScript, Ruby...

  • 8
  • 1
    Every language implementation worth its salt has been doing inlining for several decades now, I think. IOW, the overhead is precisely 0. – Jörg W Mittag May 10 '16 at 12:27
  • 1
    "more function calls will be made" is often not true since many of those calls will have their overhead optimized away by the various compilers/interpreters processing your code and inlining stuff. If your language doesn't have these kinds of optimizations, I might not consider it modern. – Ixrec May 10 '16 at 12:27
  • 2
    How will it affect performance? It will either make it faster, or slower, or not change it, depending on which specific language you use and what is the structure of the actual code and possibly on what version of the compiler you're using and maybe even what platform you're running on. Every answer you get is going to be some variation of this uncertainty, with more words and more supporting evidence. – GrandOpener May 10 '16 at 13:07
  • 1
    The impact, if any, is so small that you, a person, will never ever notice it. There are other far more important things to worry about. Like the whether tabs should be 5 or 7 spaces. – MetaFight May 10 '16 at 14:28
20

Maybe. The compiler might decide "hey, this function is only called a few times, and I'm supposed to optimize for speed, so I'll just inline this function". Essentially, the compiler will replace the function call with the body of the function. For example, the source code would look like this.

void DoSomething()
{
   a = a + 1;
   DoSomethingElse(a);
}

void DoSomethingElse(int a)
{
   b = a + 3;
}

The compiler decides to inline DoSomethingElse, and the code becomes

void DoSomething()
{
   a = a + 1;
   b = a + 3;
}

When functions are not inlined, yes there is a performance hit to make a function call. However, it's such a minuscule hit that only extremely high performance code is going to worry about function calls. And on those kinds of projects, the code is typically written in assembly.

Function calls (depending on the platform) typically involve a few 10s of instructions, and that's including saving / restoring the stack. Some function calls consist a jump and return instruction.

But there's other things that might impact function call performance. The function being called may not be loaded into the processor's cache, causing a cache miss and forcing the memory controller to grab the function from main RAM. This can cause a big hit for performance.

In a nutshell: function calls may or may not impact performance. The only way to tell is to profile your code. Don't try to guess where the slow code spots are, because the compiler and hardware have some incredible tricks up their sleeves. Profile the code to get the location of the slow spots.

  • 1
    I have seen with modern compilers (gcc, clang) in situations where I really cared that they created quite bad code for loops inside a large function. Extracting the loop into a static function didn't help because of inlining. Extracting the loop into an external function created in some cases significant (measurable in benchmarks) speed improvements. – gnasher729 May 10 '16 at 12:48
  • 1
    I would piggy-back on this and say OP should be careful about Premature Optimization – Patrick May 10 '16 at 13:36
  • 1
    @Patrick Bingo. If you you're going to optimize, use a profiler to see where the slow sections are. Do not guess. You can usually get a feel for where the slow sections might be, but confirm it with a profiler. – CHendrix May 10 '16 at 13:42
  • @gnasher729 To solve that particular issue, one will need more than a profiler - one will have to learn to read the disassembled machine code as well. While there is premature optimization, there is no such thing as premature learning (at least in software development). – rwong May 10 '16 at 19:50
  • You might have this problem if you are calling a function a million times, but you are more likely to have other problems which are having a significantly bigger impact. – Michael Shaw May 10 '16 at 20:21
5

This is a matter of implementation of the compiler or runtime (and its options) and cannot be said with any certainty.

Within C and C++, some compilers will inline calls based on optimization settings - this can be trivially seen by examining the generated assembly when looking at tools such as https://gcc.godbolt.org/

Other languages, such as Java have this as part of the runtime. This is part of the JIT and elaborated on in this SO question. In paticular look at the JVM options for HotSpot

-XX:InlineSmallCode=n Inline a previously compiled method only if its generated native code size is less than this. The default value varies with the platform on which the JVM is running.
-XX:MaxInlineSize=35 Maximum bytecode size of a method to be inlined.
-XX:FreqInlineSize=n Maximum bytecode size of a frequently executed method to be inlined. The default value varies with the platform on which the JVM is running.

So yes, the HotSpot JIT compiler will inline methods that meet certain criteria.

The impact of this, is hard to determine as each JVM (or compiler) may do things differently and trying to answer with the broad stroke of a language is almost certainty wrong. The impact can only be properly determined by profiling the code in the appropriate running environment and examining the compiled output.

This can be seen as a misguided approach with CPython not inlining, but Jython (Python running in the JVM) having some calls inlined. Likewise MRI Ruby not inlining while JRuby would, and ruby2c which is a transpiler for ruby into C... which could then be inlining or not depending on the C compiler options that was compiled with.

Languages don't inline. Implementations may.

5

You're looking for performance in the wrong place. The problem with function calls is not that they cost much. There is another problem. Function calls could be absolutely free, and you would still have this other problem.

It is that a function is like a credit card. Since you can easily use it, you tend to use it more than maybe you should. Suppose you call it 20% more than you need to. Then, typical large software contains several layers, each calling functions in the layer below, so the factor of 1.2 can get compounded by the number of layers. (For example, if there are five layers, and each layer has a slowdown factor of 1.2, the compounded slowdown factor is 1.2^5 or 2.5.) This is just one way to think about it.

This does not mean you should avoid function calls. What it means is, when the code is up and running, you should know how to find and eliminate the waste. There is much excellent advice on how to do this on stackexchange sites. This gives one of my contributions.

ADDED: Small example. Once I worked in a team on factory-floor software that tracked a series of work orders or "jobs". There was a function JobDone(idJob) that could tell if a job was done. A job was done when all its sub-tasks were done, and each of those was done when all its sub-operations were done. All these things were kept track of in a relational database. A single call to another function could extract all that information, so JobDone called that other function, saw if the job was done, and threw the rest away. Then people could easily write code like this:

while(!JobDone(idJob)){
    ...
}

or

foreach(idJob in jobs){
    if (JobDone(idJob)){
        ...
    }
}

See the point? The function was so "powerful" and easy to call that it got called way too much. So the performance problem was not the instructions going in and out of the function. It was that there needed to be a more direct way to tell if jobs were done. Again, this code could have been embedded in thousands of lines of otherwise innocent code. Trying to fix it in advance is what everyone tries to do, but that's like trying to throw darts in a dark room. What you need instead is to get it running, and then let the "slow code" tell you what it is, simply by taking time. For that I use random pausing.

1

I think it really depends on the language and on the function. While the c and c++ compilers can inline a lot of functions, this is not the case for Python or Java.

While I do not know the specific details for java (except that every method is virtual but I suggest you to check better the documentation), in Python I am sure that there is no inlining, no tail recursion optimization and function calls are quite expensive.

Python functions are basically executable objects (and infact you can also define the call() method to make an object instance a function). This means that there is quite a lot of overhead in calling them...

BUT

when you define variables inside functions, the interpreter uses the LOADFAST instead of the normal LOAD instruction in the bytecode, making your code faster...

Another thing is that when you define a callable object, patterns like memoization are possible and they can effectively speed up your computation a lot (at the cost of using more memory). Basically it's always a trade off. Function calls cost also depends on the parameters, because they determine how much stuff you actually have to copy on the stack (thus in c/c++ it's common practice to pass big parameters like structures by pointers/reference instead of by value).

I think that your question is in practice too broad to be answered completely on stackexchange.

What I suggest you to do is to start with one language and study the advanced documentation to understand how function calls are implemented by that specific language.

You wil be surprised by how many things you will learn in this process.

If you have a specific problem, make measurements/profiling and decide weather it's better to create a function or to copy/paste the equivalent code.

if you ask a more specific question, it would be easier to get a more specific answer, I think.

  • Quoting you: "I think that your question is in practice too broad to be answered completely on stackexchange." How can I narrow it down then? I would love to see some actual data representing function call impact in performance. I don't care what language, I am just curious of seeing a more detailed explanation, backed up with data if possible, as I said. – dabadaba May 10 '16 at 14:36
  • The point is that it depends on the language. In C and C++, if the function is inlined, the impact is 0. If not inlined, it depends by its parameters, if it's in the cache or not, etc... – ingframin May 10 '16 at 14:39
1

I measured the overhead of direct and virtual C++ function calls on the Xenon PowerPC some time ago.

The functions in question had a single parameter and a single return, so parameter passing occurred on registers.

Long story short, the overhead of a direct (non-virtual) function call was approximately 5.5 nanoseconds, or 18 clock cycles, compared to an inline function call. The overhead of a virtual function call was 13.2 nanoseconds, or 42 clock cycles, compared to inline.

These timings are likely different on different processor families. My test code is here; you can run the same experiment on your hardware. Use a high-precision timer like rdtsc for your CFastTimer implementation; system time() is not nearly precise enough.

protected by gnat Nov 30 '17 at 10:21

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.