web-dev-qa-db-fra.com

PHP facteur 30 différence de performance de Linux à Windows

Notre équipe travaille à développer WordPress plugins et fournit des instances hébergées sur quelques serveurs indépendants. Notre installation WordPress est gérée par Git, tous les serveurs ont le même source & WordPress installation déployée, seuls les domaines et les données réelles dans la base de données varient. Pour chaque installation, MySql s'exécute sur le même hôte. WordPress fonctionne exclusivement sur chaque serveur.

Cependant, après avoir déployé cette configuration sur un Windows Server 2008 RC2, nous avons remarqué une différence de performances drastique par rapport à nos autres serveurs: le temps de génération de page augmente par rapport à la moyenne. 400ms à 4000-5000ms pour les pages générées avec PHP. Pour les ressources statiques fournies par Apache uniquement, la vitesse est à peu près la même que sur Linux.

Nous avons donc pris quelques mesures pour réduire le problème:

  1. Assurez-vous qu'aucun logiciel antivir n'est en cours d'exécution ou que d'autres éléments du domaine Windows interfèrent
  2. Collectez des données de profilage pour identifier les tueurs de temps pendant l'exécution du script
  3. Testez différentes configurations de serveur et de matériel
  4. Vérifiez à la fois Apache et PHP pour les erreurs de configuration évidentes

Après quelques profils, nous avons rapidement remarqué que l'évaluation des expressions régulières était horriblement lente sur nos machines Windows. L'évaluation de 10 000 expressions régulières (preg_match) Prend environ 90 ms sous Linux et 3000 ms sous Windows.

Le profilage, les tests du système et les détails de configuration sont fournis ci-dessous. Nous ne voulons pas optimiser ce script (ce que nous savons faire). Nous voulons que le script s'exécute approximativement à la même vitesse sur Windows que sur Linux (étant donné la même configuration concernant opcache/...). Pas besoin d'optimiser l'empreinte mémoire du script également.

Mise à jour: Après un certain temps, les systèmes semblent manquer de mémoire, déclenchant des exceptions de mémoire et des allocations aléatoires. Voir ci-dessous pour plus de détails. Le redémarrage d'Apache/PHP a résolu le problème pour l'instant.

La trace de _get_browser Est:

File (called from)
require wp-blog-header.php (index.php:17)
wp (wp-blog-header.php:14)
WP->main (functions.php:808)
php::do_action_ref_array (class-wp.php:616)
php::call_user_func_array (wp-includes/plugin:507)
wp_slimstat::slimtrack  (php::internal (507))
wp_slimstat::_get_browser (wp-slimstat.php:385)

pdate 2: Pour une raison quelconque, je ne me souviens pas que nous ayons recommencé à activer PHP en tant que module Apache sur nos serveurs (les mêmes qui fournissent de mauvaises performances). Mais aujourd'hui, ils fonctionnent à une vitesse fulgurante (~ 1 s/demande). L'ajout d'Opcache ramène cela à ~ 400 ms/demande. Apache/PHP/Windows est resté le même.

1) Résultats de profilage

Le profilage a été effectué avec XDebug sur toutes les machines. Habituellement, nous n'avons collecté que quelques exécutions - celles-ci étaient suffisantes pour révéler l'emplacement où la plupart du temps (50% +) était passé: la méthode [get_browser][1] Du WordPress wp-slimstats:

protected static function _get_browser(){
    // Load cache
    @include_once(plugin_dir_path( __FILE__ ).'databases/browscap.php');
    // browscap.php contains $slimstat_patterns and $slimstat_browsers

    $browser = array('browser' => 'Default Browser', 'version' => '1', 'platform' => 'unknown', 'css_version' => 1, 'type' => 1);
    if (empty($slimstat_patterns) || !is_array($slimstat_patterns)) return $browser;

    $user_agent = isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:'';
    $search = array();
    foreach ($slimstat_patterns as $key => $pattern){
        if (preg_match($pattern . 'i', $user_agent)){
            $search = $value = $search + $slimstat_browsers[$key];
            while (array_key_exists(3, $value) && $value[3]) {
                $value = $slimstat_browsers[$value[3]];
                $search += $value;
            }
            break;
        }
    }

    // Lots of other lines to relevant to the profiling results
  }

