鍍金池/ 問答/PHP/ PHP 的 call_user_func_array 方法是否效率很低?

PHP 的 call_user_func_array 方法是否效率很低?

Laravel 5.1Facade 類 的 __callStatic 方法代碼如下:

public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();

    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }

    switch (count($args)) {
        case 0:
            return $instance->$method();

        case 1:
            return $instance->$method($args[0]);

        case 2:
            return $instance->$method($args[0], $args[1]);

        case 3:
            return $instance->$method($args[0], $args[1], $args[2]);

        case 4:
            return $instance->$method($args[0], $args[1], $args[2], $args[3]);

        default:
            return call_user_func_array([$instance, $method], $args);
    }
}

為什么不直接寫成:

public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();

    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }

    return call_user_func_array([$instance, $method], $args);
}
回答
編輯回答
柒喵

Iterations: 100 000
Averaged over: 10
PHP 5.6.30 (cli) (built: Jan 18 2017 19:47:28)

Overall Average
Invocation Time (s) Delta (s) %
directFunction 0.0089 -0.0211 -70.19
directStatic 0.0098 -0.0202 -67.39
directLambda 0.0109 -0.0191 -63.52
directInstance 0.0116 -0.0184 -61.31
directClosure 0.0150 -0.0150 -50.15
Invoke 0.0282 -0.0018 -6.13
call_user_func 0.0300
ClosureFactory 0.0316 +0.0016 +5.20
assignedClosureFactory 0.0328 +0.0028 +9.28
call_user_func_array 0.0399 +0.0099 +33.02
InvokeCallUserFunc 0.0418 +0.0118 +39.17
directImplementation 0.0475 +0.0175 +58.28

Iterations: 100 000
Averaged over: 10
PHP 7.1.2 (cli) (built: Feb 14 2017 21:24:45)

Overall Average
Invocation Time (s) Delta (s) %
directFunction 0.0043 -0.0096 -68.92
directStatic 0.0050 -0.0089 -64.04
directInstance 0.0058 -0.0081 -58.22
directLambda 0.0063 -0.0075 -54.44
directClosure 0.0081 -0.0058 -41.57
call_user_func 0.0139
call_user_func_array 0.0147 +0.0008 +5.84
Invoke 0.0187 +0.0048 +34.61
ClosureFactory 0.0207 +0.0069 +49.43
assignedClosureFactory 0.0219 +0.0080 +57.75
directImplementation 0.0232 +0.0094 +67.53
InvokeCallUserFunc 0.0264 +0.0126 +90.67
2017年12月5日 07:09
編輯回答
夏夕

三個(gè)...不定參數(shù)的寫法是php 5.6版本才有的新特性,我猜可能框架5.1版本的時(shí)候還沒支持php新特性的吧

2017年2月27日 04:54
編輯回答
孤客

光看題主的問題, php 中的 call_user_func_array() 并不慢, 相反這個(gè)比普通的函數(shù)執(zhí)行更快, 因?yàn)檫@個(gè)在 php7 添加為語言結(jié)構(gòu)(其他語言結(jié)構(gòu), 比如 echo), 這個(gè)信息可以從鳥哥的博客獲取到.

另外 laravel 中 facade 中多這樣一層封裝, 其實(shí)是使用 __callStatic() 函數(shù)實(shí)現(xiàn), 這個(gè)稍微會(huì)有一點(diǎn)性能損失.

2017年8月9日 00:16
編輯回答
孤慣

我剛剛創(chuàng)建了一個(gè)laravel的項(xiàng)目

"laravel/framework": "5.5.*"

然后看了源碼:

/**
 * Handle dynamic, static calls to the object.
 *
 * @param  string  $method
 * @param  array   $args
 * @return mixed
 *
 * @throws \RuntimeException
 */
public static function __callStatic($method, $args)
{
    $instance = static::getFacadeRoot();

    if (! $instance) {
        throw new RuntimeException('A facade root has not been set.');
    }

    return $instance->$method(...$args);
}
2017年9月7日 09:06
編輯回答
青裙

答:call_user_func_array 效率偏低。

基準(zhǔn)測試如下

對(duì)比范圍

  • 直接調(diào)用

  • 變量函數(shù)調(diào)用

  • call_user_func 調(diào)用

  • call_user_func_array 調(diào)用

測試結(jié)果

clipboard.png
我們可以看到,call_user_func_array 所用時(shí)間為:1.1608240604401s

測試過程

測試代碼如下:

<?php
error_reporting(E_ALL | E_STRICT);
define('ITERATIONS', 2000000);

class Bench
{
    private $bench_name;
    private $start_time;
    private $end_time;

    public function start($name)
    {
        $this->bench_name = $name;
        $this->start_time = microtime(true);
    }

    public function end()
    {
        $this->end_time = microtime(true);
        echo "Call style: " . $this->bench_name . '; ' . ($this->end_time - $this->start_time) . " seconds". PHP_EOL;
    }
}

class Test
{
    public function test($a, $b, $c)
    {
        return;
    }
}


$bench = new Bench();
$test = new Test();
$arg = [1, 2, 3];
$func_name = 'test';

$bench->start('normal');
for ($i=0; $i < ITERATIONS; ++$i) {
    $test->test($arg[0], $arg[1], $arg[2]);
}
$bench->end();

$bench->start('var_function');
for ($i=0; $i < ITERATIONS; ++$i) {
    $test->$func_name($arg[0], $arg[1], $arg[2]);
}
$bench->end();

$bench->start('call_user_func');
for ($i=0; $i < ITERATIONS; ++$i) {
    call_user_func([$test, $func_name], $arg[0], $arg[1], $arg[2]);
}
$bench->end();

$bench->start('call_user_func_array');
for ($i=0; $i < ITERATIONS; ++$i) {
    call_user_func_array([$test, $func_name], $arg);
}
$bench->end();
2018年5月16日 08:05
編輯回答
脾氣硬

這問題我也納悶. mark, 看會(huì)不會(huì)遇到能解答這個(gè)問題的人.

2018年5月26日 23:32
編輯回答
抱緊我

題主是不是看錯(cuò)了或者看的是修改過的源碼,原始laravel中并沒有發(fā)現(xiàn)存在這些代碼,能否標(biāo)出具體的laravel版本和文件路徑

我看到的laravel的 Facade 類中代碼是這樣的

/**
     * Handle dynamic, static calls to the object.
     *
     * @param  string  $method
     * @param  array   $args
     * @return mixed
     *
     * @throws \RuntimeException
     */
    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }
2017年7月8日 02:37
編輯回答
心沉

額 題主是什么版本的??? 感覺跟樓上就幾個(gè)同學(xué)看到的一樣啊

2017年9月3日 00:23