PHP 曾經存在各式各樣的語言層面快取機制,從 PHP 5.5 之後在編譯時就以 Zend Opcache 為預設的快取機制,從此也算是一統天下。
註:在官方宣佈不維護 APC 之後,有人跳出來做了 APCu,這到目前仍是可以使用的
註:composer 的最佳化中的第二種方式即是使用 APCu 進行快取,但它會與--classmap-authoritative
衝突
PHP 7 對其語言的直譯做了大幅度變化,以下將 PHP 5 與 7 分開說明。
不過對於 Zend VM 而言,它最終執行的都是 opcodes:可以將其想像是 JVM 執行 bytecode 那樣的模式。
從 PHP Source Code 到 opcodes 通常是經過以下流程
之後把 opcode 丟進 Zend VM 中執行。
PHP 7 的 Lexical Analysis 並沒有太大改變,但 Parse 階段不再直接解析為 opcode array。
AST 的存在可以將解析器與編譯器分離,使其可以做到一些原本難以做到的語法,詳細可以參閱 AST 的 RFC。
Zend Opcache 是利用將(PHP 7)compile 出來的 opcode 存在記憶體中,在文件未改變的情況下直接使用 opcode 執行,如此一來就不需要重新再做 Lexical Analysis、Parse 及 Compile。
通常會在 php.ini
中對幾個 Opcache 的設定做客製化:
opcache.memory_consumption
這個值表示 opcache 在儲存 opcodes 時會使用到多少記憶體,這個大小視自己檔案多寡而定。
可以靠著使用 opcache_get_status()
看到目前使用多少記憶體來儲存 opcodes。opcahce.memory_consuption
的設定要比 opcache_get_status()
所得到的值再大一些才有意義。
單位是 MB。
opcache.interned_strings_buffer
PHP 有個優化性能的特性是 interned strings,它可以將已經出現過的字串儲存於一塊記憶體中,如果該字串重複出現的話就直接將指標指向該記憶體區段,從而減少字串儲存的消耗。
預設情況下,interned string 僅會儲存在各 php-fpm 的 process 中,藉由設定 opcache.interned_strings_buffer
讓各 php-fpm 能夠共享一塊指定大小的 buffer 來改善性能。
單位是 MB。
opcache.max_accelerated_files
這個值代表 Opcache 可以快取多少 PHP 檔案,這個值一定要比專案中所有的檔案數量總合要大,vendor/
下的也要一併計算進去。
可以用一個很簡單的指令去計算:find ./ -type f -name *.php | wc -l
opcache.revalidate_freq = 0
此值表示多久(單位為秒)檢查一次 PHP 檔案是否有變化,如果檔案發生變化就重新編譯。
通常我會將其設定為 0
:在開發環境時當然會希望隨時都使用最新的 PHP Code;在生產環境時這個值並無意義(我們會在下一個設定中讓生產環境永遠不會去檢查 PHP 檔案是否變更)
opcache.validate_timestamps
它決定是否在一段時間後檢查 PHP 檔是否有變化,從而決定是否重新編譯,這個時間由 opcache.revalidate_freq
做決定。
如果這個值設定為 1
,它會在每幾秒去確定 PHP 檔案是否發生變化;如果這個值設定為 0
,它永遠不會去確定 PHP 檔案是否變化。
通常在開發環境中為 1
,在生產環境為 0
。
opcache.fast_shutdown
很多關於 opcache 的文章都會提到這個值應該設定為 1
,然後抱怨官方文件中沒有特別說明。
事實上,這個值在 PHP 7.2 開始就被移除,現在 PHP 會自動處理這些狀況。
PHP 在 7.4 加入 Preload 的功能,它整合於 Opcache 中。
其原因是 Opcache 雖然降低了編譯時的開銷,但還因為每個 PHP 檔案都是獨立每編譯的,所以在檔案與檔案之間的關係仍然無法被快取起來。
藉由 preload 的機制,我們可以一次載入大量的 PHP 檔案(例如把 composer 的 autoloader 載入),並且提前建立檔案之間的關係。
PHP 在 8.0 加入 JIT,它整合於 Opcache 中。
本次的 JIT 會使用 DynASM 作為為基礎去將 op_array 生成 opcodes,根據 RFC 的說明中,開發者原本嘗試過 LLVM 但其效能慢了 100 倍。
V8 引擎得益於其 JIT 的實現才能擁有極高的運行效率,雖然 PHP 的腳步慢了一些,但未來仍是可以期待的。
註:依照目前實現的 JIT,可能會與 Xdebug 等 extension 有所衝突,這問題還需要等待開放新的核心 API 才有解。
Opcache 是 PHP 極重要的一項 Feature,可以讓運行效率大幅提升(而核心開發者也仍為此努力著)。
會建議所有的 PHP 開發者,無論是開發環境或生產環境,都應該要使用 Opcache。