Cette fonction similaire à PHP get_browser Détecte les capacités et le système d'exploitation du navigateur. La plupart du temps d'exécution du script est passé dans cette boucle foreach, évaluant tous ces preg_match (~ Environ 8000 - 10000 par demande de page). Cela prend environ 90 ms sous Linux et 3000 ms sous Windows. Les résultats étaient les mêmes sur toutes les configurations testées (l'image montre les données de deux exécutions):

wp_slimstat::_get_browser profiling results on IIS8

Bien sûr, le chargement de deux énormes baies prend du temps. Évaluer également les expressions régulières. Mais nous nous attendons à ce qu'ils prennent environ le même temps sur Linux et Windows. Il s'agit du résultat du profilage sur un linux vm (une seule demande de page). La différence est assez évidente:

enter image description here

Un autre tueur de temps était en fait l'Object-Cache WordPress utilise:

function get( $key, $group = 'default', $force = false, &$found = null ) {
    if ( empty( $group ) )
        $group = 'default';

    if ( $this->multisite && ! isset( $this->global_groups[ $group ] ) )
        $key = $this->blog_prefix . $key;

    if ( $this->_exists( $key, $group ) ) {
        $found = true;
        $this->cache_hits += 1;
        if ( is_object($this->cache[$group][$key]) )
            return clone $this->cache[$group][$key];
        else
            return $this->cache[$group][$key];
    }

    $found = false;
    $this->cache_misses += 1;
    return false;
}

Le temps est consacré à cette fonction elle-même (3 exécutions de script):

enter image description here

Sous Linux:

enter image description here

Le dernier vrai tueur de gros temps a été la traduction. Chaque traduction, chargée depuis la mémoire, prend de 0,2 ms à 4 ms dans WordPress: enter image description here

Sous Linux:

enter image description here

2) Systèmes testés

Afin de nous assurer que la virtualisation ou Apache affectent cela, nous l'avons testé sur plusieurs configurations. Antivir a été désactivé sur toutes les configurations:

  • Linux Debian, Apache 2 & PHP sur les versions stables à jour. C'est la même chose pour les développeurs fonctionnant sur leurs machines virtuelles que pour les serveurs intermédiaires/en direct. Agissant comme un système de référence des performances souhaitées. Soit exécuté dans notre bureau ou dans certains fournisseurs d'hébergement (espace partagé) .Les systèmes Windows avaient entre 4 Go et 8 Go de RAM, en tout temps l'utilisation de la mémoire était inférieure à 50%. Les virtualisations n'exécutent jamais Windows et Apache en même temps.
  • Life-Servers, fonctionnant sur T-Systems (serveurs virtualisés gérés), sur VMWare Player
    • Gagnez 2008 R2. Apache 2.2.25 + PHP 5.4.26 NTS, VC9 en tant que module fastcgi
    • Gagnez 2008 R2. Apache 2.2.25 + PHP 5.5.1 NTS, VC11 en tant que module fastcgi
    • Gagnez 2008 R2. Apache 2.2.25 + PHP 5.5.1 NTS, VC11 comme module Apache
    • Win 2008 R2, Apache 2.2.25 + PHP 5.5.11 TS, VC11 en tant que module Apache (c'est celui rapide que j'ai mentionné dans la mise à jour 2)
  • Sur une machine locale, hôte: OpenSuse, virtualisation: lecteur VMWare, identique à @ T-Systems. Pour éviter que leur infrastructure nous influence:
    • Gagnez 2008 R2. Apache 2.2.25 + PHP 5.4.26 NTS, VC9 en tant que module fastcgi
    • Gagnez 2008 R2. IIS7 + PHP 5.4.26 NTS, VC9 en tant que module fastcgi (avec et sans wincache)
    • Win 2012. IIS * + PHP 5.5.10 NTS, VC11 en tant que module fastcgi (avec et sans wincache)
  • Sur une machine locale sans virtualisation
    • Gagnez 2008 R2. Apache 2.2.25 + PHP 5.4.26 NTS, VC9 en tant que module fastcgi

