【问题标题】:PHP factor 30 performance difference from Linux to WindowsPHP 因素 30 从 Linux 到 Windows 的性能差异
【发布时间】:2014-05-15 17:39:05
【问题描述】:

我们的团队正在开发 WordPress 插件,并在几个独立的服务器上提供托管实例。我们的 WordPress 安装由 Git 管理,所有服务器都部署了相同的源和 WordPress 设置,只有域和数据库中的实际数据有所不同。对于每次安装,MySql 都在同一台主机上运行。 WordPress 仅在每台服务器上运行。

但是,在 Windows Server 2008 RC2 上部署此设置后,我们注意到与我们的其他服务器相比存在巨大的性能差异:页面生成时间从 avg.使用 PHP 生成的页面需要 400 毫秒到 4000-5000 毫秒。仅 Apache 提供的静态资源,速度与 linux 差不多。

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

  1. 确保没有运行防病毒软件或其他 Windows 域的东西干扰
  2. 收集分析数据以识别脚本执行期间的时间杀手
  3. 测试不同的服务器和硬件设置
  4. 仔细检查 Apache 和 PHP 配置是否存在明显的配置错误

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

下面提供了分析、系统测试和配置详细信息。 我们不想优化这个脚本(我们知道该怎么做)。我们希望让脚本在 Windows 上的运行速度与在 Linux 上大致相同(假设 opcache/...的设置相同)。也无需优化脚本的内存占用。

更新:一段时间后,系统似乎内存不足,触发内存不足异常和随机分配。有关更多详细信息,请参见下文。重启 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 模块激活(同样会导致性能不佳)。但是今天它们运行得非常快(~1sec/request)。添加 Opcache 可以将其降低到 ~400ms/req。 Apache/PHP/Windows 保持不变。

1) 分析结果

分析是在所有机器上使用 XDebug 完成的。通常我们只收集了一些运行 - 这些足以揭示大部分时间(50%+)花费的位置:WordPress 插件 wp-slimstats 的方法 [get_browser][1]

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 上大约需要 90 毫秒,在 Windows 上大约需要 3000 毫秒。所有测试设置的结果都相同(图片显示了两次执行的数据):

当然,加载两个巨大的数组需要一些时间。也评估正则表达式。但我们预计它们在 Linux 和 Windows 上花费的时间大致相同。这是 linux vm 上的分析结果(仅限一页请求)。区别很明显:

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

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.2 毫秒到 4 毫秒:

在 Linux 上:

2) 测试系统

为了确保虚拟化或 Apache 确实会影响这一点,我们在几个设置上对此进行了测试。所有设置都禁用了 Antivir:

  • Linux Debian、Apache 2 和 PHP 在最新的稳定版本上。这对于在他们的虚拟机中运行的开发人员来说和在登台/实时服务器上是一样的。作为所需性能的参考系统。在我们的办公室或某些托管服务(共享空间)中运行。 Windows 系统的内存在 4GB 到 8GB 之间,内存使用率一直低于 50%。虚拟化永远不会同时运行 Windows 和 Apache。
  • Life-Servers,在 T-Systems(托管虚拟化服务器)上运行,在 VMWare Player 上
    • 赢得 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 中提到的快速模块)
  • 在本地机器上,主机:OpenSuse,虚拟化: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),它们的完整堆栈运行得非常快。相同的配置。

更新 2:在 Life-Servers 上将 PHP 作为 apache 模块运行比 fastcgi 方法稍快:低至约 2 秒,减少 50%。

内存不足

一段时间后,我们的 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 内存管理器不能分配更多的内存,尽管脚本可以这样做。在事件发生时,服务器有大约 50% 的可用 RAM (2GB+)。所以服务器实际上并没有用完内存。重启 Apache/PHP 暂时解决了这个问题。

不确定此问题是否与此处的性能问题有关。然而,由于这两个问题似乎都与内存有关,因此将其包含在此处。特别是我们将尝试重现提供良好性能的 Windows 测试的设置。

3) Apache & PHP 配置

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

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_module 配置:

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

php.ini 摘录:

realpath_cache_size = 12M
pcre.recursion_limit = 100000

4) 目前怀疑的原因

旧假设:

所有三个示例都严重依赖大数组和字符串操作。那某种似乎是普通工厂。由于实现在 Linux 上运行良好,我们怀疑这是 Windows 上的内存问题。鉴于在精确定位的位置没有数据库交互,我们不怀疑数据库或服务器 PHP 集成是问题所在。不知何故,PHP 的内存交互似乎很慢。 也许有人干扰了 Windows 上的内存,导致访问速度显着变慢?

旧假设 2:

由于相同的堆栈在其他 Windows 机器上运行良好,我们假设问题出在 Windows 配置中。

新假设 3:

实际上我没有假设。为什么 PHP 会像 fastcgi 那样运行比 apache 模块慢得多>

关于如何验证这一点或在这里找到真正问题的任何想法?非常欢迎任何有关解决此问题的帮助或指导。

【问题讨论】:

  • 您是否在同一硬件上运行两者?
  • @hek2mgl : apache 和 mysql?是的。考虑到当前的内存/CPU 消耗和所花费时间的位置,这应该不会造成问题。
  • 不,我是说windows和linux
  • 我的意思是:它们有可比性吗?
  • @halfer:IIS 是一个完美的选择。但是,我们在 IIS 上也有同样糟糕的性能(参见 2)测试系统)。

