php7和PHP5對比的新特性和性能優化


1  抽象語法樹( AST)

   1)在 PHP5中,從 php 腳本到 opcodes 的執行的過程是:

    Lexing:詞法掃描分析,將源文件轉換成 token 流;
    Parsing:語法分析,在此階段生成 op arrays。

2)PHP7 中在語法分析階段不再直接生成 op arrays,而是先生成 AST,所以過程多了一步:

    Lexing:詞法掃描分析,將源文件轉換成 token 流;
    Parsing:語法分析,從 token 流生成抽象語法樹;
    Compilation:從抽象語法樹生成 op arrays。

添加了抽象語法樹:內存的使用增加了,但是執行時間上卻有所降低

AST在PHP編譯過程作為一個中間件的角色,替換原來直接從解釋器吐出opcode的方式,讓解釋器(parser)和編譯器(compliler)解耦,可以減少一些Hack代碼,同時,讓實現更容易理解和可維護

2 Natice TLS

 

PHP在多線程模式下,需要解決“線程安全”(TS,Thread Safe)的問題,因為線程是共享進程的內存空間的,所以每個線程本身需要通過某種方式,構建私有的空間來保存自己的私有數據,避免和其他線程相互污染。

而PHP5采用的方式,就是維護一個全局大數組,為每一個線程分配一份獨立的存儲空間,線程通過各自擁有的key值來訪問這個全局數據組。而這個獨有的key值在PHP5中需要傳遞給每一個需要用到全局變量的函數,PHP7認為這種傳遞的方式並不友好,並且存在一些問題。因而,嘗試采用一個全局的線程特定變量來保存這個key值。

3 指定參數 返回值類型

PHP語言一個非常重要的特點就是“弱類型”,它讓PHP的程序變得非常容易編寫.

PHP7可選的方式支持類型定義,除此之外,還引入了一個開關指令declare(strict_type=1);,當這個指令一旦開啟,將會強制當前文件下的程序遵循嚴格的函數傳參類型和返回類型。

4 zval 結構的變化

 

在PHP5的時候, zval的定義如下:


 

    struct _zval_struct {
         union {
              long lval;
              double dval;
              struct {
                   char *val;
                   int len;
              } str;
              HashTable *ht;
              zend_object_value obj;
              zend_ast *ast;
         } value;
         zend_uint refcount__gc;
         zend_uchar type;
         zend_uchar is_ref__gc;
    };

首先這個結構體的大小是(在64位系統)24個字節, 我們仔細看這個zval.value聯合體, 其中zend_object_value是最大的長板, 它導致整個value需要16個字節, 這個應該是很容易可以優化掉的, 比如把它挪出來, 用個指針代替,因為畢竟IS_OBJECT也不是最最常用的類型.

第二, 這個結構體的每一個字段都有明確的含義定義, 沒有預留任何的自定義字段, 導致在PHP5時代做很多的優化的時候, 需要存儲一些和zval相關的信息的時候, 不得不采用其他結構體映射, 或者外部包裝后打補丁的方式來擴充zval, 比如5.3的時候新引入專門解決循環引用的GC, 它不得采用如下的比較hack的做法

第三, PHP的zval大部分都是按值傳遞, 寫時拷貝的值, 但是有倆個例外, 就是對象和資源, 他們永遠都是按引用傳遞, 這樣就造成一個問題, 對象和資源在除了zval中的引用計數以外, 還需要一個全局的引用計數, 這樣才能保證內存可以回收. 所以在PHP5的時代, 以對象為例, 它有倆套引用計數, 一個是zval中的, 另外一個是obj自身的計數:

 

第四, 我們知道PHP中, 大量的計算都是面向字符串的, 然而因為引用計數是作用在zval的, 那么就會導致如果要拷貝一個字符串類型的zval, 我們別無他法只能復制這個字符串. 當我們把一個zval的字符串作為key添加到一個數組里的時候, 我們別無他法只能復制這個字符串. 雖然在PHP5.4的時候, 我們引入了INTERNED STRING, 但是還是不能根本解決這個問題.

還比如, PHP中大量的結構體都是基於Hashtable實現的, 增刪改查Hashtable的操作占據了大量的CPU時間, 而字符串要查找首先要求它的Hash值, 理論上我們完全可以把一個字符串的Hash值計算好以后, 就存下來, 避免再次計算等等

第五, 這個是關於引用的, PHP5的時代, 我們采用寫時分離, 但是結合到引用這里就有了一個經典的性能問題:

第六, 也是最重要的一個, 為什么說它重要呢? 因為這點促成了很大的性能提升, 我們習慣了在PHP5的時代調用MAKE_STD_ZVAL在堆內存上分配一個zval, 然后對他進行操作, 最后呢通過RETURN_ZVAL把這個zval的值”copy”給return_value, 然后又銷毀了這個zval, 比如pathinfo這個函數:

 

5 異常處理

PHP 5 的 try ... catch ... finally 無法處理傳統錯誤,如果需要,你通常會考慮用 set_error_handler() 來 Hack 一下。但是仍有很多錯誤類型是 set_error_handler() 捕捉不到的

 PHP 7引入 Throwable 接口,錯誤及異常都實現了 Throwable,無法直接實現 Throwable,但可以擴展 \Exception 和 \Error 類。可以用 Throwable 捕捉異常跟錯誤。\Exception 是所有PHP及用戶異常的基類;\Error 是所有內部PHP錯誤的基類。

$name = "Tony";

try {

    $name = $name->method();

} catch (\Error $e) {

    echo "出錯消息 --- ", $e->getMessage(), PHP_EOL;

}

 

try {

    $name = $name->method();

} catch (\Throwable $e) {

    echo "出錯消息 --- ", $e->getMessage(), PHP_EOL;

}

 

try {

    intdiv(5, 0);

} catch (\DivisionByZeroError $e) {

    echo "出錯消息 --- ", $e->getMessage(), PHP_EOL;

}

 

6 hashtable 的變化

7 執行器

8 新的參數解析方式

PHP5  對應的參數解析 zend_parse_parament,

PHP7對應的參數解析:fast_zpp  
————————————————
版權聲明:本文為CSDN博主「fish_study_csdn」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/fish_study_csdn/article/details/80407755


免責聲明!

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



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