Les résultats de profilage mentionnés ci-dessus étaient les mêmes sur les différents systèmes (dérivation de ~ 10%). Windows a toujours été un facteur important plus lent que Linux.

L'utilisation d'une nouvelle installation de WordPress & Slimstats a donné environ les mêmes résultats. La réécriture du code n'est pas une option ici.

Mise à jour: Pendant ce temps, nous avons trouvé deux autres systèmes Windows (Windows 2008 R2, VM & Phys) où cette pile complète s'exécute assez rapidement. Même configuration cependant.

pdate 2: Exécuter PHP en tant que module Apache sur les Life-Servers était légèrement plus rapide que la méthode fastcgi: jusqu'à ~ 2sec, 50% de moins.

Manque de mémoire

Après un certain temps, notre Live-Server cesse de fonctionner, déclenchant ces exceptions de mémoire insuffisante:

PHP Fatal error:  Out of memory (allocated 4456448) (tried to allocate 136 bytes)
PHP Fatal error:  Out of memory (allocated 8650752) (tried to allocate 45 bytes) 
PHP Fatal error:  Out of memory (allocated 6815744) (tried to allocate 24 bytes) 

Cela se produit à des emplacements de script aléatoires. De toute évidence, Zend Memory Manager n'est pas en mesure d'allouer plus de mémoire, bien que les scripts soient autorisés à le faire. Au moment de l'incident, le serveur avait environ 50% d'espace libre RAM (2 Go +). Donc, le serveur ne manque pas de RAM. Le redémarrage d'Apache/PHP a résolu ce problème pour l'instant.

Je ne sais pas si ce problème est lié aux problèmes de performances ici. Pourtant, comme les deux problèmes semblent être liés à la mémoire, ils sont inclus ici. En particulier, nous essaierons de reproduire les paramètres des tests Windows qui ont fourni des performances décentes.

) Apache & PHP Configuration

... n'ont probablement pas d'embûches communes. La mise en mémoire tampon de sortie est activée (par défaut), la neutralisation multicolore est désactivée, ... Si des options sont intéressantes, nous les fournirons avec plaisir.

Sortie de httpd.exe -V

Server version: Apache/2.4.7 (Win32)
Apache Lounge VC10 Server built:   Nov 26 2013 15:46:56
Server's Module Magic Number: 20120211:27
Server loaded:  APR 1.5.0, APR-UTIL 1.5.3
Compiled using: APR 1.5.0, APR-UTIL 1.5.3
Architecture:   32-bit
Server MPM:     WinNT
  threaded:     yes (fixed thread count)
    forked:     no
Server compiled with....
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses disabled)
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=256
 -D HTTPD_ROOT="/Apache"
 -D SUEXEC_BIN="/Apache/bin/suexec"
 -D DEFAULT_PIDLOG="logs/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/Apache_runtime_status"
 -D DEFAULT_ERRORLOG="logs/error.log"
 -D AP_TYPES_CONFIG_FILE="conf/mime.types"
 -D SERVER_CONFIG_FILE="conf/httpd.conf"

Configuration mpm_winnt_module:

<IfModule mpm_winnt_module>
    ThreadsPerChild 150
    ThreadStackSize 8388608 
    MaxConnectionsPerChild 0
</IfModule>

Extrait de php.ini:

realpath_cache_size = 12M
pcre.recursion_limit = 100000

4) Raison suspecte actuelle

Ancienne hypothèse:

