2017-07-11 65 views
8

Moja aplikacja szeroko korzysta z funkcji ciągów mb_, a przejście na php 7 spowodowało ogólnie wolniejszą aplikację. Wyszukałem problemy do funkcji ciągów mb_. Oto kod odniesienia, a wyniki:Funkcje php 7 mb_ (wielobajtowe) są ~ 60% wolniejsze niż w 5.3 (tylko wydanie 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/>"; 

Platforma Windows 7 64bit, 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 

Nie wiem, czy mój zestaw się to źle czy coś, ale wydaje się nie do pomyślenia że funkcje wielobajtowe powinny działać wolniej. Wszelkie pomysły dotyczące tego, co i jak zrobić, aby rozwiązać ten problem? Z góry dziękuję.

Edycja: jak sugeruje apokryfos, może to być problem wyłącznie dla systemu Windows.

+0

Niestety, ja po prostu nie widzę go http://sandbox.onlinephpfunctions.com/code/401f138baf7c4110f1370f8e597bba5610dd0a47 – apokryfos

+0

@apokryfos nie wiem co OS uruchamia podany link testowy, być może jest to problem z wersją systemu Windows php – MIrrorMirror

+2

Tylko dla czytelności: 'microtime' pobiera argument boolean, który sprawia, że ​​zwraca już float - nie ma potrzeby" eksplodować "itd. - Myśląc o tym: To może być cały problem, czyli '$ time = explode ('', $ time); $ start = $ time [1] + $ time [0]; 'ma reprezentować? Po prostu dodajesz część msec aktualnego znacznika czasu do części sekundowej? – ccKep

Odpowiedz

3

Mogę potwierdzić, że wynik jest powtarzalny na Windows 7. Po kilku eksperymentach, znalazłem szybkie rozwiązanie, które IMO nie powinno mieć żadnego efektu.

Jak widać z mb_strlen(), funkcja podpis, użyje wewnętrznego kodowania, jeśli nie podasz parametru kodowania. Dotyczy to również innych używanych funkcji.

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

Co znalazłem dziwne jest, jeśli ustawienie wewnętrznego kodowania na UTF-8 przez wywołanie mb_internal_encoding("UTF-8") i pominąć parametr kodowania funkcje dostał szybciej.

PHP 5.5 Wynik:

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 Wynik:

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 

Niestety, szybkie rozwiązanie nie jest idealne, jako mb_stripos() i mb_strripos() wydają się nie mieć wpływu. Nadal są powolne.

Jest to kod (skrócony):

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

Wow, to dziwne – hanshenrik

+0

ktoś proszę zgłoś zgłoszenie błędu, to musi być błąd – hanshenrik

+0

@hanshenrik napisał jeden dla tego, jak również https: // bugs. php.net/bug.php?id=74935 – MIrrorMirror

3

to brzmi jak "błąd regresji wydajności". prawdopodobnie powinien zgłosić zgłoszenie błędu, więc twórcy PHP mogą go obejrzeć, na stronie bugs.php.net

tymczasem widzę, że w swoich fragmentach używasz wyłącznie UTF-8. dopóki używasz wyłącznie UTF-8, możesz przyspieszyć użycie preg_, który obsługuje tylko jeden rodzaj zestawu znaków Unicode: UTF-8. oto moja próba:

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]; 
} 

oto moje wyniki testów na 100.000 iteracji o PHP 7.0 na debian 9 Linux (kernel 4.9):

mb_strlen dostaje wolniejszy od około 60ms do 100 ms

mb_stripos dostali dużo szybciej od około 1400ms do 75ms

mb_substr ma dużo wolniejszy, od około 47 ms do około 800 ms

  • ale proponuję ponownie uruchomić te testy na Windows, jak mówiłeś, że może to być okna wyłączności problem

również pamiętać, że funkcje te nie są wyposażone w kompletne, jak widać z LogicException użytkownika oni rzucają.

również pamiętać, że ze względu na ograniczenia w preg_ musiałem cap mb_substr na 65000 iteracji w

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

ponieważ, jeśli zapytać preg szukać ciąg długo ponad 65000 znaków, to daje błąd...

również pamiętać, że kod odniesienia można dużo łatwiej, wszystko to

$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/>"; 

może być po prostu zastąpione

$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/>"; 

który wyprowadza coś takiego: mb_strlen: 0.085 seconds (czyli o 85 milisekund)

lub

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

(i mogę wziąć dziką przypuszczenie, że ma coś wspólnego z realloc() wydajności, w których Linux tupie okna, ale nie mam dowodu)

+0

dziękuję za odpowiedź. tylko komentarz do kodu optymalizującego czas pomiaru: powód jest taki, jaki jest, i nie został zoptymalizowany tak jak Ty i inni sugerowaliście, jest to, że rozkłada się na t> 1sek (pokazuje wartości ujemne itp.) – MIrrorMirror

+1

Oh, żeby to obejść możesz użyć number_format() :) (Jestem teraz na telefonie, więc nie naprawię go, ale kiedy wrócę do komputera, to zrobię) – hanshenrik

+1

@MIrrorMirror naprawił to za pomocą number_format ^^ (a jeśli nie chce innych operacji formatowania number_format, po prostu daj mu 2 argumenty pustego łańcucha na końcu) – hanshenrik