深入了解php opcode緩存原理


什么是opcode

opcode(operate code)是計算機指令中的一部分,用於指定要執行的操作,指令的格式和規范由處理器的指定規范指定

opcode是一種php腳本編譯后的中間語言,就像java的ByteCode,或者.NET的MSL

為什么要使用opcode緩存

opcode cache的目的是避免重復編譯,減少CPU和內存開銷的。如果動態內容的性能瓶頸不在於CPU和內容,而在於IO操作,比如數據庫查詢帶來的IO開銷,這個時候opcode cache的性能提升是非常有局限的。無論如何既然opcode cache 可以降低cpu和內存的開銷,這當然是好事了

目前PHP中常見的opcode cahce模塊如下

  1. APC

  2. Optimizer+(目前已開源並與php5.5+集成了opcache)

  3. xcache

  4. eAccelerator

Opcode原理

例如有如下一段代碼

<?php
echo 'Hello World';
$a = 1 + 1;
echo $a;
?>


php執行這段代碼會經過如下4個步驟(准確的說,通過php的語言引擎Zend)

Scanning(Lexing)將php代碼轉化為語言片段(Tokens)
Parsing,將Tokens轉化為簡單而有意義的表達式
Complilation,將表達式編譯成Opcode
Execution,順序執行Opcode,每次一條,從而實現php腳本的功能


如下圖

php_cycle.jpeg

Lexing階段

Lex 就是一個詞法分析的依據表。Zend引擎會會對輸入的php代碼進行詞法分析(切確的說是: Zend/zend_language_scanner.c會根據Zend/zend_language_scanner.l(Lex文件) ),從而得到一個一個的詞,php中提供了一個函數:token_get_all可以將一段php代碼解析成tokens

如果用這個函數分析上面的示例代碼,結果如下:

Array
(
    [0] => Array
        (
            [0] => 374
            [1] => <?php
            [2] => 1
        )

    [1] => Array
        (
            [0] => 377
            [1] =>
            [2] => 1
        )

    [2] => Array
        (
            [0] => 317
            [1] => echo
            [2] => 1
        )

    [3] => Array
        (
            [0] => 377
            [1] =>
            [2] => 1
        )

    [4] => Array
        (
            [0] => 316
            [1] => "Hello World"
            [2] => 1
        )

    [5] => ;
    [6] => Array
        (
            [0] => 310
            [1] => $a
            [2] => 1
        )

    [7] => Array
        (
            [0] => 377
            [1] =>
            [2] => 1
        )

    [8] => =
    [9] => Array
        (
            [0] => 377
            [1] =>
            [2] => 1
        )

    [10] => Array
        (
            [0] => 306
            [1] => 1
            [2] => 1
        )

    [11] => Array
        (
            [0] => 377
            [1] =>
            [2] => 1
        )

    [12] => +
    [13] => Array
        (
            [0] => 377
            [1] =>
            [2] => 1
        )

    [14] => Array
        (
            [0] => 306
            [1] => 1
            [2] => 1
        )

    [15] => ;
    [16] => Array
        (
            [0] => 377
            [1] =>
            [2] => 1
        )

    [17] => Array
        (
            [0] => 317
            [1] => echo
            [2] => 1
        )

    [18] => Array
        (
            [0] => 377
            [1] =>
            [2] => 1
        )

    [19] => Array
        (
            [0] => 310
            [1] => $a
            [2] => 1
        )

    [20] => ;
    [21] => Array
        (
            [0] => 376
            [1] => ?>
            [2] => 1
        )

)


分析這個返回結果我們可以發現,源碼中的字符串,字符,空格都會原樣返回。每個源代碼的字符都會出現在相應的順序處。而其他的例如標簽,操作符,語句 都被轉化成一個包含;兩部分的array:Token ID(也就是在Zend內部的該Token的對應碼,比如T_ECHO,T_STRING)和 源碼中原來的內容

Parsing階段

Parsing階段首先會丟棄Tokens array中的多余空格,然后將剩余的Tokens轉換成一個一個簡單的表達式

echo a contanst string
add two numbers together
store the result of the prior expression to a variable
echo a variable

Complilation階段

Complilation階段會把Tokens編譯成一個個op_array,每個op_array包含如下5個部分

Opcode數字的標示,指明了每個op_array的操作類型,比如add,echo
結果  存放Opcode的結果
操作數1 給Opcode的操作數
操作數2
擴展值 1個整形用來區別被重載的操作符


比如我的php代碼會被Parsing成:

ZEND_ECHO 'Hello World'
ZEND_ADD   ~0 1 1
ZEND_ASSIGN !0 ~0
ZEND_ECHI ~0

在上面的代碼我們並沒有看到 $a,去哪里了?


這個就要介紹操作數了,每個操作數都是由以下兩個部分組成:

  1. op_type :為IS_CONST,IS_TMP_VAR,IS_VAR,IS_UNUESED or IS_CV

  2. u 一個聯合體,根據op_type不同 分別用不同的類型保存這個操作數的值(const)或者左值(var)


而對於var來說,每個var也不一樣

IS_TMP_VAR 顧名思義就是這是一個臨時變量,保存一些op_array 的結果,以便接下來的op_array 使用,這種的操作數u保存着一個指向變量表的一個句柄(整數),這個操作數一般用~ 開頭 ,比如 ~0 表示 變量表中0號的未知的臨時變量


IS_VAR 這是我們一般意義上的變量,他們以$開頭表示


IS_CV 表示ZE2.1/PHP5.1以后的編譯器使用的一種cache機制,這種變量保存着被應用的變量地址,當一個變量第一次被應用的時候 ,就會被CV起來,以后對這個變量的引用就不需要再去查找active符號表了,CV變量已!開頭表示


這么開來 我的$a 被優化成了!0了


Opcode Cache原理

通過上面的介紹,我們了解了opcode,關於opcode cache的原理圖大致如下

opcode_cache.jpeg


我們可以看到除了 Lexing,Parsing,Complilation,Execution階段 還多了一個階段:檢測文件是否有更新

如果沒有更新直接獲取緩存的opcode,直接進入Execution階段然后返回結果

如果更新了就按照原來流程(加入一個環節:圖中紅色部分 緩存opcode)


原文地址: 深入了解php opcode緩存原理
標簽: php    opcache    opcode    apc    token_get_all    緩存   

智能推薦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM