How Significant Is PHP Function Call Overhead?
Asked Answered
A

4

13

I'm relatively new to PHP and slowly learning the idiosyncrasies specific to the language. One thing I get dinged by a lot is that I (so I'm told) use too many function calls and am generally asked to do things to work around them. Here's two examples:

// Change this:
} catch (Exception $e) {
  print "It seems that error " . $e->getCode() . " occured";
  log("Error: " . $e->getCode());
}

// To this:
} catch (Exception $e) {
  $code = $e->getCode();
  print "It seems that error " . $code . " occured";
  log("Error: " . $code);
}

2nd Example

// Change this:
$customer->setProducts($products);

// To this:
if (!empty($products)) {
  $customer->setProducts($products);
}

In the first example I find that assigning $e->getCode() to $code ads a slight cognitive overhead; "What's '$code'? Ah, it's the code from the exception." Whereas the second example adds cyclomatic complexity. In both examples I find the optimization to come at the cost of readability and maintainability.

Is the performance increase worth it or is this micro optimization?

I should note that we're stuck with PHP 5.2 for right now.

I've done some very rough bench tests and find the function call performance hit to be on the order of 10% to 70% depending on the nature of my bench test. I'll concede that this is significant. But before that catch block is hit there was a call to a database and an HTTP end point. Before $products was set on the $customer there was a complex sort that happened to the $products array. At the end of the day does this optimization justify the cost of making the code harder to read and maintain? Or, although these examples are simplifications, does anybody find the 2nd examples just as easy or easier to read than the first (am I being a wiener)?

Can anyone cite any good articles or studies about this?

Edit:

An example bench test:

<?php
class Foo {
        private $list;
        public function setList($list) {
                $this->list = $list;
        }
}

$foo1 = new Foo();

for($i = 0; $i < 1000000; $i++) {
        $a = array();
        if (!empty($a))
                $foo1->setList($a);
}
?>

Run that file with the time command. On one particular machine it takes an average of 0.60 seconds after several runs. Commenting out the if (!empty($a)) causes it to take an average of 3.00 seconds to run.

Clarification: These are examples. The 1st example demonstrates horrible exception handling and a possible DRY violation at the expense of a simple, non-domain-specific example.

Aleciaaleck answered 31/7, 2013 at 13:35 Comment(6)
10% to 70% overhead by calling what function?Chalfant
IMHO, If this level of optimization is necessary, then PHP is not the right tool in the first place.Marked
In your first example you need to call $e->getCode() twice (adding overhead) but the second part is that you are assigning the result to a variable and using it multiple times. Good coding practice. In the second example you are doing defensive programming again good programming practice.Hargrove
It all depends what the function actually does, and how long that takes to execute: $e->getCode(); is pretty trivial, and smacks of micro-optimisation; but a function that makes a db call to return 1000 rows would be significant if called repeatedly several times in succession. Profiling your code should give you timing figures for function callsPomiferous
@Anigel, I added one of my bench tests to the question.Aleciaaleck
@jeff In most OO languages that would not be considered "calling it twice" (though it could be a DRY violation either way). Assignment would just assign a reference of some sort thereby making assignment or instance->getField() equivalent. Could this be a misunderstanding on my part about how PHP handles OO?Aleciaaleck
L
5

The canonical PHP implementation is very slow because it's easy to implement and the applications PHP aims at do not require raw performance like fast function calls.

You might want to consider other PHP implementations.

If you are writing the applications you should be writing in PHP (dump data from DB to the browser over a network) then the function call overhead is not significant. Certainly don't go out of your way to duplicate code because you were afraid using a function would be too much overhead.

Loosejointed answered 31/7, 2013 at 13:51 Comment(0)
B
10

PHP function call overhead is precisely 15.5355%.

:) Just stirring the pot.

Seriously, here are a couple great links on the subject:

Is it possible to have too many functions in a PHP application?

functions vs repeated code

The code maintainability versus speed discussions at these links address the (maybe more important) question implied by the OP, but just to add a little data that may also be pertinent and hopefully useful to people who come across this thread in the future, here are results from running the below code on a 2011 Macbook Pro (with very little drive space and too many programs running).

As noted elsewhere, an important consideration in deciding whether to call a function or put the code "in-line" is how many times the function will be called from within a certain block of code. The more times the function will be called, the more it's worth considering doing the work in-line.

Results (times in seconds)

Call Function Method | In-Line Method | Difference | Percent Different

