簡介:這是PHP 程式加速探索的詳細頁面,介紹了和php,有關的知識、技巧、經驗,和一些php源碼等。
class='pingjiaF' frameborder='0' src='http://biancheng.dnbcw.info/pingjia.php?id=324442' scrolling='no'>
- (一)簡介
- (二)是否需要加速?
- (三)如何加速?
-<1> 測試
◆ 伺服器負載測試 ApacheBench
◆ 指令碼執行速度測試 PEAR:: Benchmark
-<2> 加速
◆ 代碼最佳化
◆ 壓縮輸出 Gzip
◆ 內容緩衝輸出 PEAR Content cache
◆ 函數緩衝輸出 PEAR Function cache
◆ 加速工具軟體 APC、Turck MMCache、PHPA、Zend Performance Suite
- (四)總結
(一)簡介
本文嘗試對PHP程式加速的各個方面進行探索,包括必要性以及從不同角度採取的具體措施。希望有助於讀者瞭解PHP程式加速,並應用於實際。
(二)是否需要加速?
這個問題聽起來有點愚蠢。在這個時代,很少有人會懷疑時間是最寶貴的財富,尤其是在商業市場上。程式執行越快,使用者就節約越多的時間,這樣你的程式就可以用更少的時間和伺服器資源為使用者服務,從而產生更多效益。
我想對於大部份人(包括我自己)來說,很多WEB項目都是在很緊張的時間裡完成的,通常沒有經過縝密的思考和嚴格的測試。當開始一個新的WEB項目。很多人都在構建那種“快而亂”的應用,缺乏必要的時間來調整和改良代碼,這時最佳化和加速就是我們必須採取的措施。
然而需要特別指出的是,並不是所有程式都需要加速。
最佳化已完成的代碼是很浪費時間的,最好的方法是在寫代碼的時候就注意到效率,然後完成項目後只最佳化確實需要最佳化的那部份。一般一個程式只會有少數幾個影響速度的瓶頸,將它們找出來並解決掉,程式就可以很好地運行。另外,當遇到執行效率低下的情況,首先要用大局的眼光來找出影響效率的主要因素,而不要拘泥於細節—例如資料量過大,伺服器頻寬不夠,或硬體設定過低,在這樣的情況下,最佳化代碼於事無補。
另外,在沒有發現程式有明顯的執行緩慢的跡象時,就不要太吹毛求疵,為了改進一些非常細節的代碼而浪費時間。用這些時間,你可以完成另一個項目或為原來的項目完成一個擴充功能。當然你可以笑話我不夠負責,沒有把工作做得盡善盡好,我也可以說你是完美主義者:-)
綜上,在你決定為你的PHP程式提速之前,問問自己是否有必要。
(三)如何加速?
要回答“如何加速”這個問題前,需要先回答以下兩個小問題:
你的程式慢在哪一部份?
PHP可以從哪幾個方面考慮加速?
第一個小問題顯然我無法給你答案,但我建議你用“測試指令碼執行速度”的方法來解決。只有找出限制速度的瓶頸,才能考慮如何去解決。
第二個小問題我大概的答案是:代碼最佳化,壓縮輸出,內容緩衝輸出,函數緩衝輸出,加速/緩衝工具軟體。如果你知道更多請告訴我:-)
下面我們來詳細地研究一下這幾個方面的相關技術。當然實際上每個方面都有無數的細節可以討論,下面的內容難免會有片面的地方,歡迎補充。
<1> 測試
◆ 伺服器負載測試
伺服器負載太大而影響程式效率也是很常見的,我們需要對此進行測試。這裡我以目前最常用的Apache伺服器為例。
Apache伺服器內建有一個叫AB(ApacheBench)的工具,在bin目錄下。使用這個輕巧的工具我們可以對伺服器進行負載測試,看看在重負荷之下伺服器的表現如何。ApacheBench 可以針對某個特定的 URL 模擬出連續的聯機請求,同時還可以模擬出同時間點數個相同的聯機請求,因此利用 ApacheBench 可協助我們在網站開發期間模擬實際上線可能的情況,利用模擬出來的資料作為調整伺服器設定或程式的依據。
在命令列下輸出:
./ab -n number_of_total_requests \
-c number_of_simultaneous_requests \
http://your_web_server/your_php_app.php
例如:
./ab -n 1000 -c 50 http://www.domain.com/myapp.php
AB將同時向http://www.domain.com/myapp.php發出50個並發請求,共發出1000次。
測試結果將可能是這樣的:
Server Software: Apache/2.0.16
Server Hostname: localhost
Server Port: 80
Document Path: /myapp.php
Document Length: 1311 bytes
Concurrency Level: 50
Time taken for tests: 8.794 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 1754000 bytes
HTML transferred: 1311000 bytes
Requests per second: 113.71
Transfer rate: 199.45 kb/s received
Connection Times (ms)
min avg max
Connect: 0 0 5
Processing: 111 427 550
Total: 111 427 555
myapp.php每秒鐘可以處理的請求數為113.71個。將請求數增加,看看伺服器能否處理更大的壓力。你也需要調節Apache的MaxClients, ThreadsPerChild, MaxThreadsPerChild 等參數,基於你的 httpd.conf 中的 MPM 模組選擇。
如果你想得到更詳細的資訊,請到www.apache.org上查閱一些更深入的文檔,包括模組和第三方的提高效率的工具。修改httpd.conf後,要重啟Apache伺服器,然後再用AB測試。你會看到每秒請求數增加或減少。
記下每次的參數,最後選擇最佳效率的那種配置。
要指出的是,除了AB,還有許多優秀的伺服器效能測試軟體。另外,如果你的伺服器不是 Apache,請自行尋找測試方法。
◆ 指令碼執行速度測試
前面有提到,只有找到影響速度的代碼,我們才有可能進行最佳化。PEAR的benchmark包中的Benchmark_Timer類和Benchmark_Iterate類,可以用來很方便地測試指令碼執行的速度。(關於PEAR的安裝與配置請自行查看相關資料)
首先用Benchmark_Iterate類來測試程式中某個函數或類的某個方法的執行時間。
benchmark1.php
<?php
require_once('Benchmark/Iterate.php');
$benchmark = new Benchmark_Iterate();
$benchmark->run(10, 'myFunction','test');
$result = $benchmark->get();
echo "
"; print_r($result); echo "
";
exit;
function myFunction($var) {
// do something
echo 'Hello ';
}
?>
建立benchmark Iterate對象$benchmark,這個對象用來執行myFunction函數10次。
$argument變數每次都傳遞給myFunction. 多次啟動並執行分析結果存入$result,然後用benchmark對象的get()方法來擷取。這個結果用print_r()輸出到螢幕。通常會輸出這樣的結果:
Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello
Array( [1] => 0.000427 [2] => 0.000079 [3] => 0.000072 [4] => 0.000071 [5] => 0.000076 [6] => 0.000070 [7] => 0.000073 [8] => 0.000070 [9] => 0.000074 [10] => 0.000072 [mean] => 0.000108 [iterations] => 10) myFunction的每次執行,benchmark對象都會跟蹤執行時間。並且會計算平均的執行時間([mean]那一行)。通過多次運行目標函數,你可以得到該函數的平均已耗用時間。
在實際測試中,函數的次數應當至少1000次左右,這樣可以得到較客觀的結果。
現在我們看看另一個測試指令碼已耗用時間的方法--使用Benchmark_Timer類來測試一段代碼執行所消耗的時間及這一段代碼中每次調用與下一次調用間的時間。
benchmark2.php
<?php
require_once 'Benchmark/Timer.php';
$timer = new Benchmark_Timer();
$timer->start();
$timer->setMarker('start_myFunction');
for($i=0; $i<10; $i++){
myFunction($argument);
}
$timer->setMarker('end_myFunction');
$timer->stop();
$profiling = $timer->getProfiling();
echo '
Time elapsed: ' .
$timer->timeElapsed('start_myFunction','end_myFunction') .'
';
echo '
'; print_r($profiling); echo '
';
exit;
function myFunction($var) {
static $counter = 0;
// do something
echo $counter++ . ' ';
}
?>
首先,建立一個benchmark timer對象$timer。然後調用start()方法,表示開始計時。 SetMaker()方法用來標記要測試的程式碼片段。MyFunction()函數在迴圈中被調用,表示一段要執行的代碼(當然實際中不會這麼簡單)。然後再用$timer對象的setMarker()方法標記程式執行終點。分析資訊用getProfiling()來擷取。在兩個標記間程式執行消耗的時間用timeElapsed()方法計算出來(就像例子中的迴圈)。最後,用print_r()輸出資訊到螢幕:
Array( [0] => Array ( [name] => Start [time] => 1085730111.27175200 [diff] => - [total] => 1085730111.271752 ) [1] => Array ( [name] => start_myFunction [time] => 1085730111.27203800 [diff] => 0.000286 [total] => 1085730111.272038 ) [2] => Array ( [name] => end_myFunction [time] => 1085730111.27263200 [diff] => 0.000594 [total] => 1085730111.272632 ) [3] => Array ( [name] => Stop [time] => 1085730111.27271800 [diff] => 0.000086 [total] => 1085730111.272718 ))0 1 2 3 4 5 6 7 8 9
Time elapsed: 0.000594
通過這種方法,你可以在代碼中設定大量時間區段標記,擷取每段代碼執行時消耗的時間,很容易可以看出到底是哪一部份的代碼影響了整個程式的運行效率。然後開始著手對這部份代碼進行改進。
用以上兩種方法,你可以找出代碼中最影響速度的部份代碼。另外還可以用來對最佳化後的代碼進行測試,看看到底執行速度提高了多少。通過測試->最佳化->測試->最佳化 …這樣不斷迴圈,你可以最終確定提供最佳效率的代碼。
<2> 加速
◆ 代碼最佳化
掌握了PEAR::BenchMark,現在你已經知道如何測試你的代碼,知道如何判斷你的代碼是快是慢,是哪一部份比較慢。那麼接下來我要說的就是如何消滅或最佳化那部份慢的代碼。
這一點上我個人最主要的經驗只有兩點,一是消除錯誤的或低效的迴圈;二是最佳化資料庫查詢語句。其實還存在一些其它的最佳化細節,比如“str_replace比ereg_replace快”、“echo比print快”等等。這些我暫時都放在一邊,稍後我會提到用緩衝來對付過於頻繁的IO。
下面我們將三個功能相同,但程式寫法不同的函數的效率(消耗的時間)進行對比。
badloops.php
<?php
require_once('Benchmark/Iterate.php');
define('MAX_RUN',100);
$data = array(1, 2, 3, 4, 5);
doBenchmark('v1', $data);
doBenchmark('v2', $data);
doBenchmark('v3', $data);
function doBenchmark($functionName = null, $arr = null)
{
reset($arr);
$benchmark = new Benchmark_Iterate;
$benchmark->run(MAX_RUN, $functionName, $arr);
$result = $benchmark->get();
echo '<br>';
printf("%s ran %d times where average exec time %.5f ms",$functionName,$result['iterations'],$result['mean'] * 1000);
}
function v1($myArray = null) {
// 效率很差的迴圈
for ($i =0; $i < sizeof($myArray); $i++)
{
echo '<!--' . $myArray[$i] . ' --> ';
}
}
function v2($myArray = null) {
// 效率略有提高
$max = sizeof($myArray);
for ($i =0; $i < $max ; $i++)
{
echo '<!--' . $myArray[$i] . ' --> ';
}
}
function v3($myArray = null){
//最佳效率
echo "<!--", implode(" --> <!--", $myArray), " --> ";
}
?>
程式輸出的結果大概是這樣的:
v1 ran 100 times where average exec time 0.18400 ms
v2 ran 100 times where average exec time 0.15500 ms
v3 ran 100 times where average exec time 0.09100 ms
可以看到,函數的執行時間變少,效率上升。
函數v1有個很明顯的錯誤,每一次迴圈的時間,都需要調用sizeof()函數來計算。函數v2則在迴圈外把$myArray數組的元素個數存到$max變數中,避免了每次迴圈都要計算數組的元素個數,所以效率提高了。函數v3的效率最高,利用了現成的函數,避免迴圈。
這個例子只是給你一個感性的認識,明白什麼是相對高效的代碼。在實際開發中,我相信會有很多人會迷迷糊糊地寫出很多低效率的代碼。要把代碼寫得精鍊而高效,恐怕需要時間去錘鍊:-) 但這是另一個話題了,我們略過不談。
資料庫應用基本上每個PHP程式都會用到,在實際開發中我發現最影響整個系統效率的就是資料庫這部份。至於資料庫的最佳化和資料查詢語句的最佳化,在此限於篇幅不詳細討論。你可以參看這兩篇文章:
http://www.phpe.net/articles/340.shtml
http://www.phpe.net/articles/323.shtml
及這篇討論:
http://club.phpe.net/index.php?s ... mp;t=4783&st=0(前面幾篇貼子總結不錯),主要是針對MySQL的。
◆壓縮輸出 gzip
利用Apache中的mod_gzip模組,我們可以利用gzip的壓縮演算法來對Apache伺服器發布的網頁內容進行壓縮後再傳輸到用戶端的瀏覽器。如果是純文字的內容,效果非常明顯,大約可以壓縮到原來的30%-40%,使使用者的瀏覽速度大大加快。
Gzip需要用戶端瀏覽器支援,目前大部份瀏覽器都支援gzip,如IE,Netscape,Mozilla等,所以這種方法值得一試。我們可以利用PHP中的預定義變數$_SERVER[‘HTTP_ACCEPT_ENCODING’]來判斷用戶端瀏覽器是否支援gzip。
gzip1.php
<?php
if(ereg('gzip',$_SERVER['HTTP_ACCEPT_ENCODING'])) {
//瀏覽器支援
} else {
//瀏覽器不支援,輸出其它內容
}
?>
接下來我們對上面這個PHP程式進行擴充,使用ob_start(ob_gzhandler)來將網頁內容壓縮,存入緩衝並發送給支援gzip的瀏覽器,瀏覽器會自動將壓縮後的內容解壓,顯示。
gzip2.php
<?php
define('MAX',100);
if(ereg('gzip',$_SERVER['HTTP_ACCEPT_ENCODING']))
{
//瀏覽器支援gzip,將內容壓縮並緩衝輸出
ob_start("ob_gzhandler";
$output = '';
for($i=0;$i<=MAX;$i++)
{
$output .= "This is line $i
";
}
echo "瀏覽器支援gzip壓縮輸出
";
echo $output;
}
else
{
//瀏覽器不支援,直接輸出
for($i=0;$i<=MAX;$i++)
{
$output .= "This is line $i
";
}
echo "瀏覽器不支援gzip壓縮輸出
";
echo $output;
}
?>
使用gzip壓縮產生的網頁的HTTP頭資訊與一般的網頁相比中會多出這樣的資訊:
Content-Encoding: gzip
Content-Length: 270
如果你想得到更詳細的資訊,請參看mod_gzip項目首頁:
http://sourceforge.net/projects/mod-gzip/
類似地,我們也可以利用mod_deflate,壓縮率比mod_gzip略低一些。調用zip函數需要耗用伺服器記憶體,所以要慎用,視需求而定。
◆ 內容緩衝輸出 PEAR cache
接下來我們開始探索更常用的緩衝技術,這也是本文的重點部份。首先我們使用PEAR中的cache包。PEAR可以將內容緩衝於檔案,資料庫或者記憶體中,我們以檔案為例。
下面是一個沒有使用緩衝的PHP小程式:
pear_content_cache1.php
<?php
echo "這是內容。<P>";
echo "目前時間是" . date('M-d-Y H:i:s A', time()) . "<BR>";
?>
上面這個程式非常簡單,現在我們為其加上緩衝。
pear_content_cache2.php
<?php
require_once 'Cache/Output.php';
//設定緩衝目錄,必須是可寫的
$cacheDir = './pear_cache';
$cache = new Cache_Output('file',array('cache_dir' => $cacheDir));
//如果nocache變數為空白,使用緩衝中的內容
//如果想獲得最新的內容,就要賦值給nocache變數
if (empty($_REQUEST['nocache']))
{
// 建立一個獨一的cache標識
// 請求+Cookie資訊
$cache_id = $cache->generateID(array('url' => $_REQUEST,'post' =>$_POST,'cookies' => $HTTP_COOKIE_VARS));
}
else
{
//想獲得最新的內容,ID為空白
$cache_id = null;
}
//看cache ID對應的緩衝內容是否可用
if ($content = $cache->start($cache_id))
{
//緩衝已存在,直接輸出,並結束指令碼
echo $content;
exit();
}
// 緩衝中不存在該內容,產生新內容並寫入緩衝
echo "這是內容。<P>";
echo "目前時間是" . date('M-d-Y H:i:s A', time()) . "<BR>";
// 把內容寫入緩衝
echo $cache->end();
?>
分別重新整理這兩個檔案,你會發現pear_content_cache1.php中的“目前時間是”這一行中的時間是隨著重新整理而變化的,而pear_content_cache2.php中的這一行則不變。這是由於pear_content_cache2.php使用了緩衝,將使用者請求的內容存入靜態檔案中。當使用者再次請求時,它直接從檔案中輸出,而不需要用程式動態產生內容。
對於pear_content_cache2.php,如果使用者想要讀取最新的資訊,而不是緩衝中成舊的資訊。那麼可以用http://…/pear_content_cache2.php?nocache=1 來訪問,這將禁用緩衝功能。重新整理一下看看,你將發現時間會隨之變化。
總結一下PEAR內容緩衝類的使用:
1.包含PEAR包 要注意設對路徑。
2.包含Output.php中的cache類
require_once 'Cache/Output.php';
3.設定緩衝目錄
$cacheDir = './pear_cache';
確認這個目錄是可寫的。Cache資料將會寫入這個目錄的子目錄中。
4.建立一個輸出緩衝對象
$cache = new Cache_Output('file',array('cache_dir' => $cacheDir));
第一個參數表示我們使用基於“檔案”方式的緩衝,第二個參數是一個與緩衝目錄相關聯的數組。
5.產生一個唯一的cache ID
$cache_id = $cache->generateID(array('url' => $_REQUEST,'post' =>$_POST,'cookies' => $HTTP_COOKIE_VARS));
這裡$cache對象的generateID()方法通過提供一個資訊數組(URL, HTTP POST data, 和 HTTP cookie)來獨一無二地標識這個請求,與其它請求區分開來。
6.增加一個邏輯判斷語句看是否對應於cacheID的快取資料是否已經存在,如果存在,擷取資料並結束指令碼。
if ($content = $cache->start($cache_id))
{ echo $content;
exit();
}
7. 將產生內容的代碼放在以上邏輯語句之後,並結束使用cache對象。
echo $cache->end();
◆ 函數緩衝輸出 PEAR cache
PEAR除了可以對輸出的內容進行緩衝處理外,還可以將對某個函數的調用結果緩衝起來。這是個很有趣的功能,如果你的程式要頻繁使用到某個函數,而且調用的結果相同的話,我建議你不妨試試,特別是當這個函數運行起來比較慢的時候。
下面我們實現對一個執行起來很慢的函數slowFunction()的緩衝調用。
<?php
require_once 'Cache/Function.php';
$cacheDir = './pear_cache/';
$cache = new Cache_Function('file',array('cache_dir' => $cacheDir));
$arr = array('蘋果', '梨','西瓜');
$cache->call('slowFunction', $arr);
echo '<BR>';
$arr = array('蘋果', '梨','西瓜');
slowFunction($arr);
function slowFunction($arr = null)
{
echo "一個執行起來很慢的函數 <br>";
echo "目前時間是 " . date('M-d-Y H:i:s A', time()) . '<br>';
foreach ($arr as $fruit)
{
echo "我吃了一個 $fruit <br>";
}
}
?>
以下是樣本的指令碼執行結果:
一個執行起來很慢的函數
目前時間是 Jul-28-2004 17:15:57 PM
我吃了一個 蘋果
我吃了一個 梨
我吃了一個 西瓜
一個執行起來很慢的函數
目前時間是 Jul-28-2004 17:17:55 PM
我吃了一個 蘋果
我吃了一個 梨
我吃了一個 西瓜
代碼中,Cache/Function.php類用來執行函數緩衝功能。$cache變數是一個Cache_Function對象,使用基於檔案的函數緩衝,存入$cacheDir目錄。要緩衝一個函數調,Cache_Function對象$cache的call()方法要像這樣使用:$cache->call(‘slowFunction’, $arr);
這裡,slowFunction()函數被調用,參數為一個數組$arr,這個函數被緩衝在$cacheDir目錄下的一個檔案裡。任何在此之後的對這個函數的調用,將會由$cache->call()返回該函數執行的結果。
函數緩衝和使用方法和內容緩衝很相似,不再多說,具體請查看PEAR手冊。
以上我們都是利用最佳化代碼的方法對程式進行提速,接著我們要關注一下PHP加速的另一個領域—緩衝工具軟體。這類軟體都是從最佳化PHP運行環境來提速的,不需要改變任何代碼。我們可以大概地將它們稱為“執行碼最佳化/緩衝工具”,你可以理解為它們用來實現比較底層的最佳化/緩衝。
以下列出目前比較常用的此類工具,具體哪種效果最好,請用自己的伺服器環境測試:
(一)APC Alternative PHP Cache
http://apc.communityconnect.com/
APC運行於Linux和FreeBSD,你需要自己編譯安裝。按照其開發人員的說法,在他們的測試環境下可以提高指令碼速度50%-400%。並且APC是個開源項目,已經加入了PHP的PECL庫,很值得一試。
(二)Turck MMCache
http://turck-mmcache.sourceforge.net/
Turck MMCache似乎是此類軟體中目前最受歡迎的一種,它開放原始碼,完全免費。它將PHP代碼先行編譯並緩衝起來,同時也對PHP運行環境進行一定最佳化。按照其官方文檔的說法,MMCache可以明顯地減輕伺服器的負載,並提高指令碼執行速度1-10倍。
MMCache與另一個知名的加速軟體Zend Optimizer相容,但注意必需先安裝MMCache(php.ini中設定)。除了加快PHP程式的速度,MMCache還可以將PHP代碼加密。
Turck MMCache同時支援Linux和Win32平台。
(三)PHPA the PHP Accelerator
http://www.php-accelerator/
PHPA又是另一個流行的PHP加速軟體。在其官方網站上有分別使用PHPA與APC、Zend Cache的PHP指令碼執行測試對比,其表現略優於APC,略遜於Zend Cache。
PHPA支援Linux, FreeBSD, OpenBSD, BSDi和Solaris系統.
(四)Zend Performance Suite
http://www.zend.com/
Zend Performance Suite是老牌的PHP加速/最佳化軟體,依託於PHP領域最知名的Zend公司。目前已經推出4.0版本,它可以為PHP應用提供者加速,內容緩衝,檔案壓縮,下載服務等,功能十分強大,獲得好幾個PHP雜誌的推薦獎—但是不得不提起,它也很昂貴,目前的價格是1875美元。
以上幾種加速軟體,希望讀者按照伺服器環境自行測試並選擇其中最適用的,因為我沒有辦法提供一個普遍適用的測試標準來判斷哪種方案是最有效。綜合起來看,我個人認為Turck MMCache是個值得推薦的選擇,免費而且功能相當出色。
(四)總結
以上從多個角度較為全面細緻地闡述了PHP加速的相關技術,包括測試技術,加速技術(壓縮,緩衝等),基本上都有附代碼和範例。希望本文有助於讀者全面瞭解PHP程式加速並在實際應用中選擇合適的加速方案。
“PHP 程式加速探索”的更多相關文章 》
愛J2EE關注Java邁克爾傑克遜視頻站JSON線上工具
http://biancheng.dnbcw.info/php/324442.html pageNo:14