PHP 性能分析魔法方法

曾經(jīng)記得鳥哥Laruence提過不建議使用”魔術(shù)方法”曙砂,自此之后一旦涉及使用魔術(shù)方法的地方,博主都會下意識的想一下丹擎,這樣寫真的好嗎姚垃?由于這一到兩年來一直忙于工作和學(xué)習(xí)新的知識,所以在這道坎兒上一直沒有做深入的探索一直恍恍惚惚過去了坏怪,今年是博主進(jìn)行深入學(xué)習(xí)的一年贝润,因此現(xiàn)在必須對這個問題做個了結(jié)了。我們先來看看鳥哥Laruence博客曾經(jīng)提到的:

優(yōu)化的建議, 是建議, 是防止大家濫用, 肆無忌憚的用. 如果你能在寫代碼的時候, 能意識到, 什么慢, 什么快, 從而避免一些沒有必要的對魔術(shù)方法的調(diào)用, 那就是這個優(yōu)化建議所追求的效果了

疑惑

魔術(shù)方法真的性能比較差嗎铝宵?
PHP7里使用魔術(shù)方法的性能還是存在問題嗎打掘?
我們應(yīng)該如何合理的使用魔術(shù)方法?

方案

面對我的疑惑鹏秋,我的方案是:

統(tǒng)計(jì)對比使用魔術(shù)方法和不使用魔術(shù)方法腳本執(zhí)行的時間差異
PHP5.6.26-1 下連續(xù)執(zhí)行腳本n次
統(tǒng)計(jì)執(zhí)行時間的平均值/最小值/最大值
PHP7.0.12-2 下連續(xù)執(zhí)行腳本n次
統(tǒng)計(jì)執(zhí)行時間的平均值/最小值/最大值
目前個人能力有限尊蚁,只能通過這種方式,如果你有更好的方案或者建議可以告訴我侣夷,謝謝横朋,haha~

測試

__construct

首先我們先來看看構(gòu)造函數(shù)__construct的實(shí)驗(yàn),php腳本如下:

<?php
/**
 * 魔術(shù)方法性能探索
 *
 * 構(gòu)造函數(shù)
 *
 * @author TIGERB <https://github.com/TIGERB>
 */

require('./function.php');
if (!isset($argv[1])) {
    die('error: variable is_use_magic is empty');
}
$is_use_magic = $argv[1];

/**
 * 構(gòu)造函數(shù)使用類名
 */
class ClassOne
{
    public function classOne()
    {
        # code...
    }
}

/**
 * 構(gòu)造函數(shù)使用魔術(shù)函數(shù)__construct
 */
class ClassTwo
{
    public function __construct()
    {
        # code...
    }
}

$a = getmicrotime();
if ($is_use_magic === 'no_magic') {
    new ClassOne();
}else {
    new ClassTwo();
}
$b = getmicrotime();

echo  ($b-$a) . "\n";
PHP5.6不使用魔術(shù)方法數(shù)據(jù)如下百拓,單位微秒μm
// PHP5.6中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php5 construct

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__construct_no_magic_php5.log 10000

// 結(jié)果
avg: 34μm
max: 483μm
min: 26μm

PHP5.6使用魔術(shù)方法數(shù)據(jù)如下琴锭,單位微秒μm
// PHP5.6中連續(xù)調(diào)用腳本10000次

sh test 10000 magic php5 construct

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__construct_magic_php5.log 10000

// 結(jié)果
avg: 28μm
max: 896μm
min: 20μm

PHP7.0不使用魔術(shù)方法數(shù)據(jù)如下,單位微秒μm

// PHP7.0中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php construct

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__construct_no_magic_php.log 10000

// 結(jié)果
avg: 19μm
max: 819μm
min: 13μm

PHP7.0使用魔術(shù)方法數(shù)據(jù)如下衙传,單位微秒μm
// PHP7.0中連續(xù)調(diào)用腳本10000次

sh test 10000 magic php construct

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__construct_magic_php.log 10000

// 結(jié)果
avg: 14μm
max: 157μm
min: 10μm

通過上面的數(shù)據(jù)我們可以看出:

使用__construct作為構(gòu)造函數(shù)的腳本執(zhí)行的平均時間是要快于使用類名作為構(gòu)造函數(shù)的决帖, 大概快5到6微秒 ,不論是在php5.6還是php7.0中粪牲。

__call

接著古瓤,我們來看看__call的實(shí)驗(yàn),php腳本如下:

<?php
/**
 * 魔術(shù)方法性能探索
 *
 * 構(gòu)造函數(shù)
 *
 * @author TIGERB <https://github.com/TIGERB>
 */

require('./function.php');
if (!isset($argv[1])) {
    die('error: variable is_use_magic is empty');
}
$is_use_magic = $argv[1];

/**
 * 構(gòu)造函數(shù)使用類名
 */
class ClassOne
{
    public function __construct()
    {
        # code...
    }

    public function test()
    {
        # code...
    }
}

/**
 * 構(gòu)造函數(shù)使用魔術(shù)函數(shù)__construct
 */
class ClassTwo
{
    public function __construct()
    {
        # code...
    }

    public function __call($method, $argus)
    {
        # code...
    }
}

$a = getmicrotime();
if ($is_use_magic === 'no_magic') {
    $instance = new ClassOne();
    $instance->test();
}else {
    $instance = new ClassTwo();
    $instance->test();
}
$b = getmicrotime();

echo  ($b-$a) . "\n";
PHP5.6不使用魔術(shù)方法數(shù)據(jù)如下腺阳,單位微秒μm
// PHP5.6中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php5 call

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__call_no_magic_php5.log 10000

// 結(jié)果
avg: 27μm
max: 206μm
min: 20μm

PHP5.6使用魔術(shù)方法數(shù)據(jù)如下落君,單位微秒μm

// PHP5.6中連續(xù)調(diào)用腳本10000次
sh test 10000 magic php5 call

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__call_magic_php5.log 10000

// 結(jié)果
avg: 29μm
max: 392μm
min: 22μm

PHP7.0不使用魔術(shù)方法數(shù)據(jù)如下,單位微秒μm

// PHP7.0中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php call

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__call_no_magic_php.log 10000

// 結(jié)果
avg: 16μm
max: 256μm
min: 10μm

PHP7.0使用魔術(shù)方法數(shù)據(jù)如下亭引,單位微秒μm

// PHP7.0中連續(xù)調(diào)用腳本10000次
sh test 10000 magic php call

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__call_magic_php.log 10000

// 結(jié)果
avg: 18μm
max: 2459μm
min: 11μm

通過上面的數(shù)據(jù)我們可以看出:

使用__call的腳本執(zhí)行的平均時間是要慢于不使用绎速, 大概慢2微秒 ,不論是在php5.6還是php7.0中焙蚓。

__callStatic

接著纹冤,我們來看看__callStatic的實(shí)驗(yàn),php腳本如下:

<?php
/**
 * 魔術(shù)方法性能探索
 *
 * 靜態(tài)重載函數(shù)
 *
 * @author TIGERB <https://github.com/TIGERB>
 */

require('./function.php');
if (!isset($argv[1])) {
    die('error: variable is_use_magic is empty');
}
$is_use_magic = $argv[1];

/**
 * 存在test靜態(tài)方法
 */
class ClassOne
{
    public function __construct()
    {
        # code...
    }

    public static function test()
    {
        # code...
    }
}

/**
 * 使用重載實(shí)現(xiàn)test
 */
class ClassTwo
{
    public function __construct()
    {
        # code...
    }

    public static function __callStatic($method, $argus)
    {
        # code...
    }
}

$a = getmicrotime();
if ($is_use_magic === 'no_magic') {
    ClassOne::test();
}else {
    ClassTwo::test();
}
$b = getmicrotime();

echo  ($b-$a) . "\n";
PHP5.6不使用魔術(shù)方法數(shù)據(jù)如下购公,單位微秒μm
// PHP5.6中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php5 callStatic

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__callStatic_no_magic_php5.log 10000

// 結(jié)果
avg: 25μm
max: 129μm
min: 19μm

PHP5.6使用魔術(shù)方法數(shù)據(jù)如下萌京,單位微秒μm

// PHP5.6中連續(xù)調(diào)用腳本10000次
sh test 10000 magic php5 callStatic

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__callStatic_magic_php5.log 10000

// 結(jié)果
avg: 28μm
max: 580μm
min: 20μm

PHP7.0不使用魔術(shù)方法數(shù)據(jù)如下,單位微秒μm

// PHP7.0中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php callStatic

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__callStatic_no_magic_php.log 10000

// 結(jié)果
avg: 14μm
max: 130μm
min: 9μm

PHP7.0使用魔術(shù)方法數(shù)據(jù)如下宏浩,單位微秒μm

// PHP7.0中連續(xù)調(diào)用腳本10000次
sh test 10000 magic php callStatic

// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__callStatic_magic_php.log 10000

