2017-07-11 4 views
8

Mon application utilise largement les fonctions mb_ chaîne et le passage à php 7 a entraîné une application globalement plus lente. J'ai repéré les problèmes dans les fonctions de chaîne mb_. Voici le code de référence et les résultats:php 7 mb_ (multi-octets) fonctions sont ~ 60% plus lent que dans 5.3 (Windows seulement question)

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

La plate-forme est Windows 7 64 bits, 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 

Je ne sais pas si mon mise en place est mauvais ou quelque chose, mais il semble inconcevable que les fonctions multi-octets devraient être plus lentes. Des idées sur pourquoi et quoi faire pour résoudre cela? Merci d'avance. Edit: comme le suggère le commentaire d'apokryfos, cela peut être un problème Windows seulement.

+0

Désolé, je ne vois pas ce http://sandbox.onlinephpfunctions.com/code/401f138baf7c4110f1370f8e597bba5610dd0a47 – apokryfos

+0

@apokryfos Je ne sais pas quel système d'exploitation exécute le lien de test que vous avez fourni, c'est peut-être un problème avec la version Windows de php – MIrrorMirror

+2

Juste pour la lisibilité: 'microtime' prend un argument booléen qui le fait retourner un flottant déjà - pas besoin de' explode' etc. - Penser à cela: C'est peut-être tout le problème, qu'est-ce que '$ time = explode ('', $ time); $ start = $ time [1] + $ time [0]; 'censé représenter? Vous ajoutez simplement la partie msec de l'horodatage actuel à la partie secondes? – ccKep

Répondre

3

Je peux confirmer que le résultat est reproductible sur Windows 7. Après quelques expériences, j'ai trouvé une solution rapide que IMO ne devrait même pas avoir un effet.

Comme vous pouvez le voir à partir de la signature de fonction mb_strlen(), il utilisera le codage interne si vous omettez le paramètre de codage. Cela s'applique également aux autres fonctions que vous utilisez.

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

Ce que je trouve étrange est si vous définissez le codage interne UTF-8 en appelant mb_internal_encoding("UTF-8") et omettez le paramètre d'encodage, les fonctions sont devenus plus rapides.

PHP 5.5 Résultat:

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 résultat:

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 

Malheureusement, cette solution rapide n'est pas parfait comme mb_stripos() et mb_strripos() ne semblent pas être affectés. Ils sont encore lents.

Ce code (raccourci):

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 c'est bizarre – hanshenrik

+0

quelqu'un s'il vous plaît déposer un rapport de bug, ce doit être un bug – hanshenrik

+0

@hanshenrik a déposé un pour cela aussi https: // bugs. php.net/bug.php?id=74935 – MIrrorMirror

3

cela ressemble à un bug de "régression de performance". devrait probablement déposer un rapport de bug, de sorte que les principaux développeurs PHP peuvent y jeter un coup d'œil, à bugs.php.net

pendant ce temps, je vois que dans vos extraits vous utilisez exclusivement UTF-8. Tant que vous utilisez exclusivement UTF-8, vous pourrez peut-être l'accélérer en utilisant preg_, qui ne supporte qu'un type de caractères Unicode: UTF-8. voici ma tentative:

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

voici mes résultats de référence sur 100.000 itérations sur php 7.0 sur debian 9 linux (kernel 4.9):

mb_strlen obtenu plus lent, d'environ 60 ms à 100 ms

mb_stripos obtenu BEAUCOUP PLUS rAPIDE, d'environ 1400ms à 75ms

mb_substr a BEAUCOUP PLUS LENT, d'environ 47 ms à environ 800 ms

  • mais je vous suggère de ré-exécuter ces tests sur les fenêtres, comme vous le dites-vous que cela pourrait être un windows-exclusif problème

noter également, ces fonctions ne sont pas complète des fonctionnalités, comme vous pouvez le voir de la LogicException Ils jettent.

noter également que, en raison d'une limitation dans preg_, je devais plafonner mb_substr à 65000 itérations

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

parce que, si vous demandez preg de chercher une chaîne longue de plus de 65 000 caractères, il donnera une Erreur...

noter également que votre code de référence peut être beaucoup plus facile, tout cela

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

peut simplement être remplacé par

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

qui sort quelque chose comme: mb_strlen: 0.085 seconds (ce qui veut dire à propos de 85 millisecondes)

ou

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

(et je peux prendre une conjecture sauvage qu'il a quelque chose à voir avec la performance realloc(), dans lequel linux piétine windows, mais je m'y suis pas de preuve)

+0

merci pour votre réponse. juste un commentaire pour le code d'optimisation de mesure du temps: la raison pour laquelle il est tel qu'il est, et non optimisé comme vous et les autres, est qu'il se décompose pour t> 1sec (montre des valeurs négatives etc.) – MIrrorMirror

+1

Oh, pour contourner ça vous pouvez utiliser number_format() :) (je suis sur un téléphone maintenant je ne vais pas le réparer, mais quand je reviens à l'ordinateur, je le ferai) – hanshenrik

+1

@MIrrorMirror l'a fixé avec number_format ^^ (et si vous ne le faites pas voulez les autres opérations de formatage de number_format, donnez simplement 2 arguments emptystring à la fin) – hanshenrik