Les trois exemples reposent fortement sur de grands tableaux et des opérations de chaîne. Cette sorte semble être l'usine commune. Comme l'implémentation fonctionne correctement sous Linux, nous soupçonnons qu'il s'agit d'un problème de mémoire sous Windows. Étant donné qu'il n'y a pas d'interaction avec la base de données aux emplacements précis, nous ne soupçonnons pas que la base de données ou l'intégration du serveur <-> PHP soit le problème. L'interaction de la mémoire de PHP semble en quelque sorte lente . Peut-être y a-t-il quelqu'un qui interfère avec la mémoire de Windows qui ralentit considérablement l'accès?

Ancienne hypothèse 2:

Comme la même pile fonctionne correctement sur d'autres machines Windows, nous supposons que le problème se situe quelque part dans la configuration Windows.

Nouvelle hypothèse 3:

En fait, je suis hors des hypothèses. Pourquoi exécuter PHP beaucoup plus lent que fastcgi puis comme module Apache>

Des idées sur la façon de vérifier cela ou de trouver le vrai problème ici? Toute aide ou directive pour résoudre ce problème est la bienvenue.

36
Fge

Windows a de nombreux services/politiques qui restreignent, empêchent, protègent, contrôlent et etc. l'utilisation de l'ordinateur dans toutes les situations.

Un bon spécialiste certifié Microsoft sera en mesure de résoudre votre question en quelques minutes, car il aura l'expérience pour dire exactement quels paramètres/services/politiques vérifier et désactiver/activer/modifier les paramètres, de sorte que le PHP les scripts sont exécutés plus rapidement.

De ma mémoire, je ne peux que vous suggérer de vérifier tout ce qui concerne la RAM, l'accès au disque dur, les variables environnementales, les limites et la sécurité (comme le pare-feu). Tout ce qui peut affecter l'exécution du script php, en commençant par certaines politiques d'appel de procédure distante et en terminant par la mémoire de la pile d'exploitation.

La logique est que php.exe appelle un fichier .dll externe pour exécuter une opération, il peut y avoir des vérifications sur la façon dont le système d'exploitation, qui ralentira à la fois l'envoi de la demande via un tel fichier .dll et la réception de la réponse de celui-ci. Si le fichier .dll utilise un disque dur pour accéder à quelque chose - les politiques d'accès au disque dur entrent en scène. En outre, comment tout est situé dans la mémoire - dans RAM ou cache de RAM sur le disque dur. Politiques d'application. Politiques de threads. Limites sur le pourcentage maximum disponible pour une utilisation pour les applications.

Je ne dis pas que les hôtes basés sur Windows sont mauvais, juste qu'ils sont beaucoup plus difficiles à configurer correctement pour un administrateur général. Si vous avez un spécialiste Microsoft en main, il peut régler votre serveur pour qu'il soit aussi rapide que le serveur Linux.

9
Toly
  • activer APC, lors de l'utilisation de PHP5.4

    • si vous ne remarquez pas de gain de vitesse, lorsque APC est activé, quelque chose est mal configuré

      [APC] extension=php_apc.dll apc.enabled=1 apc.shm_segments=1 apc.shm_size=128M apc.num_files_hint=7000 apc.user_entries_hint=4096 apc.ttl=7200 apc.user_ttl=7200

  • activer Zend Opcode quand on PHP 5.5

    [Zend] zend_extension=ext/php_zend.dll zend_optimizerplus.enable=1 zend_optimizerplus.use_cwd=1 zend_optimizerplus.validate_timestamp=0 zend_optimizerplus.revalidate_freq=2
    zend_optimizerplus.revalidate_path=0 zend_optimizerplus.dups_fix=0 zend_optimizerplus.log_verbosity_level=1 zend_optimizerplus.memory_consumption=128 zend_optimizerplus.interned_strings_buffer=16 zend_optimizerplus.max_accelerated_files=2000 zend_optimizerplus.max_wasted_percentage=25 zend_optimizerplus.consistency_checks=0 zend_optimizerplus.force_restart_timeout=60 zend_optimizerplus.blacklist_filename= zend_optimizerplus.fast_shutdown=0 zend_optimizerplus.optimization_level=0xfffffbbf zend_optimizerplus.enable_slow_optimizations=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=10000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=1

  • désactiver Wordpress extensions pas à pas, pour trouver le monstre d'utilisation de la mémoire

  • définissez Wordpress: define('WP_MEMORY_LIMIT', '128M');, sauf si vous utilisez des plugins de conversion d'image qui devraient suffire
  • définir une mémoire illimitée dans php.ini ini_set('memory_limit', -1);
  • sans exécuter Xdebug, cela semble fou, mais le débogueur lui-même a un impact élevé
  • utilisez memory_get_usage et répartissez les appels sur tout le système pour trouver la position du code, là où la mémoire fuit
  • essayez zend.enable_gc=1, les scripts seront plus lents, mais utiliseront moins de mémoire
  • peut-être simplement désactiver la vérification du navigateur de l'utilisateur dans les paramètres SlimStats.
  • si cela n'est pas possible, essayez de remplacer la fonction SlimStats getBrowser (), avec un substitut getBrowser () plus rapide
  • pour une comparaison rapide des récupérateurs d'agent utilisateur, voir https://github.com/quentin389/ua-speed-tests
  • https://github.com/garetjax/phpbrowscap
