使用vld看php的opcode

之前整理了一篇文章-PHP APC的設定與應用,介紹APC如何安裝、設定、以及為何APC能讓PHP效能更好。APC能提升PHP效能的主因,就是當中所提到的opcode cache。

由於php的執行會先將php code轉成opcode,因此,要瞭解如何寫更有效率的php程式,讓寫出來的php程式效能更好,可以經由opcode來瞭解。Optimize your PHP Code – Tips, Tricks and Techniques這篇文章的說明中提到很多提升php效能的技巧。我們就以opcode來解釋第五點-在for內為何不要使用count()。

何謂opcode?有哪些指令?可以參考官方資料 - PHP: Opcode list - Manual。那…如何看所寫的php code會被轉成怎樣的opcode?

首先,我們需要安裝vld(Vulcan Logic Disassembler)。在FreeBSD下安裝vld很簡單,用port即可
cd /usr/ports/devel/pecl-vld
make install

安裝好後,只需要如下簡單的指令就可以看到opcode
php -q -d vld.active=1 opcodetest.php

下面,我們就使用vld來看count放在for內,php是如何執行的?為了將焦點集中在opcode,for迴圈內就先不放任何程式碼。並且在進入迴圈之前,也故意跑一次count,以便和第二版做比較。
<?php
$myarray = array('apc', 'opcode', 'test');
$total = count($myarray);
for ($counter = 0; $counter < count($myarray); $counter++) {
}

filename:       /apc/opcodetest.php
function name:  (null)
number of ops:  26
compiled vars:  !0 = $myarray, !1 = $total, !2 = $counter
line     # *  op              fetch          ext  return  operands
--------------------------------------------------------------------
   2     0  >   EXT_STMT
         1      INIT_ARRAY                          ~0      'apc'
         2      ADD_ARRAY_ELEMENT                   ~0      'opcode'
         3      ADD_ARRAY_ELEMENT                   ~0      'test'
         4      ASSIGN                                      !0, ~0
   3     5      EXT_STMT
         6      EXT_FCALL_BEGIN
         7      SEND_VAR                                    !0
         8      DO_FCALL                         1          'count'
         9      EXT_FCALL_END
        10      ASSIGN                                      !1, $2
   4    11      EXT_STMT
        12      ASSIGN                                      !2, 0
        13  >   EXT_FCALL_BEGIN
        14      SEND_VAR                                    !0
        15      DO_FCALL                         1          'count'
        16      EXT_FCALL_END
        17      IS_SMALLER                          ~6      !2, $5
        18      EXT_STMT
        19    > JMPZNZ                          23          ~6, ->24
        20  >   POST_INC                            ~7      !2
        21      FREE                                        ~7
        22    > JMP                                         ->13
   5    23  > > JMP                                         ->20
   6    24  > > RETURN                                      1
        25*   > ZEND_HANDLE_EXCEPTION
vld所呈現的opcode如上,總共26行。有興趣瞭解上面opcode含意,請見PHP: Opcode list - Manual

接者,將程式碼做修改。不在for中做count動作。來看opcode的變化
<?php
$myarray = array('apc', 'opcode', 'test');
$total = count($myarray);
for ($counter = 0; $counter < $total; $counter++) {
}

filename:       /apc/opcodetest.php
function name:  (null)
number of ops:  22
compiled vars:  !0 = $myarray, !1 = $total, !2 = $counter
line     # *  op              fetch          ext  return  operands
--------------------------------------------------------------------
   2     0  >   EXT_STMT
         1      INIT_ARRAY                          ~0      'apc'
         2      ADD_ARRAY_ELEMENT                   ~0      'opcode'
         3      ADD_ARRAY_ELEMENT                   ~0      'test'
         4      ASSIGN                                      !0, ~0
   3     5      EXT_STMT
         6      EXT_FCALL_BEGIN
         7      SEND_VAR                                    !0
         8      DO_FCALL                         1          'count'
         9      EXT_FCALL_END
        10      ASSIGN                                      !1, $2
   4    11      EXT_STMT
        12      ASSIGN                                      !2, 0
        13  >   IS_SMALLER                          ~5      !2, !1
        14      EXT_STMT
        15    > JMPZNZ                          19          ~5, ->20
        16  >   POST_INC                            ~6      !2
        17      FREE                                        ~6
        18    > JMP                                         ->13
   5    19  > > JMP                                         ->16
   6    20  > > RETURN                                      1
        21*   > ZEND_HANDLE_EXCEPTION

vld所顯示的opcode總共22行,和上一版本相比,少了下面4行。
13  >   EXT_FCALL_BEGIN
14      SEND_VAR                                    !0
15      DO_FCALL                         1          'count'
16      EXT_FCALL_END

也就是說,當我們使用for ($counter = 0; $counter < count($myarray); $counter++)這樣的程式時,在每次的迴圈執行時都要計算一次陣列的大小。以上面程式的例子,我們只要在for之前跑一次count()即可,不需要每次都使用系統資源去做無意義的計算。

當我們在處理大量資料、或者高流量的系統時,甚至上述兩者的組合時,這樣的小差異就會如成語所說....積沙成塔、聚少成多,而影響了程式執行效能。

當然,換更有力的硬體也能解決。不過,只是修改一下程式寫作方式,就可以改善效能,又何必另花成本?還可以少點電力消耗,為地球盡點力。 :)

不過,有些提升php效能的說法,僅靠opcode似乎看不出差異。

參考資料

留言