// 結(jié)果
avg: 14μm
max: 159μm
min: 10μm

通過上面的數(shù)據(jù)我們可以看出:

在php5.6中使用__callStatic的腳本執(zhí)行的平均時間是要慢于不使用知残, 大概慢3微秒 ;在php7.0中使用__callStatic的腳本執(zhí)行的平均時間是要大致等于不使用__callStatic的;

__set

接著比庄,我們來看看__set的實(shí)驗(yàn)求妹,php腳本如下:

<?php
/**
 * 魔術(shù)方法性能探索
 *
 * 設(shè)置私有屬性__set
 *
 * @author TIGERB <https://github.com/TIGERB>
 */

require('./function.php');
if (!isset($argv[1])) {
    die('error: variable is_use_magic is empty');
}
$is_use_magic = $argv[1];

/**
 * 實(shí)現(xiàn)公共方法設(shè)置私有屬性
 */
class ClassOne
{
    /**
     * 私有屬性
     *
     * @var string
     */
    private $someVariable = 'private';

    public function __construct()
    {
        # code...
    }

    public function setSomeVariable($value = '')
    {
        $this->someVariable = $value;
    }
}

/**
 * 使用_set設(shè)置私有屬性
 */
class ClassTwo
{
    /**
     * 私有屬性
     *
     * @var string
     */
    private $someVariable = 'private';

    public function __construct()
    {
        # code...
    }

    public function __set($name = '', $value = '')
    {
        $this->$name = $value;
    }
}

$a = getmicrotime();
if ($is_use_magic === 'no_magic') {
    $instance = new ClassOne();
    $instance->setSomeVariable('public');
}else {
    $instance = new ClassTwo();
    $instance->someVariable = 'public';
}
$b = getmicrotime();

echo  ($b-$a) . "\n";
PHP5.6不使用魔術(shù)方法數(shù)據(jù)如下乏盐,單位微秒μm
// PHP5.6中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php5 set
// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__set_no_magic_php5.log 10000

// 結(jié)果
avg: 31μm
max: 110μm
min: 24μm

PHP5.6使用魔術(shù)方法數(shù)據(jù)如下,單位微秒μm

// PHP5.6中連續(xù)調(diào)用腳本10000次
sh test 10000 magic php5 set
// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__set_magic_php5.log 10000

// 結(jié)果
avg: 33μm
max: 138μm
min: 25μm
PHP7.0不使用魔術(shù)方法數(shù)據(jù)如下制恍,單位微秒μm
// PHP7.0中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php set
// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__set_no_magic_php.log 10000

// 結(jié)果
avg: 15μm
max: 441μm
min: 11μm
PHP7.0使用魔術(shù)方法數(shù)據(jù)如下父能,單位微秒μm
// PHP7.0中連續(xù)調(diào)用腳本10000次
sh test 10000 magic php set
// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__set_magic_php.log 10000

// 結(jié)果
avg: 17μm
max: 120μm
min: 11μm

通過上面的數(shù)據(jù)我們可以看出:

使用__set的腳本執(zhí)行的平均時間是要慢于不使用, 大概慢2微秒 净神,不論是在php5.6還是php7.0中何吝。

__get

接著,我們來看看__get的實(shí)驗(yàn)强挫,php腳本如下:

<?php
/**
 * 魔術(shù)方法性能探索
 *
 * 讀取私有屬性__get
 *
 * @author TIGERB <https://github.com/TIGERB>
 */

require('./function.php');
if (!isset($argv[1])) {
    die('error: variable is_use_magic is empty');
}
$is_use_magic = $argv[1];

/**
 * 實(shí)現(xiàn)公共方法獲取私有屬性
 */
class ClassOne
{
    /**
     * 私有屬性
     *
     * @var string
     */
    private $someVariable = 'private';

    public function __construct()
    {
        # code...
    }

    public function getSomeVariable()
    {
        return $this->someVariable;
    }
}

/**
 * 使用_get獲取私有屬性
 */
class ClassTwo
{
    /**
     * 私有屬性
     *
     * @var string
     */
    private $someVariable = 'private';

    public function __construct()
    {
        # code...
    }

    public function __get($name = '')
    {
        return $this->$name;
    }
}

$a = getmicrotime();
if ($is_use_magic === 'no_magic') {
    $instance = new ClassOne();
    $instance->getSomeVariable();
}else {
    $instance = new ClassTwo();
    $instance->someVariable;
}
$b = getmicrotime();

echo  ($b-$a) . "\n";
PHP5.6不使用魔術(shù)方法數(shù)據(jù)如下岔霸,單位微秒μm
// PHP5.6中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php5 get
// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__get_no_magic_php5.log 10000

// 結(jié)果
avg: 28μm
max: 590μm
min: 20μm

PHP5.6使用魔術(shù)方法數(shù)據(jù)如下薛躬,單位微秒μm

// PHP5.6中連續(xù)調(diào)用腳本10000次
sh test 10000 magic php5 get
// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__get_magic_php5.log 10000

// 結(jié)果
avg: 28μm
max: 211μm
min: 22μm

PHP7.0不使用魔術(shù)方法數(shù)據(jù)如下俯渤,單位微秒μm

// PHP7.0中連續(xù)調(diào)用腳本10000次
sh test 10000 no_magic php get
// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__get_no_magic_php.log 10000

// 結(jié)果
avg: 16μm
max: 295μm
min: 10μm

PHP7.0使用魔術(shù)方法數(shù)據(jù)如下,單位微秒μm

// PHP7.0中連續(xù)調(diào)用腳本10000次
sh test 10000 magic php get
// 運(yùn)行數(shù)據(jù)統(tǒng)計(jì)腳本
sh analysis ./logs/__get_magic_php.log 10000

// 結(jié)果
avg: 19μm
max: 525μm
min: 12μm

通過上面的數(shù)據(jù)我們可以看出:

在php5.6中使用__get的腳本執(zhí)行的平均時間是要大致等于不使用__get的;在php7.0中使用__get的腳本執(zhí)行的平均時間是要慢于不使用型宝, 大概慢3微秒 八匠。

結(jié)語

這里主要測試了__construct(), __call()趴酣, __callStatic()梨树, __get(), __set()這五個常用的且可有其他實(shí)現(xiàn)方式代替的魔法函數(shù)岖寞。通過上面的測試再回來解答我的疑惑

魔術(shù)方法真的性能比較差嗎抡四?

答:除了使用__construct之外,這里使用其他的魔法方法的時間大致慢10微妙以內(nèi)仗谆。

PHP7里使用魔術(shù)方法的性能還是存在問題嗎指巡?

答:在PHP7中使用與不使用魔術(shù)方法之間的差異和在PHP5.6中近乎一致。

我們應(yīng)該如何合理的使用魔術(shù)方法隶垮?

答:通過整個測試我們可以看出使不使用魔法方法這之間的執(zhí)行時間差異大致都是在10微妙以內(nèi)的藻雪,所以如果魔法方法可以很好的節(jié)省我們的開發(fā)成本和優(yōu)化我們的代碼結(jié)構(gòu),我們應(yīng)該可以考慮犧牲掉這不到10微妙狸吞。而__construct是要快的勉耀,所以使用__construct應(yīng)該沒什么異議。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蹋偏,一起剝皮案震驚了整個濱河市便斥,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌威始,老刑警劉巖枢纠,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異字逗,居然都是意外死亡京郑,警方通過查閱死者的電腦和手機(jī)宅广,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來些举,“玉大人跟狱,你說我怎么就攤上這事』海” “怎么了驶臊?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長叼丑。 經(jīng)常有香客問我误墓,道長旺罢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮妨蛹,結(jié)果婚禮上卤恳,老公的妹妹穿的比我還像新娘秸歧。我一直安慰自己灾锯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布绰垂。 她就那樣靜靜地躺著室奏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪劲装。 梳的紋絲不亂的頭發(fā)上胧沫,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天,我揣著相機(jī)與錄音占业,去河邊找鬼绒怨。 笑死,一個胖子當(dāng)著我的面吹牛纺酸,可吹牛的內(nèi)容都是我干的窖逗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼餐蔬,長吁一口氣:“原來是場噩夢啊……” “哼碎紊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起樊诺,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤仗考,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后词爬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秃嗜,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了锅锨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叽赊。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖必搞,靈堂內(nèi)的尸體忽然破棺而出必指,到底是詐尸還是另有隱情,我是刑警寧澤恕洲,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布塔橡,位于F島的核電站,受9級特大地震影響霜第,放射性物質(zhì)發(fā)生泄漏葛家。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一泌类、第九天 我趴在偏房一處隱蔽的房頂上張望癞谒。 院中可真熱鬧,春花似錦末誓、人聲如沸扯俱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至殊校,卻和暖如春晴玖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背为流。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工呕屎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人敬察。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓秀睛,卻偏偏與公主長得像,于是被迫代替她去往敵國和親莲祸。 傳聞我的和親對象是個殘疾皇子蹂安,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容