为什么PHP函数调用*如此*昂贵?

Nik*_*kiC 35 php function

PHP中的函数调用很昂贵.这是一个测试它的小基准:

<?php
const RUNS = 1000000;

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

// with function call
$start = microtime(true);
for ($i = 0; $i < RUNS; ++$i) {
    strlen($string) <= $maxChars;
}
echo 'with function call: ', microtime(true) - $start, "\n";

// without function call
$start = microtime(true);
for ($i = 0; $i < RUNS; ++$i) {
    !isset($string[$maxChars]);
}
echo 'without function call: ', microtime(true) - $start;
Run Code Online (Sandbox Code Playgroud)

这使用函数first(strlen)测试功能相同的代码,然后不使用函数(isset不是函数).

我得到以下输出:

with function call:    4.5108239650726
without function call: 0.84017300605774
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,使用函数调用的实现比不调用任何函数的实现慢了五(5.38)倍.

我想知道为什么函数调用如此昂贵.什么是主要瓶颈?它是哈希表中的查找吗?还是那么慢?


我重新审视了这个问题,并决定再次运行基准测试,XDebug完全禁用(不仅仅是分析已禁用).这表明,我的测试相当复杂,这一次,我得到了10000000次运行:

with function call:    3.152988910675
without function call: 1.4107749462128
Run Code Online (Sandbox Code Playgroud)

这里函数调用只有大约两倍(2.23)慢,所以差异远小于此.


我刚刚在PHP 5.4.0快照上测试了上面的代码,得到了以下结果:

with function call:    2.3795559406281
without function call: 0.90840601921082
Run Code Online (Sandbox Code Playgroud)

这里差异再次略大(2.62).(但总的来说,两种方法的执行时间都显着下降).

Art*_*cto 43

函数调用在PHP中很昂贵,因为有很多东西要完成.

请注意,这isset不是一个函数(它有一个特殊的操作码),所以它更快.

对于像这样的简单程序:

<?php
func("arg1", "arg2");
Run Code Online (Sandbox Code Playgroud)

有六个(每个参数有四个+)操作码:

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                                            

您可以检查操作码的实现zend_vm_def.h.ZEND_在名称前面加上例如for ZEND_INIT_FCALL_BY_NAME和search.

ZEND_DO_FCALL_BY_NAME特别复杂.然后是函数本身的实现,它必须展开堆栈,检查类型,转换zval并可能将它们分开并实际工作......

  • 感谢Artefacto,这对我很有帮助.我将看看这些定义.+1 (2认同)

Gor*_*onM 5

Is the overhead for calling a user function really that big? Or rather is it really that big now? Both PHP and computer hardware have advanced in leaps and bounds in the nearly 7 years since this question was originally asked.

I've written my own benchmarking script below which calls mt_rand() in a loop both directly and via a user-function call:

const LOOPS = 10000000;

function myFunc ($a, $b)
{
    return mt_rand ($a, $b);
}

// Call mt_rand, simply to ensure that any costs for setting it up on first call are already accounted for
mt_rand (0, 1000000);

$start = microtime (true);
for ($x = LOOPS; $x > 0; $x--)
{
    mt_rand (0, 1000000);
}
echo "Inline calling mt_rand() took " . (microtime(true) - $start) . " second(s)\n";

$start = microtime (true);
for ($x = LOOPS; $x > 0; $x--)
{
    myFunc (0, 1000000);
}
echo "Calling a user function took " . (microtime(true) - $start) . " second(s)\n";
Run Code Online (Sandbox Code Playgroud)

Results on PHP 7 on a 2016 vintage i5 based desktop (More specifically, Intel® Core™ i5-6500 CPU @ 3.20GHz × 4) are as follows:

Inline calling mt_rand() took 3.5181620121002 second(s) Calling a user function took 7.2354700565338 second(s)

The overhead of calling a user function appears to roughly double the runtime. But it took 10 million iterations for it to become particularly noticeable. This means that in most cases the differences between inline code and a user function are likely to be negligible. You should only really worry about that kind of optimisation in the innermost loops of your program, and even then only if benchmarking demonstrate a clear performance problem there. Anything else would be a that would yield little to no meaningful performance benefit for added complexity in the source code.

If your PHP script is slow then the odds are almost certainly that it's going to be down to I/O or poor choice of algorithm rather than function call overhead. Connecting to a database, doing a CURL request, writing to a file or even just echoing to stdout are all orders of magnitude more expensive than calling a user function. If you don't believe me, have mt_rand and myfunc echo their output and see how much slower the script runs!

In most cases the best way to optimise a PHP script is to minimise the amount of I/O it has to do (only select what you need in DB queries rather than relying on PHP to filter out unwanted rows, for example), or get it to cache I/O operations though something such as memcache to reduce the cost of I/O to files, databases, remote sites, etc