日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

深入理解PHP Opcode缓存原理

發布時間:2024/9/20 php 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入理解PHP Opcode缓存原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么是opcode緩存?

當解釋器完成對腳本代碼的分析后,便將它們生成可以直接運行的中間代碼,也稱為操作碼(Operate Code,opcode)。Opcode cache的目地是避免重復編譯,減少CPU和內存開銷。如果動態內容的性能瓶頸不在于CPU和內存,而在于I/O操作,比如數據庫查詢帶來的磁盤I/O開銷,那么opcode cache的性能提升是非常有限的。但是既然opcode cache能帶來CPU和內存開銷的降低,這總歸是好事。

現代操作碼緩存器(Optimizer+,APC2.0+,其他)使用共享內存進行存儲,并且可以直接從中執行文件,而不用在執行前“反序列化”代碼。這將帶來顯著的性能加速,通常降低了整體服務器的內存消耗,而且很少有缺點。

為什么要使用Opcode緩存?

這得從PHP代碼的生命周期說起,請求PHP腳本時,會經過五個步驟,如下圖所示:

Zend引擎必須從文件系統讀取文件、掃描其詞典和表達式、解析文件、創建要執行的計算機代碼(稱為Opcode),最后執行Opcode。每一次請求PHP腳本都會執行一遍以上步驟,如果PHP源代碼沒有變化,那么Opcode也不會變化,顯然沒有必要每次都重行生成Opcode,結合在Web中無所不在的緩存機制,我們可以把Opcode緩存下來,以后直接訪問緩存的Opcode豈不是更快,啟用Opcode緩存之后的流程圖如下所示:

有那些PHP opcode緩存插件?

Optimizer+(Optimizer+于2013年3月中旬改名為Opcache,PHP 5.5集成Opcache,其他的會不會消失?)、eAccelerator、xcache、APC?...

PHP opcode原理

Opcode是一種PHP腳本編譯后的中間語言,就像Java的ByteCode,或者.NET的MSL,舉個例子,比如你寫下了如下的PHP代碼:

  • <?php
  • ???echo?"Hello?World";
  • ???$a?=?1?+?1;
  • ???echo?$a;
  • ?>
  • PHP執行這段代碼會經過如下4個步驟(確切的來說,應該是PHP的語言引擎Zend)

  • Scanning(Lexing) ,將PHP代碼轉換為語言片段(Tokens)
  • Parsing, 將Tokens轉換成簡單而有意義的表達式
  • Compilation, 將表達式編譯成Opocdes
  • Execution, 順次執行Opcodes,每次一條,從而實現PHP腳本的功能
  • 題外話:現在有的Cache比如APC,可以使得PHP緩存住Opcodes,這樣,每次有請求來臨的時候,就不需要重復執行前面3步,從而能大幅的提高PHP的執行速度。

    那什么是Lexing? 學過編譯原理的同學都應該對編譯原理中的詞法分析步驟有所了解,Lex就是一個詞法分析的依據表。 Zend/zend_language_scanner.c會根據Zend/zend_language_scanner.l(Lex文件),來輸入的 PHP代碼進行詞法分析,從而得到一個一個的“詞”,PHP4.2開始提供了一個函數叫token_get_all,這個函數就可以講一段PHP代碼 Scanning成Tokens;

    如果用這個函數處理我們開頭提到的PHP代碼,將會得到如下結果:

  • Array
  • (
  • ????[0]?=>?Array
  • ????????(
  • ???????????[0]?=>?367
  • ???????????[1]?=>?Array
  • ????????(
  • ????????????[0]?=>?316
  • ????????????[1]?=>?echo
  • ????????)
  • ????[2]?=>?Array
  • ????????(
  • ????????????[0]?=>?370
  • ????????????[1]?=>
  • ????????)
  • ????[3]?=>?Array
  • ????????(
  • ????????????[0]?=>?315
  • ????????????[1]?=>?"Hello?World"
  • ????????)
  • ????[4]?=>?;
  • ????[5]?=>?Array
  • ????????(
  • ????????????[0]?=>?370
  • ????????????[1]?=>
  • ????????)
  • ????[6]?=>?=
  • ????[7]?=>?Array
  • ????????(
  • ????????????[0]?=>?370
  • ????????????[1]?=>
  • ????????)
  • ????[8]?=>?Array
  • ????????(
  • ????????????[0]?=>?305
  • ????????????[1]?=>?1
  • ????????)
  • ????[9]?=>?Array
  • ????????(
  • ????????????[0]?=>?370
  • ????????????[1]?=>
  • ????????)
  • ????[10]?=>?+
  • ????[11]?=>?Array
  • ????????(
  • ????????????[0]?=>?370
  • ????????????[1]?=>
  • ????????)
  • ????[12]?=>?Array
  • ????????(
  • ????????????[0]?=>?305
  • ????????????[1]?=>?1
  • ????????)
  • ????[13]?=>?;
  • ????[14]?=>?Array
  • ????????(
  • ????????????[0]?=>?370
  • ????????????[1]?=>
  • ????????)
  • ????[15]?=>?Array
  • ????????(
  • ????????????[0]?=>?316
  • ????????????[1]?=>?echo
  • ????????)
  • ????[16]?=>?Array
  • ????????(
  • ????????????[0]?=>?370
  • ????????????[1]?=>
  • ????????)
  • ????[17]?=>?;
  • )
  • 分析這個返回結果我們可以發現,源碼中的字符串,字符,空格,都會原樣返回。每個源代碼中的字符,都會出現在相應的順序處。而,其他的比如標簽,操作符,語句,都會被轉換成一個包含倆部分的Array: Token ID (也就是在Zend內部的改Token的對應碼,比如,T_ECHO,T_STRING),和源碼中的原來的內容。

    接下來,就是Parsing階段了,Parsing首先會丟棄Tokens Array中的多于的空格,然后將剩余的Tokens轉換成一個一個的簡單的表達式

  • echo?a?constant?string
  • add?two?numbers?together
  • store?the?result?of?the?prior?expression?to?a?variable
  • echo?a?variable
  • 然后就改Compilation階段了,它會把Tokens編譯成一個個op_array, 每個op_arrayd包含如下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_ECHO?!0
  • 你可能會問了,我們的$a去那里了?

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

  • op_type : 為IS_CONST, IS_TMP_VAR, IS_VAR, IS_UNUSED, or IS_CV b)
  • 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了。

    參考:http://www.laruence.com/2008/06/18/221.html


    來源:https://blog.linuxeye.com/361.html

    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

    以上是生活随笔為你收集整理的深入理解PHP Opcode缓存原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。