PHP因素30从Linux到Windows的性能差异

我们的团队正在开发WordPress插件,并在几台独立的服务器上提供托pipe实例。 我们的WordPress安装是由Gitpipe理的,所有的服务器都有相同的源代码和WordPress安装程序,只有数据库中的域名和实际数据有所不同。 对于每个安装,MySql运行在同一个主机上。 WordPress在每台服务器上都是独占运行的。

但是,在Windows Server 2008 RC2上部署此设置之后,我们注意到与其他服务器相比,性能显着不同:页面生成时间从平均值上升。 用PHP生成的页面为400ms到4000-5000ms。 对于仅由Apache提供的静态资源,速度与linux上大致相同。

所以我们采取了一些措施来缩小这个问题的范围:

  1. 确保没有antivir软件运行或其他Windows域的东西干扰
  2. 收集configuration文件数据以在脚本执行期间识别计时器
  3. testing不同的服务器和硬件设置
  4. 仔细检查Apache和PHPconfiguration是否有明显的configuration错误

经过一些分析后,我们很快注意到正则expression式的评估在我们的Windows机器上是非常慢的。 评估10.000正则expression式( preg_match )在Linux上需要大约90ms,在Windows上需要大约3000ms。

以下提供了分析,系统testing和configuration细节。 我们不想优化这个脚本(我们知道该怎么做)。 我们希望让脚本在Linux上运行的速度与在Linux上的运行速度大致相同 (对于opcache / …给出相同的设置)。 也不需要优化脚本的内存占用。

更新:一段时间后,系统似乎耗尽内存,触发内存exception和随机分配。 请参阅下面的更多细节。 重新启动Apache / PHP现在解决了这个问题。

跟踪_get_browser是:

 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) 

更新2 :由于某些原因,我不记得我们回到激活PHP作为我们的服务器上的Apache模块(相同的性能不佳)。 但今天他们跑得非常快(〜1秒/请求)。 添加Opcache带来这个下降到~400ms / req。 Apache / PHP / Windows保持不变。

1)分析结果

在所有机器上使用XDebug进行分析。 通常我们只收集了几次运行 – 这些足以显示大部分时间(50%以上)的位置:WordPress插件的方法[get_browser][1] 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 } 

这个函数类似于PHP的get_browser检测浏览器的能力和操作系统。 大部分的脚本执行时间都花费在这个foreach循环中,评估所有这些preg_match (大约8000 – 10000每页请求)。 这在Linux上需要大约90ms,在Windows上需要3000ms。 所有testing的结果都是一样的(图为两次执行的数据):

在IIS8上的wp_slimstat :: _ get_browser分析结果

当然,加载两个巨大的arrays需要一些时间。 也评估正则expression式。 但是我们期望他们在Linux和Windows上大致同时进行。 这是linux vm上的性能分析结果(只有一个页面请求)。 区别很明显:

在这里输入图像说明

另一个时间杀手实际上是对象cachingWordPress的使用:

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

这个函数本身花了时间(3个脚本执行):

在这里输入图像说明

在linux上:

在这里输入图像说明

最后一个真正的大时间杀手是翻译。 从内存中加载的每个翻译,都会在WordPress中使用0.2ms到4ms之间的任何内容: 在这里输入图像说明

在linux上:

在这里输入图像说明

2)testing系统

为了确保虚拟化或Apache确实影响到了这一点,我们在几个设置上进行了testing。 Antivir在所有设置中被禁用:

  • Linux Debian,Apache 2和PHP上最新的稳定版本。 对于在虚拟机中运行的开发人员来说,这同样适用于登台/活动服务器。 充当期望performance的参考系统。 要么在我们的办公室运行,要么在一些托pipe提供(共享空间)。 Windows系统有4GB到8GB的内存,在任何时候内存使用率都低于50%。 虚拟化从不同时运行Windows和Apache。
  • 在VMWare播放器上运行的T-Systems(托pipe虚拟化服务器)上运行的生命服务器
    • 赢得2008 R2。 Apache 2.2.25 + PHP 5.4.26 NTS,VC9作为fastcgi模块
    • 赢得2008 R2。 Apache 2.2.25 + PHP 5.5.1 NTS,VC11作为fastcgi模块
    • 赢得2008 R2。 Apache 2.2.25 + PHP 5.5.1 NTS,VC11作为apache模块
    • Win 2008 R2,Apache 2.2.25 + PHP 5.5.11 TS,VC11作为apache模块(这是我在更新2中提到的最快的一个)
  • 在本地机器上,Host:OpenSuse,Virtualization:VMWare播放器,与@T-Systems相同。 为了避免他们的基础设施影响我们:
    • 赢得2008 R2。 Apache 2.2.25 + PHP 5.4.26 NTS,VC9作为fastcgi模块
    • 赢得2008 R2。 IIS7 + PHP 5.4.26 NTS,VC9作为fastcgi模块(有和没有wincache)
    • Win 2012. IIS * + PHP 5.5.10 NTS,VC11作为fastcgi模块(有和没有wincache)
  • 在没有虚拟化的本地机器上
    • 赢得2008 R2。 Apache 2.2.25 + PHP 5.4.26 NTS,VC9作为fastcgi模块

上述的分析结果在不同的系统上是相同的(〜10%的推导)。 Windows一直是比Linux慢的重要因素。

使用全新的WordPress和Slimstats安装导致约。 相同的结果。 重写代码在这里不是一个选项。

更新 :同时,我们发现了另外两个Windows系统(Windows 2008 R2,VM&Phys),这个完整的堆栈运行速度非常快。 虽然相同的configuration。

更新2 :在Life-Servers上运行PHP as apache模块的速度稍快于fastcgi方法:降低到〜2秒,减less50%。

内存不足

过了一段时间,我们的Live-Server停止工作,触发了这些内存不足的例外:

 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) 

这发生在随机脚本位置。 很明显,Zend内存pipe理器不能分配更多的内存,虽然脚本将被允许这样做。 如果事件发生的话,服务器有大约50%的空闲RAM(2GB +)。 所以服务器并没有真正用完ram。 重新启动Apache / PHP现在解决了这个问题。

不知道这个问题是否与性能问题有关。 然而,由于这两个问题似乎都与记忆有关,所以这里就包括这个问题 特别是,我们将尝试重现提供不错性能的Windowstesting的设置。

3)Apache和PHPconfiguration

…可能没有任何常见的陷阱。 输出缓冲已启用(默认),禁用多重覆盖,…如果有任何选项是有意义的,我们将很乐意提供它们。

输出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" 

mpm_winnt_moduleconfiguration:

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

php.ini的摘录:

 realpath_cache_size = 12M pcre.recursion_limit = 100000 

4)目前怀疑的原因

旧假设:

这三个例子都严重依赖于大数组和string操作。 那种似乎是常见的工厂。 由于Linux的实现工作ok'ish,我们怀疑这是Windows上的内存问题。 考虑到在指针位置没有数据库交互,我们不怀疑数据库或服务器PHP集成是问题。 不知何故,PHP的内存交互似乎很慢。 也许有人干扰Windows上的内存使访问速度显着变慢?

旧假设2:

由于相同的堆栈在其他Windows机器上运行正常,我们假设问题在Windowsconfiguration中的某处。

新的假设3:

其实我没有假设 为什么运行PHP的速度要比fastcgi慢得多,然后像apache模块那样运行

任何想法如何validation这个或在这里find真正的问题? 任何帮助或方向来解决这个问题是非常受欢迎的。

Windows在各种情况下都有许多限制,防止,保护,控制和使用计算机的服务/策略。

一位优秀的微软认证专家将能够在几分钟内解决你的问题,因为他们将有经验确切地知道哪些设置/服务/策略来检查和禁用/启用/更改设置,以便更快地执行PHP脚本。

