什么是opcode
也許你曾經嘗試過用C/C++編寫動態內容,雖然開發過程極其繁瑣,但為了獲得性能提升,這樣做或許是值得的,它們可以將動態內容編譯成二進制可執行文件,也就是目標代碼,由操作系統進程直接裝載運行。如今已經很少有人使用C/C++編寫動態內容了,絕大多數的Web開發人員享受著當下的幸福時光,很多優秀的腳本語言可供選擇,比如PHP,Ruby,Python,它們都屬于解釋型語言,所以用它們編寫的動態內容都需要依賴響應的解釋器程序來運行。
解釋器程序也是一個二進制可執行文件,比如/bin/ruby,它同樣可以直接在進程中運行,在運行過程中,解釋器程序需要對輸入的腳本代碼進行分析,領會它們的旨意,然后執行它們。比如下面我們調用PHP的解釋器,讓它執行一段簡單的腳本代碼:
~ zfs$ php -r 'print 1+1;'
很好,它很快的輸出了正確的結果,也許你覺的1+1的計算太小兒科,的確,在人類的大腦中計算1+1是很簡單,幾乎不用思考,但解釋器的工作方式可不是你想象的那樣,1+1和100+1000對它來說幾乎沒有什么區別,因為解釋器核心引擎根本看不懂這些腳本代碼,無法直接執行,所以需要進行一系列的代碼分析工作,當解釋器完成對腳本代碼的分析后,便將它們生成可以直接運行的中間代碼,也稱為操作碼(Operate Code,opcode)。
從程序代碼到中間代碼的這個過程,我們稱為解釋(parse),它由解釋器來完成。如此相似的是,編譯型語言中的編譯器(如C語言的編譯器gcc),也要將程序代碼生成中間代碼,這個過程我們稱為編譯(compile)。編譯器和解釋器的一個本質不同在于,解釋器在生成中間代碼后,便直接執行它,所以運行時的控制權在解釋器;而編譯器則將中間代碼進一步優化,生成可以直接運行的目標程序,但不執行它,用戶可以在隨后的任意時間執行它,這時的控制權在目標程序,和編譯器沒有任何關系。
事實上,就解釋和編譯本身而言,它們的原理是相似的,都包括詞法分析,語法分析,語義分析等,所以,有些時候將解釋型語言中生成opcode的過程也稱為"編譯",需要你根據上下文來理解。
那么,opcode究竟是什么樣的呢? 它又是如何解釋生成的呢? 我們以PHP為例,來尋找這些答案。
PHP解釋器的核心引擎為Zend Engine,可以很容易的查看它的版本:
- ~ zfs$ php -v
- PHP 5.5.14 (cli) (built: Sep 9 2014 19:09:25)
- Copyright (c) 1997-2014 The PHP Group
- Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
還記得前面我們曾經用PHP計算1+1的腳本代碼嗎? 我們來看看這段代碼的opcode。在此之前,需要安裝PHP的Parsekit擴展,它是一個用C編寫的二進制擴展,由PECL來維護。有了Parsekit擴展后,我們就可以通過它提供的運行時API來查看任何PHP文件或者代碼段的opcode。我們直接在命令行中調用parsekit_compile_string(),如下所示:
/usr/local/php/bin/php -r "var_dump(parsekit_compile_string('print 1+1;'));"
這樣一來,我們便獲得了這段代碼的opcode,返回的是數組形式,結果如下所示:
- array(20) {
- ["type"]=>
- int(4)
- ["type_name"]=>
- string(14) "ZEND_EVAL_CODE"
- ["fn_flags"]=>
- int(0)
- ["num_args"]=>
- int(0)
- ["required_num_args"]=>
- int(0)
- ["pass_rest_by_reference"]=>
- bool(false)
- ["uses_this"]=>
- bool(false)
- ["line_start"]=>
- int(0)
- ["line_end"]=>
- int(0)
- ["return_reference"]=>
- bool(false)
- ["refcount"]=>
- int(1)
- ["last"]=>
- int(5)
- ["size"]=>
- int(5)
- ["T"]=>
- int(2)
- ["last_brk_cont"]=>
- int(0)
- ["current_brk_cont"]=>
- int(4294967295)
- ["backpatch_count"]=>
- int(0)
- ["done_pass_two"]=>
- bool(true)
- ["filename"]=>
- string(17) "Parsekit Compiler"
- ["opcodes"]=>
- array(5) {
- [0]=>
- array(8) {
- ["address"]=>
- int(33847532)
- ["opcode"]=>
- int(1)
- ["opcode_name"]=>
- string(8) "ZEND_ADD"
- ["flags"]=>
- int(197378)
- ["result"]=>
- array(3) {
- ["type"]=>
- int(2)
- ["type_name"]=>
- string(10) "IS_TMP_VAR"
- ["var"]=>
- int(0)
- }
- ["op1"]=>
- array(3) {
- ["type"]=>
- int(1)
- ["type_name"]=>
- string(8) "IS_CONST"
- ["constant"]=>
- &int(1)
- }
- ["op2"]=>
- array(3) {
- ["type"]=>
- int(1)
- ["type_name"]=>
- string(8) "IS_CONST"
- ["constant"]=>
- &int(1)
- }
- ["lineno"]=>
- int(1)
- }
- [1]=>
- array(7) {
- ["address"]=>
- int(33847652)
- ["opcode"]=>
- int(41)
- ["opcode_name"]=>
- string(10) "ZEND_PRINT"
- ["flags"]=>
- int(770)
- ["result"]=>
- array(3) {
- ["type"]=>
- int(2)
- ["type_name"]=>
- string(10) "IS_TMP_VAR"
- ["var"]=>
- int(1)
- }
- ["op1"]=>
- array(3) {
- ["type"]=>
- int(2)
- ["type_name"]=>
- string(10) "IS_TMP_VAR"
- ["var"]=>
- int(0)
- }
- ["lineno"]=>
- int(1)
- }
- [2]=>
- array(7) {
- ["address"]=>
- int(33847772)
- ["opcode"]=>
- int(70)
- ["opcode_name"]=>
- string(9) "ZEND_FREE"
- ["flags"]=>
- int(271104)
- ["op1"]=>
- array(4) {
- ["type"]=>
- int(2)
- ["type_name"]=>
- string(10) "IS_TMP_VAR"
- ["var"]=>
- int(1)
- ["EA.type"]=>
- int(0)
- }
- ["op2"]=>
- array(3) {
- ["type"]=>
- int(8)
- ["type_name"]=>
- string(9) "IS_UNUSED"
- ["opline_num"]=>
- string(1) "0"
- }
- ["lineno"]=>
- int(1)
- }
- [3]=>
- array(7) {
- ["address"]=>
- int(33847892)
- ["opcode"]=>
- int(62)
- ["opcode_name"]=>
- string(11) "ZEND_RETURN"
- ["flags"]=>
- int(16777984)
- ["op1"]=>
- array(3) {
- ["type"]=>
- int(1)
- ["type_name"]=>
- string(8) "IS_CONST"
- ["constant"]=>
- &NULL
- }
- ["extended_value"]=>
- int(0)
- ["lineno"]=>
- int(1)
- }
- [4]=>
- array(5) {
- ["address"]=>
- int(33848012)
- ["opcode"]=>
- int(149)
- ["opcode_name"]=>
- string(21) "ZEND_HANDLE_EXCEPTION"
- ["flags"]=>
- int(0)
- ["lineno"]=>
- int(1)
- }
- }
- }
parsekit擴展的安裝請參見這篇文章
系統緩存
它是指APC把PHP文件源碼的編譯結果緩存起來,然后在每次調用時先對比時間標記。如果未過期,則使用緩存的中間代碼運行。默認緩存3600s(一小時)。但是這樣仍會浪費大量CPU時間。因此可以在php.ini中設置system緩存為永不過期(apc.ttl=0)。不過如果這樣設置,改運php代碼后需要重啟WEB服務器。目前使用較多的是指此類緩存。
用戶數據緩存
緩存由用戶在編寫PHP代碼時用apc_store和apc_fetch函數操作讀取、寫入的。如果數據量不大的話,可以一試。如果數據量大,使用類似memcache此類的更加專著的內存緩存方案會更好。
APC模塊的安裝
最簡單的方法是直接使用pecl,在命令行下輸入:/usr/local/php/bin/pecl install apc
然后按照提示一步步完成即可,示例如下:
- [root@iZ23bm1tc0pZ ~]# /usr/local/php/bin/pecl install apc
- downloading APC-3.1.13.tgz ...
- Starting to download APC-3.1.13.tgz (171,591 bytes)
- .....................................done: 171,591 bytes
- 55 source files, building
- running: phpize
- Configuring for:
- PHP Api Version: 20100412
- Zend Module Api No: 20100525
- Zend Extension Api No: 220100525
- Enable internal debugging in APC [no] : no
- Enable per request file info about files used from the APC cache [no] : no
- Enable spin locks (EXPERIMENTAL) [no] : no
- Enable memory protection (EXPERIMENTAL) [no] : no
- Enable pthread mutexes (default) [no] : no
- Enable pthread read/write locks (EXPERIMENTAL) [yes] : yes
然后重啟服務器即可:
lnmp nginx restart
先看一下沒有使用apc情況下的壓測結果:
- [root@iZ23bm1tc0pZ ~]# ab -n1000 -c100 http://Vevb.com/index.php
- This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
- Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://m.survivalescaperooms.com/
- Licensed to The Apache Software Foundation, http://www.apache.org/
- Benchmarking zfsphp.cn (be patient)
- Completed 100 requests
- Completed 200 requests
- Completed 300 requests
- Completed 400 requests
- Completed 500 requests
- Completed 600 requests
- Completed 700 requests
- Completed 800 requests
- Completed 900 requests
- Completed 1000 requests
- Finished 1000 requests
- Server Software: nginx
- Server Hostname: zfsphp.cn
- Server Port: 80
- Document Path: /index.php
- Document Length: 14341 bytes
- Concurrency Level: 100
- Time taken for tests: 15.517 seconds
- Complete requests: 1000
- Failed requests: 0
- Total transferred: 14544000 bytes
- HTML transferred: 14341000 bytes
- Requests per second: 64.45 [#/sec] (mean)
- Time per request: 1551.671 [ms] (mean)
- Time per request: 15.517 [ms] (mean, across all concurrent requests)
- Transfer rate: 915.34 [Kbytes/sec] received
- Connection Times (ms)
- min mean[+/-sd] median max
- Connect: 0 2 4.8 0 17
- Processing: 46 1481 277.0 1560 1638
- Waiting: 42 1481 277.1 1560 1638
- Total: 58 1482 272.8 1560 1638
- Percentage of the requests served within a certain time (ms)
- 50% 1560
- 66% 1576
- 75% 1582
- 80% 1587
- 90% 1602
- 95% 1612
- 98% 1622
- 99% 1629
- 100% 1638 (longest request)
可見最大吞吐率只有64.45reqs/s,然后我們開啟apc,測試結果如下:
- [root@iZ23bm1tc0pZ ~]# ab -n1000 -c100 http://Vevb.com/index.php
- This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
- Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://m.survivalescaperooms.com/
- Licensed to The Apache Software Foundation, http://www.apache.org/
- Benchmarking zfsphp.cn (be patient)
- Completed 100 requests
- Completed 200 requests
- Completed 300 requests
- Completed 400 requests
- Completed 500 requests
- Completed 600 requests
- Completed 700 requests
- Completed 800 requests
- Completed 900 requests
- Completed 1000 requests
- Finished 1000 requests
- Server Software: nginx
- Server Hostname: Vevb.com
- Server Port: 80
- Document Path: /index.php
- Document Length: 14341 bytes
- Concurrency Level: 100
- Time taken for tests: 7.122 seconds
- Complete requests: 1000
- Failed requests: 0
- Total transferred: 14544000 bytes
- HTML transferred: 14341000 bytes
- Requests per second: 140.41 [#/sec] (mean)
- Time per request: 712.189 [ms] (mean)
- Time per request: 7.122 [ms] (mean, across all concurrent requests)
- Transfer rate: 1994.29 [Kbytes/sec] received
- Connection Times (ms)
- min mean[+/-sd] median max
- Connect: 0 1 2.4 0 10
- Processing: 23 677 125.3 705 775
- Waiting: 22 677 125.4 705 775
- Total: 30 678 123.1 705 775
- Percentage of the requests served within a certain time (ms)
- 50% 705
- 66% 719
- 75% 726
- 80% 730
- 90% 742
- 95% 750
- 98% 760
- 99% 765
- 100% 775 (longest request)
[root@iZ23bm1tc0pZ ~]# ab -n1000 -c100 http://Vevb.com/index.php
新聞熱點
疑難解答