2017-07-11 14 views
8

我的應用程序廣泛使用mb_字符串函數,切換到php 7導致應用程序整體速度較慢。我跟蹤了mb_字符串函數的問題。下面是基準代碼和結果:php 7 mb_(多字節)函數比5.3版本慢60%(僅限Windows問題)

$time = microtime(); 
$time = explode(' ', $time); 
$start = $time[1] + $time[0]; 
$startms = $time[0]; 
    for ($i=0; $i<100000; $i++) { 
     $a = mb_strlen("fdsfdssdfoifjosdifjosdifjosdij:ά", "UTF-8"); 
    } 
$time = microtime(); 
$time = explode(' ', $time); 
$finish = $time[1] + $time[0]; 
$finishms = $time[0]; 
$total_time = round(($finish - $start), 4); 
echo "mb_strlen: " . $total_time*1000 ." milliseconds<br/>"; 

$time = microtime(); 
$time = explode(' ', $time); 
$start = $time[1] + $time[0]; 
$startms = $time[0]; 
    for ($i=0; $i<100000; $i++) { 
     $a = mb_stripos("fdsfdssdfoifjosdifjosdifjosdij:ά", "α", 0, "UTF-8"); 
    } 
$time = microtime(); 
$time = explode(' ', $time); 
$finish = $time[1] + $time[0]; 
$finishms = $time[0]; 
$total_time = round(($finish - $start), 4); 
echo "mb_stripos: " . $total_time*1000 ." milliseconds<br/>"; 


$time = microtime(); 
$time = explode(' ', $time); 
$start = $time[1] + $time[0]; 
$startms = $time[0]; 
    for ($i=0; $i<100000; $i++) { 
     $a = mb_substr("fdsfdssdfoifjosdifjosdifjosdij:ά", $i, 1, "UTF-8"); 
    } 
$time = microtime(); 
$time = explode(' ', $time); 
$finish = $time[1] + $time[0]; 
$finishms = $time[0]; 
$total_time = round(($finish - $start), 4); 
echo "mb_substr: " . $total_time*1000 ." milliseconds<br/>"; 

平臺爲Windows 7 64位,IIS 7.5:

php 5.3.28 
mb_strlen: 250 milliseconds 
mb_stripos: 3078.1 milliseconds 
mb_substr: 281.3 milliseconds 

php 7.1.1 
mb_strlen: 406.3 milliseconds 
mb_stripos: 4796.9 milliseconds 
mb_substr: 421.9 milliseconds 

我不知道如果我的設立是錯了或什麼,但似乎是不可思議多字節函數應該更慢。任何想法爲什麼和如何解決這個問題?先謝謝你。

編輯:正如apokryfos的評論所暗示的,這可能是Windows唯一的問題。

+0

對不起,我只是沒有看到它http://sandbox.onlinephpfunctions.com/code/401f138baf7c4110f1370f8e597bba5610dd0a47 – apokryfos

+0

@apokryfos我不知道是什麼操作系統運行你提供的測試鏈接,也許這是一個與windows版本的問題php – MIrrorMirror

+2

只是爲了可讀性:'microtime'需要一個布爾參數,它已經返回一個浮點數 - 不需要'爆炸'等 - 思考它:這可能是整個問題,'$ time = explode('',$ time); $ start = $ time [1] + $ time [0];'應該表示?你只是將當前時間戳的msec部分添加到秒部分? – ccKep

回答

3

我可以確認,你的結果是可重複的Windows 7. 經過一些實驗後,我發現一個快速解決方案,IMO甚至不應該有效果。

正如你可以從mb_strlen()函數簽名看到的, 如果你省略編碼參數,它將使用內部編碼。 這也適用於您使用的其他功能。

mixed mb_strlen (string $str [, string $encoding = mb_internal_encoding() ]) 

我發現奇怪的是,如果你通過調用mb_internal_encoding("UTF-8")設置內部編碼爲UTF-8和省略的編碼參數, 功能得到更快。