在我的记忆中,我只能建议你检查所有与RAM,硬盘访问,环境变量,限制和安全性(如防火墙)有关的事情。 一切都会影响php脚本的执行,从一些远程过程调用策略开始,到操作栈内存结束。

其逻辑是,php.exe调用一些外部.dll文件来执行一些操作,可能会检查OS所做的方式,这会减慢通过这样的.dll发送请求,并从它接收响应。 如果.dll使用硬盘访问某些内容 – 硬盘访问策略进入场景。 另外,所有东西都在内存中 – RAM或RAM的硬盘缓存中。 应用程序策略 线程策略。 限制可用于应用程序的最大百分比。

我并不是说基于Windows的主机是不好的,只是为了一个普通的管理员而设置它们要困难得多。 如果您手上有Microsoft专家,他可以将您的服务器调整为与基于Linux的服务器一样快。

  • 启用APC时,使用PHP5.4

    • 如果您没有注意到速度增益,那么当APC打开时,某些内容会被错误配置

      [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

  • 在PHP 5.5上启用Zend Opcode

    [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

  • 禁用WordPress的扩展步骤,找到内存使用的怪物

  • 设置Wordpress: define('WP_MEMORY_LIMIT', '128M'); ,除非你使用图像转换插件,应该足够了
  • 在php.ini中设置无限的内存ini_set('memory_limit', -1);
  • 没有运行Xdebug的配置文件,这听起来很疯狂,但调试器本身有很大的影响
  • 使用memory_get_usage并在整个系统中传播调用以查找内存泄漏的代码位置
  • zend.enable_gc=1一试,脚本将会变慢,但使用更少的内存
  • 也许只是禁用检查SlimStats设置中的用户浏览器。
  • 如果这是不可能的,尝试覆盖SlimStats的getBrowser()函数, 更快的getBrowser()替代
  • 有关用户代理访问者的速度比较,请参阅https://github.com/quentin389/ua-speed-tests
  • https://github.com/garetjax/phpbrowscap

我看了一下Github上的插件:

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

而且包含的违规文件是一个已被缩小到一定程度的文件,实际上是数据(不是代码),有5个变体,每个变体大约有400KB

还有那个是400KB的maxmind.dat文件,虽然我不知道它是否同时使用。

您正在使用旧版本的插件,版本3.2.3,并有一个更新的,可以解决您的问题。

比较差异是很难的,因为作者或谁没有保持git历史的顺序,所以我不得不手动比较文件。 与_get_browser相关的大部分更改似乎都是添加一个缓存。

有可能加载这个文件的速度很慢,但是我希望PHP能够在两个平台上以相似的速度加载两个文件,这两个平台都是IO缓存正在工作的。

编辑看更接近,可能无法解决您的问题。 这些文件基本上是大型正则表达式查找表。 你的Linux系统上有一个APC缓存,而这个不是? APC缓存可能会保持PHP文件数据缓存(尽管不是编译的正则表达式模式)

通过UNIX套接字(不是TCP套接字)使用NGINX和FCGI for PHP。

http://wiki.nginx.org/PHPFcgiExample

即使没有加速器,您也会马上注意到速度的提升。 此外,上面的设置将有更低的内存使用足迹。

要解决这类问题,您必须:

  • 使用系统日志,即错误日志:按错误级别进行排序
    和/或确定问题日期的日期。
  • 比较两个系统中使用的Windows更新,其中一个可能有问题。
  • 比较两个系统中使用的软件,其中之一的设置可能是错误的。

如果是这种情况,您需要卸载导致问题的Windows更新和/或软件,完全关闭服务器,然后重新安装更新或软件(以确保在安装期间处于稳定状态)。

可以帮助您解决此问题的工具包括sysinternals套件: http : //technet.microsoft.com/en-us/sysinternals/bb842062.aspx

或者更简单地说,开源的VBS脚本可以在系统上生成可比较的更新和应用程序列表。