有篇有名的文章叫做:40 Tips for optimizing your php code ,不過原文已經消失了,這邊有轉貼版 ,至於翻譯則是在論壇上面隨便找的(看得懂就好,懶得去找原本是誰翻的,我猜是對岸網友)。
所以我做了一點實驗…
測試環境:
CPU: Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz
Memory: 8007 MB (用 Free -m 看)
Kernel: Linux 2.6.26-2-amd64 (debian)
FS: reiserfs
OS: Debian x86_64
PHP: PHP 5.2.6-1+lenny8 with Suhosin-Patch 0.9.6.2 (cli)
載入模組 (只有預先關閉 APC)
[PHP Modules] bcmath, bz2, calendar, ctype, curl, date, dba, dom, exif, filter, ftp, gd, gettext, hash, iconv, imagick, json, libxml, mbstring, mcrypt, mhash, mime_magic, mysql, mysqli, ncurses, openssl, pcntl, pcre, PDO, pdo_mysql, posix, Reflection, session, shmop, SimpleXML, soap, sockets, SPL, standard, suhosin, sysvmsg, sysvsem, sysvshm, tokenizer, wddx, xml, xmlreader, xmlwriter, zip,
zlib
[Zend Modules] Suhosin
測試方法有兩種,在 command line 下面用 time php5 test.php 或是用 php-benchmark ,我用後者,方便寫測試程式,有興趣的人應該也可以用前者驗證,挑幾條來玩玩看:
原文 If a method can be static, declare it static. Speed improvement is by a factor of 4.
翻譯 如果一個方法可靜態化,就對它做靜態聲明。速率可提升至4倍
結論 比較快是一定的…但究竟快了幾倍,那也得看方法內是做什麼事情而定。
原文 echo is faster than print.
翻譯 echo 比 print 快
結論 的確如此
原文 Use echo’s multiple parameters instead of string concatenation.
翻譯 使用echo的多重參數(譯註:指用逗號而不是句點)代替字元串連接
結論 的確如此
原文 When echoing strings it’s faster to separate them by comma instead of dot. Note: This only works with echo, which is a function that can take several strings as arguments.
翻譯 輸出多個字元串時,用逗號代替句點來分隔字元串,速度更快。註意:只有echo能這麼做,它是一種可以把多個字元串當作參數的“函數”(譯註:PHP手冊中說echo是語言結構,不是真正的函數,故把函數加上了雙引號)
結論 的確如此
原文 It’s better to use switch statements than multi if, else if, statements.
翻譯 使用選擇分支語句(譯註:即switch case)好於使用多個if,else if語句
結論 我測的結果是如果要轉換型別,Switch 會比較慢,但兩者還是沒有顯著差異。
原文 Error suppression with @ is very slow.
翻譯 用@屏蔽錯誤消息的做法非常慢
結論 這邊有個問題,就是要顯示錯誤訊息反而花更多成本,但用 @ 屏蔽的確在邏輯上會多做一些事情
原文 Error messages are expensive
翻譯 錯誤訊息代價昂貴
結論 請對照上一條原則.. XD
原文 $row[’id’] is 7 times faster than $row[id]
翻譯 $row[‘id’]的效率是$row[id]的7倍
結論 效率會比較高,但不明顯,我覺得更重要的是 $row[’id’] 可讀性好多了!
原文 Do not use functions inside of for loop.
翻譯 盡量不要在for循環中使用函數,比如for ($x=0; $x < count($array); $x) 每循環一次都會調用count()函數
結論 非常正確
原文 Just declaring a global variable without using it in a function also slows things down (by about the same amount as incrementing a local var). PHP probably does a check to see if the global exists.
翻譯 僅定義一個局部變量而沒在函數中調用它,同樣會減慢速度(其程度相當於遞增一個局部變量)。PHP大概會檢查看是否存在全局變量
結論 在 function 內有差,在 Class 內沒有顯著差別。
原文 Method invocation appears to be independent of the number of methods defined in the class because I added 10 more methods to the test class (before and after the test method) with no change in performance.
翻譯 方法調用看來與類中定義的方法的數量無關,因為我(在測試方法之前和之後都)添加了10個方法,但性能上沒有變化
結論 差距並不顯著
原文 When working with strings and you need to check that the string is either of a certain length you’d understandably would want to use the strlen() function. This function is pretty quick since it’s operation does not perform any calculation but merely return the already known length of a string available in the zval structure (internal C struct used to store variables in PHP). However because strlen() is a function it is still somewhat slow because the function call requires several operations such as lowercase & hashtable lookup followed by the execution of said function. In some instance you can improve the speed of your code by using an isset() trick. Ex. if (strlen($foo) < 5) { echo "Foo is too short"; } vs. if (!isset($foo{5})) { echo "Foo is too short"; } Calling isset() happens to be faster then strlen() because unlike strlen(), isset() is a language construct and not a function meaning that it's execution does not require function lookups and lowercase. This means you have virtually no overhead on top of the actual code that determines the string's length. 翻譯 當操作字元串並需要檢驗其長度是否滿足某種要求時,你想當然地會使用strlen()函數。此函數執行起來相當快,因為它不做任何計算,只返回在zval 結構(C的內置數據結構,用於存儲PHP變量)中存儲的已知字元串長度。但是,由於strlen()是函數,多多少少會有些慢,因為函數調用會經過諸多步驟,如字母小寫化(譯註:指函數名小寫化,PHP不區分函數名大小寫),會跟隨被調用的函數一起執行。在某些情況下,你可以使用isset() 技巧加速執行你的程式。 舉例如下: if (strlen($foo) < 5) { echo "Foo is too short"; } 與 if (!isset($foo{5})) { echo "Foo is too short"; } 結論 是的,跑 1000 次的情況下效能差了 10% 。
如果有興趣深入探討,可以看更多 The PHP Benchmark 的測試,最後附上我寫的測試程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
<?php require('/usr/share/php/Benchmark/Timer.php'); $timer = new Benchmark_Timer(); $timer->start(); class TestCase { public static function get_something_static($string) { return sha1($string); } public function get_something($string) { return sha1($string); } } $timer->setMarker('StaticMethodStart'); for($i = 0; $i < 1000; $i++) $str = TestCase::get_something_static('Hello World'); $timer->setMarker('StaticMethodStop'); $timer->setMarker('MethodStart'); for($i = 0; $i< 1000; $i++) $str = TestCase::get_something('Hello World'); $timer->setMarker('MethodStop'); $timer->setMarker('PrintStart'); for($i = 0; $i< 1000; $i++) print('Hello World'); $timer->setMarker('PrintStop'); $timer->setMarker('EchoStart'); for($i = 0; $i< 1000; $i++) echo 'Hello World'; $timer->setMarker('EchoStop'); $timer->setMarker('Start: Echo + , '); for($i = 0; $i< 1000; $i++) echo 'Hello World' . 'Hello World' . 'Hello World' . 'Hello World'; $timer->setMarker('End: Echo + , '); $timer->setMarker('Start: Echo + . '); for($i = 0; $i< 1000; $i++) echo 'Hello World', 'Hello World', 'Hello World' , 'Hello World'; $timer->setMarker('End: Echo + . '); $timer->setMarker('Start: Switch'); for($i = 0; $i< 10000; $i++) { $k = 3; // try test $k="asdad"; switch ($k) { case 0: echo "i equals 0"; break; case 1: echo "i equals 1"; break; case 2: echo "i equals 2"; break; } } $timer->setMarker('End: Switch'); $timer->setMarker('Start: if'); for($i = 0; $i< 10000; $i++) { $k = 3; // try test $k="asdad"; if ($k == 0) echo "i equals 0"; elseif ($k == 1) echo "i equals 1"; elseif ($k == 2) echo "i equals 2"; } $timer->setMarker('End: if'); $timer->setMarker('suppress start'); for($i = 0; $i< 100; $i++) @include("example.txt"); $timer->setMarker('suppress end'); $timer->setMarker('no suppress start'); for($i = 0; $i< 100; $i++) include("example.txt"); $timer->setMarker('no suppress end'); $timer->setMarker('start string convert'); $row = array('id' => 'value'); for($i = 0; $i< 10; $i++) print $row[id]; $timer->setMarker('end string convert'); $timer->setMarker('start no string convert'); $row = array('id' => 'value'); for($i = 0; $i< 10; $i++) print $row['id']; $timer->setMarker('end no string convert'); for($i = 0; $i < 1000; $i++) { $row[$i] = $i; } $timer->setMarker('start no functions inside of for loop'); for($i = 0; $i < 1000; $i++) echo $i; $timer->setMarker('end no functions inside of for loop'); $timer->setMarker('start using functions inside of for loop'); function f_count($array) { echo 'call f_count();'; return count($array);} for($i = 0; $i < f_count($row); $i++) echo $i; $timer->setMarker('end using functions inside of for loop'); $foo = "Hello World."; $timer->setMarker('start strlen'); for($i = 0; $i < 1000; $i++) if (strlen($foo) < 5) { echo "Foo is too short"; } $timer->setMarker('end strlen'); $timer->setMarker('start isset'); for($i = 0; $i < 1000; $i++) if ( ! isset($foo{5})) { echo "Foo is too short"; } $timer->setMarker('end isset'); $timer->setMarker('start declare many methods'); class TestCase2 { public function get_something1($string) { return sha1($string);} public function get_something2($string) { return sha1($string);} public function get_something3($string) { return sha1($string);} public function get_something4($string) { return sha1($string);} public function get_something5($string) { return sha1($string);} public function get_something6($string) { return sha1($string);} public function get_something7($string) { return sha1($string);} public function get_something8($string) { return sha1($string);} public function get_something9($string) { return sha1($string);} public function get_something10($string) { return sha1($string);} public function get_something11($string) { return sha1($string);} public function get_something12($string) { return sha1($string);} public function get_something13($string) { return sha1($string);} public function get_something14($string) { return sha1($string);} public function get_something15($string) { return sha1($string);} public function get_something16($string) { return sha1($string);} public function get_something17($string) { return sha1($string);} public function get_something18($string) { return sha1($string);} public function get_something19($string) { return sha1($string);} public function get_something20($string) { return sha1($string);} public function get_something21($string) { return sha1($string);} public function get_something22($string) { return sha1($string);} public function get_something23($string) { return sha1($string);} public function get_something24($string) { return sha1($string);} public function get_something25($string) { return sha1($string);} public function get_something26($string) { return sha1($string);} public function get_something27($string) { return sha1($string);} public function get_something28($string) { return sha1($string);} public function get_something29($string) { return sha1($string);} public function get_something30($string) { return sha1($string);} public function get_something31($string) { return sha1($string);} public function get_something32($string) { return sha1($string);} public function get_something33($string) { return sha1($string);} public function get_something34($string) { return sha1($string);} public function get_something35($string) { return sha1($string);} public function get_something36($string) { return sha1($string);} public function get_something37($string) { return sha1($string);} public function get_something38($string) { return sha1($string);} public function get_something39($string) { return sha1($string);} public function get_something40($string) { return sha1($string);} public function get_something41($string) { return sha1($string);} public function get_something42($string) { return sha1($string);} public function get_something43($string) { return sha1($string);} public function get_something44($string) { return sha1($string);} public function get_something45($string) { return sha1($string);} public function get_something46($string) { return sha1($string);} public function get_something47($string) { return sha1($string);} public function get_something48($string) { return sha1($string);} public function get_something49($string) { return sha1($string);} public function get_something50($string) { return sha1($string);} } for($i = 0; $i < 1000; $i++) TestCase2::get_something1('Hello World'); $timer->setMarker('end declare many methods'); $timer->setMarker('start declare one method'); class TestCase3 { public function get_something1($string) { return sha1($string);} } for($i = 0; $i < 1000; $i++) TestCase3::get_something1('Hello World'); $timer->setMarker('end declare one method'); $timer->setMarker('start define redundancy variable'); class TestCase4 { var $test1; protected $test2; public $test3; private $test4; var $test5; protected $test6; public $test7; private $test8; var $test9; protected $test10; public $test11; private $test12; // try to remove variables in this method and run it again. public function get_something($string) { (int) $test; (int) $test2; (int) $test3; (int) $test4; (int) $test5; return sha1($string);} } for($i = 0; $i < 1000; $i++) TestCase4::get_something('Hello World'); $timer->setMarker('end define redundancy variable'); $timer->setMarker('start no variable'); class TestCase5 { public function get_something($string) { return sha1($string);} } for($i = 0; $i < 1000; $i++) TestCase5::get_something('Hello World'); $timer->setMarker('end no variable'); $timer->timeElapsed('StaticMethodStart', 'StaticMethodStop') . "\n"; $timer->timeElapsed('MethodStart', 'MethodStop') . "\n"; $timer->timeElapsed('PrintStart', 'PrintStop') . "\n"; $timer->timeElapsed('EchoStart', 'EchoStop') . "\n"; $timer->timeElapsed('Start: Echo + , ', 'End: Echo + , ') . "\n"; $timer->timeElapsed('Start: Echo + . ', 'End: Echo + . ') . "\n"; $timer->timeElapsed('Start: Switch', 'End: Switch') . "\n"; $timer->timeElapsed('Start: if', 'End: if') . "\n"; $timer->timeElapsed('suppress start', 'suppress end') . "\n"; $timer->timeElapsed('no suppress start', 'no suppress end') . "\n"; $timer->timeElapsed('start string convert', 'start no string convert') . "\n"; $timer->timeElapsed('start no string convert', 'end no string convert') . "\n"; $timer->timeElapsed('start using functions inside of for loop', 'end using functions inside of for loop') . "\n"; $timer->timeElapsed('start no functions inside of for loop', 'end no functions inside of for loop') . "\n"; $timer->timeElapsed('start strlen', 'end strlen') . "\n"; $timer->timeElapsed('start isset', 'end isset') . "\n"; $timer->timeElapsed('start declare many methods', 'end define redundancy variable') . "\n"; $timer->timeElapsed('start declare one method', 'end declare one method') . "\n"; $timer->timeElapsed('start define redundancy variable', 'end strlen') . "\n"; $timer->timeElapsed('start no variable', 'end no variable') . "\n"; $timer->stop(); $timer->display(); |
測試環境
marker time index ex time perct --------------------------------------------------------------------------------------- Start 1271166102.63234900 - 0.00% --------------------------------------------------------------------------------------- StaticMethodStart 1271166102.63238600 0.000037 0.03% --------------------------------------------------------------------------------------- StaticMethodStop 1271166102.63430300 0.001917 1.46% --------------------------------------------------------------------------------------- MethodStart 1271166102.63431500 0.000012 0.01% --------------------------------------------------------------------------------------- MethodStop 1271166102.63781100 0.003496 2.66% --------------------------------------------------------------------------------------- PrintStart 1271166102.63782000 0.000009 0.01% --------------------------------------------------------------------------------------- PrintStop 1271166102.65499800 0.017178 13.09% --------------------------------------------------------------------------------------- EchoStart 1271166102.65501200 0.000014 0.01% --------------------------------------------------------------------------------------- EchoStop 1271166102.66703400 0.012022 9.16% --------------------------------------------------------------------------------------- Start: Echo + , 1271166102.66704600 0.000012 0.01% --------------------------------------------------------------------------------------- End: Echo + , 1271166102.68043400 0.013388 10.20% --------------------------------------------------------------------------------------- Start: Echo + . 1271166102.68044700 0.000013 0.01% --------------------------------------------------------------------------------------- End: Echo + . 1271166102.69948600 0.019039 14.50% --------------------------------------------------------------------------------------- Start: Switch 1271166102.69951200 0.000026 0.02% --------------------------------------------------------------------------------------- End: Switch 1271166102.70379900 0.004287 3.27% --------------------------------------------------------------------------------------- Start: if 1271166102.70381000 0.000011 0.01% --------------------------------------------------------------------------------------- End: if 1271166102.70807200 0.004262 3.25% --------------------------------------------------------------------------------------- suppress start 1271166102.70808100 0.000009 0.01% --------------------------------------------------------------------------------------- suppress end 1271166102.71372200 0.005641 4.30% --------------------------------------------------------------------------------------- no suppress start 1271166102.71373400 0.000012 0.01% --------------------------------------------------------------------------------------- no suppress end 1271166102.72371200 0.009978 7.60% --------------------------------------------------------------------------------------- start string convert 1271166102.72373200 0.000020 0.02% --------------------------------------------------------------------------------------- end string convert 1271166102.72392700 0.000195 0.15% --------------------------------------------------------------------------------------- start no string convert 1271166102.72393900 0.000012 0.01% --------------------------------------------------------------------------------------- end no string convert 1271166102.72407900 0.000140 0.11% --------------------------------------------------------------------------------------- start no functions inside of for loop 1271166102.72462000 0.000541 0.41% --------------------------------------------------------------------------------------- end no functions inside of for loop 1271166102.73778500 0.013165 10.03% --------------------------------------------------------------------------------------- start using functions inside of for loop 1271166102.73779700 0.000012 0.01% --------------------------------------------------------------------------------------- end using functions inside of for loop 1271166102.74494600 0.007149 5.45% --------------------------------------------------------------------------------------- start strlen 1271166102.74495800 0.000012 0.01% --------------------------------------------------------------------------------------- end strlen 1271166102.74547700 0.000519 0.40% --------------------------------------------------------------------------------------- start isset 1271166102.74548900 0.000012 0.01% --------------------------------------------------------------------------------------- end isset 1271166102.74571200 0.000223 0.17% --------------------------------------------------------------------------------------- start declare many methods 1271166102.74572100 0.000009 0.01% --------------------------------------------------------------------------------------- end declare many methods 1271166102.74899700 0.003276 2.50% --------------------------------------------------------------------------------------- start declare one method 1271166102.74900600 0.000009 0.01% --------------------------------------------------------------------------------------- end declare one method 1271166102.75232400 0.003318 2.53% --------------------------------------------------------------------------------------- start define redundancy variable 1271166102.75233400 0.000010 0.01% --------------------------------------------------------------------------------------- end define redundancy variable 1271166102.75980700 0.007473 5.69% --------------------------------------------------------------------------------------- start no variable 1271166102.75981600 0.000009 0.01% --------------------------------------------------------------------------------------- end no variable 1271166102.76346600 0.003650 2.78% --------------------------------------------------------------------------------------- Stop 1271166102.76362100 0.000155 0.12% --------------------------------------------------------------------------------------- total - 0.131272 100.00%
echo語句 “不要求返回任何數值” 為最主要的差異 …
所以運行速度要略微快於print
PHP代碼中可以把print作為一個普通函數來使用
$rs = print “Hello World”;
$rs的值將為1
超級贊的
我也對40 Tips for optimizing your php code 有很大的疑問
看到實際抄做範例
只能說贊
我想環境跟設定影響應該滿大的,我自己是用 Fedora 12 + PHP 5.3.1 跑的結果大部分跟那篇文章描述的一樣… 😳
您這麼一說,我補跑了另一個測試環境,果然和您說的一樣,兩個環境有些數據是相反的,我得要研究一下是什麼造成的…
翻譯 錯誤訊息代價昂貴結論
請對照上一條原則.. XD ((這有誤 有去聽過PHP大師那場 才會懂他所謂的昂貴
請看這頁 http://talks.php.net/show/ntu/25
i see, 「錯誤訊息代價昂貴」是對的,避免錯誤或警告可以節省運算時間。這邊「請對照上一條原則.. XD」的意思是是因為上一條原則指出「用@屏蔽錯誤消息的做法非常慢」,可是實際上不用 @ 會更慢,所以兩者有點自打嘴巴的嫌疑 😛
雖然我不太清楚 @ 的實做方式 (是否因為加了 @ 而繞過 php_verror() 等 function , 或許晚點可以用 Callgrind 驗證看看).. 😀
“It’s better to use switch statements than multi if, else if, statements” 應該是為了 debugging 與 maintenance … 🙄
如果是從這個角度來看 Switch Case 的敘述確比較好看一些