tags:

views:

186

answers:

1

A function call in PHP is expensive. Here is a small benchmark to test it:

<?php
    function stringOkay($string, $maxChars) {
        return !isset($string[$maxChars]);
    }

    const RUNS = 1000000;

    // create test string
    $string = str_repeat('a', 1000);
    $maxChars = 500;

    // userland function call
    $start = microtime(true);
    for ($i = 0; $i < RUNS; ++$i) {
        stringOkay($string, $maxChars);
    }
    echo 'userland function call: ', microtime(true) - $start, '<br />';

    // native function call
    $start = microtime(true);
    for ($i = 0; $i < RUNS; ++$i) {
        strlen($string) <= $maxChars;
    }
    echo 'native function call: ', microtime(true) - $start, '<br />';

    // userland implementation
    $start = microtime(true);
    for ($i = 0; $i < RUNS; ++$i) {
        !isset($string[$maxChars]);
    }
    echo 'userland implementation: ', microtime(true) - $start, '<br />';

This tests a functionally identical code as a userland function, an implementation using a native function and an implementation using no functions at all.

I get the following output (yes, my machine is slow):

userland function call:  5.3892569541931
native function call:    4.5108239650726
userland implementation: 0.84017300605774

As you can see the native function call is more than five times slower than the implementation not calling any function. Now, I know these two codes aren't identical. I do understand that strlen needs to create a variable and return it, I understand that it probably does some checking of the passed value before proceeding. So, let us assume that strlen is 30% heavier than isset. Still the difference is big.

Now, I would like to know why a function call is so expensive. What's the main bottleneck? Is it the lookup in the hash table? Or what is so slow?

+3  A: 

Function calls are expensive in PHP because there's lot of stuff being done.

Note that isset is not a function (it has a special opcode for it), so it's faster.

For a simple program like this:

<?php
func("arg1", "arg2");

There are six (four + one for each argument) opcodes:

1      INIT_FCALL_BY_NAME                                       'func', 'func'
2      EXT_FCALL_BEGIN                                          
3      SEND_VAL                                                 'arg1'
4      SEND_VAL                                                 'arg2'
5      DO_FCALL_BY_NAME                              2          
6      EXT_FCALL_END                                            

You can check the implementations of the opcodes in zend_vm_def.h. Prepend ZEND_ to the names, e.g. for ZEND_INIT_FCALL_BY_NAME and search.

ZEND_DO_FCALL_BY_NAME is particularly complicated. Then there's the the implementation of the function itself, which must unwind the stack, check the types, convert the zvals and possibly separate them and to the actual work...

Artefacto
Thanks Artefacto, this really helps me. I will have a look at those definitions. +1
nikic