PHP 5.5的結果:

5.5.12 

with encoding parameter: 
- mb_strlen: 172 ms, result: 5 
- mb_substr: 218 ms, result: う 
- mb_strpos: 218 ms, result: 3 
- mb_stripos: 1,669 ms, result: 3 
- mb_strrpos: 234 ms, result: 3 
- mb_strripos: 1,685 ms, result: 3 

with internal encoding: 
- mb_strlen: 47 ms, result: 5 
- mb_substr: 78 ms, result: う 
- mb_strpos: 62 ms, result: 3 
- mb_stripos: 1,669 ms, result: 3 
- mb_strrpos: 94 ms, result: 3 
- mb_strripos: 1,669 ms, result: 3 

PHP 7.0的結果:

7.0.12 

with encoding parameter: 
- mb_strlen: 640 ms, result: 5 
- mb_substr: 702 ms, result: う 
- mb_strpos: 686 ms, result: 3 
- mb_stripos: 7,067 ms, result: 3 
- mb_strrpos: 749 ms, result: 3 
- mb_strripos: 7,130 ms, result: 3 

with internal encoding: 
- mb_strlen: 31 ms, result: 5 
- mb_substr: 31 ms, result: う 
- mb_strpos: 47 ms, result: 3 
- mb_stripos: 7,270 ms, result: 3 
- mb_strrpos: 62 ms, result: 3 
- mb_strripos: 7,116 ms, result: 3 

不幸的是,這種快速的解決方案是不完美的,因爲mb_stripos()mb_strripos()似乎並沒有受到影響。 他們仍然很慢。

這是代碼(縮短):

echo PHP_VERSION."\n"; 
echo "\nwith encoding parameter:\n"; 

$t = microtime(true)*1000; 
for($i=0; $i<100000; $i++){ 
    $n = mb_strlen("あえいおう","UTF-8"); 
} 
$t = microtime(true)*1000-$t; 
echo "- mb_strlen: ".number_format($t)." ms, result: {$n}\n"; 

$t = microtime(true)*1000; 
for($i=0; $i<100000; $i++){ 
    $n = mb_substr("あえいおう",-1,1,"UTF-8"); 
} 
$t = microtime(true)*1000-$t; 
echo "- mb_substr: ".number_format($t)." ms, result: {$n}\n"; 

//set internal encoding 
//and omit encoding parameter 

mb_internal_encoding("UTF-8"); 
echo "\nwith internal encoding:\n"; 

$t = microtime(true)*1000; 
for($i=0; $i<100000; $i++){ 
    $n = mb_strlen("あえいおう"); 
} 
$t = microtime(true)*1000-$t; 
echo "- mb_strlen: ".number_format($t)." ms, result: {$n}\n"; 

$t = microtime(true)*1000; 
for($i=0; $i<100000; $i++){ 
    $n = mb_substr("あえいおう",-1,1); 
} 
$t = microtime(true)*1000-$t; 
echo "- mb_substr: ".number_format($t)." ms, result: {$n}\n"; 
+0

哇,這很奇怪 – hanshenrik

+0

某人請提交一個錯誤報告,這必須是一個錯誤 – hanshenrik

+0

@hanshenrik爲此提交了一個以及https:// bugs。 php.net/bug.php?id=74935 – MIrrorMirror

3

這聽起來像是一個「性能倒退」錯誤。應該可能提交一個bug報告,所以php核心開發人員可以看看它,在bugs.php.net

與此同時,我看到在你的片段中,你完全使用UTF-8。只要你專門使用UTF-8,你可以用preg_來加速它,它只支持1種unicode字符集:UTF-8。這裏是我的嘗試:

function _mb_strlen(string $str, string $encoding = 'UTF-8'): int { 
    assert ($encoding === 'UTF-8'); 
    preg_match ('/.$/u', $str, $matches, PREG_OFFSET_CAPTURE); 
    return empty ($matches) ? 0 : ($matches [0] [1]) + 1; 
} 
function _mb_stripos(string $haystack, string $needle, int $offset = 0, string $encoding = 'UTF-8') { 
    assert ($encoding === 'UTF-8'); 
    if ($offset !== 0) { 
     throw new LogicException ('NOT IMPLEMENTED'); 
    } 
    preg_match ('/' . preg_quote ($needle) . '/ui', $haystack, $matches, PREG_OFFSET_CAPTURE); 
    return empty ($matches) ? false : $matches [0] [1]; 
} 
function _mb_substr(string $str, int $start, int $length = NULL, string $encoding = 'UTF-8'): string { 
    assert ($encoding === 'UTF-8'); 
    if ($start < 0) { 
     throw new LogicException ('NOT IMPLEMENTED'); 
    } elseif ($start > 0) { 
     $rex = '/.{' . $start . '}(.{0,'; 
    } else { 
     $rex = '/(.{0,'; 
    } 
    if ($length !== NULL) { 
     $rex .= $length; 
    } 
    $rex .= '})/u'; 
    preg_match ($rex, $str, $matches); 
    // var_dump ($rex, $matches); 
    return empty ($matches) ? '' : $matches [1]; 
} 

這裏是在Debian 9的Linux上的PHP 7.0 10萬次迭代我的基準測試結果(內核4.9):

mb_strlen越來越慢,約60ms的100毫秒

mb_stripos了速度快了很多,約1400ms,以75ms

mb_substr得了慢了很多,從約47毫秒到約800毫秒

  • 但我建議你重新在Windows上運行這些測試,因爲你說你認爲這可能是一個Windows的獨家發行

也注意到,這些功能都沒有完整的功能,你可以從LogicException來看看他們扔。

也注意到,由於參看preg_的限制,我不得不在65000次迭代

for($i = 0; $i < 65000; $i ++) { 
    $a = mb_substr ("fdsfdssdfoifjosdifjosdifjosdij:ά", $i, 1, "UTF-8"); 
} 

封頂mb_substr因爲,如果你問預浸尋找一個字符串長超過65000個字符,它會給出一個錯誤...

也注意到,您的基準測試的代碼可以進行輕鬆了許多,這一切

$time = microtime(); 
$time = explode(' ', $time); 
$start = $time[1] + $time[0]; 
$startms = $time[0]; 
    for ($i=0; $i<100000; $i++) { 
     $a = mb_strlen("fdsfdssdfoifjosdifjosdifjosdij:ά", "UTF-8"); 
    } 
$time = microtime(); 
$time = explode(' ', $time); 
$finish = $time[1] + $time[0]; 
$finishms = $time[0]; 
$total_time = round(($finish - $start), 4); 
echo "mb_strlen: " . $total_time*1000 ." milliseconds<br/>"; 

可以簡單地用

$starttime=microtime(true); 
    for ($i=0; $i<100000; $i++) { 
     $a = mb_strlen("fdsfdssdfoifjosdifjosdifjosdij:ά", "UTF-8"); 
    } 
$endtime=microtime(true); 
echo "mb_strlen: " . number_format(($endtime-$starttime),3) ." seconds<br/>"; 

取代其輸出是這樣的:mb_strlen: 0.085 seconds (這意味着約85毫秒)

echo "mb_strlen: " . number_format(($endtime - $starttime) * 1000),2) . " milliseconds<br/>"; 

(我可以猜猜它有事情做與realloc()的性能,在linux下重踏窗口,但我沒有證據)

+0

謝謝你的回覆。只是對時間測量優化代碼的評論:它是原樣,而不是像你和其他人一樣優化的原因是它在t> 1秒時出現故障(顯示負值等) – MIrrorMirror

+1

哦,爲了解決這個問題你可以使用number_format():)(我現在使用手機,所以我不會修復它,但當我回到電腦時,我會) – hanshenrik

+1

@MIrrorMirror使用number_format ^^修復它(如果你沒有想要number_format的其他格式化操作,只需在最後給它2個空字符串參數) – hanshenrik