1,000 iterations (4 runs)

0.0039088726043701 | 0.0031478404998779 | 0.00076103210449219 | 19.4694

0.0038208961486816 | 0.0025999546051025 | 0.0012209415435791 | 31.9543

0.0030159950256348 | 0.0029480457305908 | 6.7949295043945E-5 | 2.2530

0.0031449794769287 | 0.0031390190124512 | 5.9604644775391E-6 | 0.1895

1,000,000 iterations (4 runs)

3.1843111515045 | 2.6896121501923 | 0.49469900131226 | 15.5355

3.131945848465 | 2.7114839553833 | 0.42046189308167 | 13.4249

3.0256152153015 | 2.7648048400879 | 0.26081037521362 | 8.6201

3.1251409053802 | 2.7397727966309 | 0.38536810874939 | 12.3312

function postgres_friendly_number($dirtyString) {
    
    $cleanString = str_ireplace("(", "-", $dirtyString);
    $badChars = array("$", ",", ")");
    $cleanString = str_ireplace($badChars, "", $cleanString);
    
    return $cleanString;
    
}


//main
$badNumberString = '-$590,832.61';

$iterations = 1000000;

$startTime = microtime(true);
for ($i = 1; $i <= $iterations; $i++) {
    $goodNumberString = postgres_friendly_number($badNumberString);
}
$endTime = microtime(true);
$firstTime = ($endTime - $startTime); 

$startTime = microtime(true);
for ($i = 1; $i <= $iterations; $i++) {
    $goodNumberString = str_ireplace("(", "-", $badNumberString);
    $badChars = array("$", ",", ")");
    $goodNumberString = str_ireplace($badChars, "", $goodNumberString);
}
$endTime = microtime(true); 
$secondTime = ($endTime - $startTime); 

$timeDifference = $firstTime - $secondTime;
$percentDifference = (( $timeDifference / $firstTime ) * 100);
Bellied answered 10/2, 2014 at 18:21 Comment(1)
This is completely useless because it doesn't even mention which version of PHP was used.Canner
L
9

Nobody has yet discussed how the server's hardware is related to function call overhead.

When a function is called, all of the CPU's registers contain data relevant to the current execution point. All of the CPU's registers must be saved to memory (typically the process' stack) or there is no hope of ever returning to that execution point and resuming execution. When returning from the function, all of the CPU's registers must be restored from memory (typically the process' stack).

So, one can see how a string of nested function calls can add overhead to the process. The CPU's registers must be saved over and over again on the stack and restored over and over again to get back from the functions.

This is really the source of the overhead of function calls. And if function arguments are passed, those must all be duplicated before the function can be called. Therefore, passing huge arrays as function arguments is a poor design.

Studies have been done on object-oriented PHP on overhead of use of getters/setters. Removing all getters/setters cut execution time by about 50%. And that simply is due to function call overhead.

Larine answered 18/11, 2014 at 21:40 Comment(3)
Which studies... links?Xenophon
The argument is not valid for PHP. Saving registers on the stack is standard procedure for all languages and pretty fast. But function calls in PHP are much slower than in C++ or even Java. I don't know what the Zend engine is doing exactly, but the main performance hit is not caused by merely saving registers.Cusp
Php stores pointer to an array, not the whole array. No matter how big the array is, the overhead is going to be the same. Also, as was said, this is not C, the main bulk of overhead is not from registers.Posturize
L
5

The canonical PHP implementation is very slow because it's easy to implement and the applications PHP aims at do not require raw performance like fast function calls.

You might want to consider other PHP implementations.

If you are writing the applications you should be writing in PHP (dump data from DB to the browser over a network) then the function call overhead is not significant. Certainly don't go out of your way to duplicate code because you were afraid using a function would be too much overhead.

Loosejointed answered 31/7, 2013 at 13:51 Comment(0)
B
4

I you are confusing terminology. Generally function call overhead means the overhead involved in calling an returning from a function. Rather than processing inline. Its not the the total cost of a function call. Its just the cost preparing the arguments and return values and doing the branch.

The problem you have is that PHP and other weakly typed scripting style languages are really bad at determining if functions have side effects. So rather than store the results of a function as a temp, they will make multiple calls. If the function is doing something complex this will be very inefficient.

So, the bottom line is: call the function once and store and reuse the result! Don't call the same function multiple times with the same arguments. (without a good reason)

Beckiebeckley answered 12/8, 2014 at 1:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.