标签: php linux windows wordpress performance


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

    一位优秀的 Microsoft 认证专家将能够在几分钟内解决您的问题,因为他们将有经验准确地告诉您要检查和禁用/启用/更改设置的设置/服务/策略,以便执行 PHP 脚本更快。

    根据我的记忆,我只能建议您检查与 RAM、硬盘访问、环境变量、限制和安全性(如防火墙)有关的所有内容。一切可能影响 php 脚本执行的东西,从一些 Remote Procedue Call 策略开始,到操作堆栈内存结束。

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

    我并不是说基于 Windows 的主机不好,只是说它们对于一般管理员来说更难正确设置。如果您手头有 Microsoft 专家,他可以将您的服务器调整为与基于 Linux 的服务器一样快。

    【讨论】:

    • 这也是我们所怀疑的。一些简单的推理将我们带到这里的记忆:我们可以追溯到 PHP 脚本执行的瓶颈,而不是网络、Apache PHP 通信或 PHP Mysql。所有的bottle-becks都只与内存中较大的数组有关,这些步骤不涉及I/O / IPC。所以它必须是 .dll(正则表达式)或内存策略。 Out sys-admin 说没有这样的政策生效。
    • 对于一个简单的测试 - 在没有 preg_match 的情况下重做脚本。使用 strpos() 之类的字符串函数 - 我认为您可以达到类似的效果。测量结果,您将确定 PCRE 函数是否运行缓慢,或者仅仅是因为数组很大。
    【解决方案3】:

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

    http://wiki.nginx.org/PHPFcgiExample

    即使没有加速器,您也会立即注意到速度提升。此外,上述设置的内存使用量会少得多。

    【讨论】:

    • Windows 上的 UNIX 套接字?将是一个管道。但这并不能解决问题。
    • 一般来说,我推荐 NGINX 和 FCGI,即使在 Windows 上也是如此。连接槽套接字提供了一些速度改进,但这不是必须的。关于您的问题,尝试隔离有问题的脚本或函数并在 CLI PHP 环境(没有 Web 服务器)中对其进行测试,看看它是否做出了一些改变。
    • Nginx + PHP FCI 在 Linux 上比使用 mod_php 的 pre-fork apacje 更快?不,您在中等负载水平下的响应时间会更短(诚然,nginx 会在高负载水平下领先,直到您遇到 fcgi 进程的瓶颈。有几个已发布的基准测试证明了这一点。
    【解决方案4】:
    • 在使用 PHP5.4 时启用 APC

      • 如果您没有注意到速度提升,则当 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 设置中禁用对用户浏览器的检查..
    • 如果不可能,请尝试使用faster getBrowser() substitute 覆盖 SlimStats getBrowser() 函数
    • 有关用户代理获取器的速度比较,请参阅https://github.com/quentin389/ua-speed-tests
    • https://github.com/garetjax/phpbrowscap

    【讨论】:

    • 这里的问题不是WordPress的内存使用问题。平均请求最多只占用 50MB。上面发布的错误已经在随机位置发生在 8MB 或更少。脚本的合法内存消耗没有问题。问题实际上是 PHP 无法为脚本分配更多内存,尽管有可用内存。
    • 对于性能部分:启用 APC / Opcache 不会导致明显差异。 Xdebug 当然会减慢速度。然而,如果没有 xdebug(根本没有启用扩展),页面生成时间只有大约 1/2 秒快,但速度很慢(3 秒+)。禁用 slimstats 当然会加快速度,但其他瓶颈仍然存在。它更多的是与症状作斗争,而不是真正的问题,我们怀疑如下(数组/内存访问明智)。
    • 旁注:也许值得与基于 Nginx 的堆栈进行比较,只是出于好奇和测试:wpn-xm.org/downloads/WPNXM-0.6.0-Lite-Setup-w32.exe
    • 将 PHP 错误报告提高到严格,检查 php_error.log。另外:激活 APC/操作码缓存应该会产生明显的差异!检查设置,使用 apc.stat=1/0、ttl 和缓存。请记住:打开 apc 时,请关闭 xdebug。
    • 禁用 XCache 和启用 APC(使用您的设置)不会导致任何明显的差异。它确实有点快。然而,平均生成时间仍处于 4 秒以上的区域。它不是需要时间的操作码生成,而是我上面发布的示例。那些没有被 APC/Optimizer 优化。当然缓存通常会提高性能。但是更改 3rd 方插件/WordPress Core 的来源不是一种选择,也不应该是必要的,因为事实证明它运行得相当快,一旦应用程序在 linux 和 windows 上以大致相同的速度运行,我们就会这样做。跨度>
    【解决方案5】:

    我在 Github 上查看了那个插件:

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

    并且包含的​​违规文件是一个文件 已经在某种程度上缩小了,真的 是数据(不是代码),每个都有 5 个变体 大约 400KB

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

    您使用的是旧版本的插件,版本 3.2.3 还有一个更新的可以解决 你的问题。

    比较差异很难,因为作者 或者没有按顺序保存 git 历史记录的人, 所以我不得不手动区分文件。大部分变化 与 _get_browser 相关的似乎正在添加缓存。

    加载该文件可能解析速度很慢,但是 我希望 PHP 以相似的方式加载这两个文件 两个平台的费率都授予 IO 缓存 工作。

    EDIT 仔细观察可能无法解决您的问题 问题。这些文件基本上是大的正则表达式 查找表。你的 Linux 系统是否有 APC 缓存 就可以了,这一个没有? APC 缓存可能会 保持 PHP 文件数据缓存(虽然不是编译的 正则表达式模式)

    【讨论】:

    • 您对插件版本的看法是正确的(尽管是 3.5.6)。然而,这些变化只会影响最大值。所需的内存,而不是要评估的正则表达式的数量。 wordpress.org/plugins/wp-slimstat/changelog
    • 是否在 Linux 上启用了 APC 更为重要。如果启用 APC,它不会一直重新加载这些字符串。
    • 不,如上所述,几乎相同的配置。包括优化扩展。
    猜你喜欢
    • 2014-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-01
    • 2013-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多