4
Jens A. Koch

J'ai jeté un œil à ce plugin sur Github:

https://github.com/wp-plugins/wp-slimstat

Et le fichier incriminé inclus est un fichier qui a été minifié dans une certaine mesure et qui est vraiment des données (pas du code), il y a 5 variations dont chacune est d'environ 400 Ko

Il y a aussi ce fichier maxmind.dat qui fait 400 Ko, bien que je ne sais pas s'il utilise les deux.

Vous utilisez une ancienne version du plugin, la version 3.2.3 et il y en a une beaucoup plus récente qui peut résoudre votre problème.

Comparer les différences est difficile car l'auteur ou celui qui n'a pas gardé l'historique git dans l'ordre, j'ai donc dû différencier manuellement le fichier. La plupart des changements liés à _get_browser semblent ajouter un cache.

Il est possible que le chargement de ce fichier soit lent à analyser, mais je m'attendrais à ce que PHP charge les deux fichiers à des taux similaires sur les deux plates-formes, à condition que IO la mise en cache fonctionne) .

[~ # ~] modifier [~ # ~] En regardant de plus près, cela pourrait ne pas résoudre votre problème. Ces fichiers sont essentiellement de grandes tables de recherche d'expression régulière. Votre système Linux avait-il un cache APC et celui-ci n'en a pas? Le cache APC garderait probablement les données du fichier PHP mises en cache (mais pas les modèles regex compilés)

2
Kristopher Ives

Utilisez NGINX et FCGI pour PHP via le socket UNIX (pas TCP socket)).

http://wiki.nginx.org/PHPFcgiExample

Vous remarquerez immédiatement des améliorations de vitesse, même sans accélérateurs. De plus, la configuration ci-dessus aurait une empreinte d'utilisation de la mémoire beaucoup plus faible.

1
mikikg

Pour résoudre un problème de ce type, vous devez:

  • Utiliser les journaux système, à savoir les journaux d'erreurs: les trier par niveau d'erreur
    et/ou date pour identifier les dates du problème.
  • Comparez les mises à jour Windows en cours d'utilisation sur l'un ou l'autre système, l'une d'elles pourrait être défectueuse.
  • Comparez les logiciels utilisés sur l'un ou l'autre système, la configuration de l'un d'entre eux pourrait être défectueuse.

Si tel est le cas, vous souhaiterez désinstaller la mise à jour Windows et/ou le logiciel à l'origine du problème, arrêter complètement votre serveur, puis réinstaller la mise à jour ou le logiciel (pour garantir un état stable lors de l'installation).

Les outils qui peuvent vous aider à résoudre ce problème incluent la suite Sysinternals: http://technet.Microsoft.com/en-us/sysinternals/bb842062.aspx

Ou plus simplement, des scripts VBS open source pour produire des listes comparables de mises à jour et d'applications sur un système.

0
Mauro Colella