第 1 部分: 使用操作码缓存软件提升性能和吞吐量 原文:http://www.ibm.com/developerworks/cn/opensource/os-php-fastapps1/ PHP 是一种脚本语言,常用于创建 Web 应用程序。它易于掌握,并能迅速生成可视化结果。然而,由于 PHP 是以解释的方式执行的,所
在很短的时间内,PHP 成为了一种非常流行的开发 Web 应用程序的程序设计语言。对初学者来说,PHP 易于安装和学习。对于有经验的开发人员而言,PHP(从 V5 开始)提供了强大的面向对象特性。PHP 开发人员拥有庞大的群体,大量的开源及商业库和工具扩展了该核心语言的功能。PHP 能迅速产生可视化结果,因此备受众人推崇。
与其他开发 Web 应用程序的脚本语言(包括 Perl、Python 和 Ruby)相似,每次 HTTP 请求调用时,PHP 代码都会被解析和翻译为操作码(PHP 引擎直接执行的原语指令 —— 类似于汇编语言)再执行。在要求很低或可忽略的情况下,服务器看上去能立即执行这个复杂的解释过程。但是一旦处理的页面增加,解释 —— 从本质上说,重复工作 —— 就会对服务器造成很大的负担。在某些情况下,“编译” PHP 代码的时间会远远超过执行该代码所需的时间。因此,当需求增加时,您常常会自食其果,因为处理这些不断解释和动态生成的页面需要消耗更多的系统资源。
若 您对处理器和 RAM 的预算没有限制,那么就大可不必通过优化应用栈(硬件、操作系统、数据库、Web 服务器和 PHP 代码)来保证站点的可响应性。然而,由于资金通常都是最缺乏的资源,所以改善性能是必不可少的。调优意味着对系统增加内存、修改操作系统参数、加速 Web 或数据库服务器、提高代码效率或者这其中的一些组合。每一项都有其各自的作用。
不要浪费周期 —— 重复使用
节约 CPU 周期的另一种方法是减少运行 PHP 应用程序所需的重复工作。当然,没有必要每次都把同样的 PHP 代码翻译一遍。PHP 代码被翻译成操作码后,可以把它保存起来并重复使用 —— 直到原始代码被修改。确实,缓存 —— 用于保存和重用 PHP 操作码 —— 是几种 PHP 加速器内部的机制,包括开源 Alternative PHP Cache (APC)、支持 PHP 的 Turck MMCache、XCache、eAccelerator 和商业 Zend Platform。后三类加速器能够缓存和优化字节码,这为系统提供了更多的速度提升。
这个月,我将探究如何安装、部署和配置 XCache。XCache 相对较新,但是很多站点使用它的效果都很好。此外,XCache 易于构建、安装和配置,因为它是做为 PHP 扩展实现的。不需对 Apache 和 PHP 进行重编译。
本 文基于 XCache V1.2.0。它可为 PHP V4.3.11 至 V4.4.4、PHP V5.1.x 至 V5.2.x 以及 PHP V6 的早期版本提供可靠支持(XCache 并不支持 PHP V5.0.x)。XCache 兼容 mod_php 和 FastCGI 但并不支持 Common Gateway Interface (CGI) 和命令行 PHP 解释器。XCache 源代码能构建在许多系统上,包括 FreeBSD、Sun Solaris、Linux® 和这里所示的 Mac OS X。使用 Cygwin UNIX® 仿真环境或 Visual C,能在 Microsoft® Windows® 上构建 XCache。还可以为 Cygwin 或原生 Win32 构建 XCache。后者与 PHP 的官方 Win32 版本兼容。
本文的演示基于 Apache V2.2.3、PHP V5.2.0、XCache V1.2.0(2006 年 10 月发布)和 Mac OS X V10.4.8 Tiger 上的 Xcode V2.4.1。硬件平台为配有 2-GHz Intel® Core Duo 处理器和 2 GB RAM 的 Apple MacBook。
构建 XCache 的基本步骤
在开始之前,首先确保 PHP 正常安装并核实 phpize
是否位于 shell 的 PATH
下。同时,还需要一个 C 编译器,例如 GNU Compiler Collection (GCC),和一组包含 make
和 m4
的开发工具。在 Mac OS X 上,免费的 Xcode 软件开发环境提供了必需的构建工具。
完成以下操作步骤,在 Mac OS X 上构建、部署 XCache 并对其进行性能检测。在其他平台上构建 XCache 与之类似。如果使用 Linux,则其发行版可能已经包含了 XCache 或者已经将其以预先打包好的格式提供给您。
增加 Mac OS X 的共享内存
首先增加 Mac OS X 上留出的共享内存总量。要实现这一目的,需创建(或编辑)文件 /etc/sysctl.conf 并创建如下条目:
清单 1. 增加 Mac OS X 上留出的共享内存总量<br>kern.sysv.shmmax=33554432<br>kern.sysv.shmmin=1<br>kern.sysv.shmmni=32<br>kern.sysv.shmseg=8<br>kern.sysv.shmall=8192<br> 登入後複製 |
这些设置把共享内存总量增加到了 32 MB。如还需进一步扩展共享内存,可以把 kern.sysv.shmall
设定为 kern.sysv.shmmax
除以硬件页面大小。使用 sysctl hw.pagesize
能获得硬件页面大小。例如,假设需要 128 MB 的共享内存,那么设定 kern.sysv.shmmax=134217728
并设定 kern.sysv.shmall=32768
。
重启 Mac OS X 使修改生效。重启后,检验新设置是否生效,键入:
sysctl -a | grep kern.sysv<br> 登入後複製 |
通过源代码构建 XCache
接下来,通过源代码构建 XCache。从 http://xcache.lighttpd.net 处下载源代码。下载完后解压代码,并转换到 .tar 文件创建的新目录。
清单 2. 通过源代码构建 XCache<br>$ cd /tmp<br>$ wget http://210.51.190.228/pub/XCache/Releases/xcache-1.2.0.tar.gz<br>$ tar xzf xcache-1.2.0.tar.gz<br>$ cd xcache<br> 登入後複製 |
运行 phpize
为编译 XCache 做准备。
phpize
<br>$ phpize<br>Configuring for:<br>PHP Api Version: 20020918<br>Zend Module Api No: 20020429<br>Zend Extension Api No: 20050606<br> 登入後複製 |
运行 configure
,创建适合于原生操作系统的 makefile。
configure
创建 makefile<br>$ ./configure --enable-xcache --enable-xcache-coverager <br>checking build system type... i686-apple-darwin8.8.1<br>checking host system type... i686-apple-darwin8.8.1<br>...<br>creating libtool<br>configure: creating ./config.status<br>config.status: creating config.h<br> 登入後複製 |
此处,--enable-xcache
选项包含 XCache 支持,--enable-xcache-coverager
选项包含用于测量加速器功效的附加特性。要启用操作码优化,添加 --enable-xcache-optimizer
。
当然,下一步将使用 make
命令行构建和安装代码。运行 make
,然后作为 root 用户运行 make install
。
make
构建和安装代码<br>$ make<br>...<br>cp ./xcache.so /Users/strike/tmp/xcache/modules/xcache.so<br>Build complete.<br><br>$ sudo make install<br>Installing shared extensions: /usr/lib/php/extensions/no-debug-non-zts-20020429/<br> 登入後複製 |
如果上述两项操作顺利完成,那么 XCache 将位于 /usr/lib/php/extensions/no-debug-non-zts-20020429/xcache.so。(路径 /usr/lib/php/extensions/no-debug-non-zts-20020429 反映了正在使用的 API 版本和用于构建 PHP 的编译选项。如果启用了试用的 Zend Thread Safety 特性,则 "no-debug" 应为 "debug","non-zts" 应为 "zts"。)
修改 php.ini 文件
因为安装了扩展,所以必需修改 php.ini 文件,使之包含 XCache 扩展并对其进行配置。打开文件 /private/etc/php.ini,在其中添加如下代码行:
<br>[xcache-common]<br>zend_extension = /usr/lib/php/extensions/no-debug-non-zts-20020429/xcache.so<br><br>[xcache.admin]<br>; Change xcache.admin.user to your preferred login name <br>xcache.admin.user = "admin"<br><br>; Change xcache.admin.pass to the MD5 fingerprint of your password<br>; Use md5 -s "your_secret_password" to find the fingerprint<br>xcache.admin.pass = "0ad72f3f352fcd8acdf266bafd0ac48d"<br><br>[xcache]<br>; Change xcache.size to tune the size of the opcode cache<br>xcache.size = 24M<br>xcache.shm_scheme = "mmap"<br>xcache.count = 2<br>xcache.slots = 8K<br>xcache.ttl = 0<br>xcache.gc_interval = 0<br><br>; Change xcache.var_size to adjust the size of variable cache<br>xcache.var_size = 8M<br>xcache.var_count = 1<br>xcache.var_slots = 8K<br>xcache.var_ttl = 0<br>xcache.var_maxttl = 0<br>xcache.var_gc_interval = 300<br>xcache.test = Off<br>xcache.readonly_protection = On<br>xcache.mmap_path = "/tmp/xcache"<br>xcache.coredump_directory = ""<br>xcache.cacher = On<br>xcache.stat = On<br>xcache.optimizer = Off<br><br>[xcache.coverager]<br>xcache.coverager = On<br>xcache.coveragedump_directory = ""<br> 登入後複製 |
注意:为了简明起见,此处省略了一些注释。要了解每个参数的含义,请参阅 XCache 源代码中 xcache.ini 文件设置的例子。
操作码和变量缓存的大小是 32 MB,这是 /etc/rc 留出的最大值。对于 Mac OS X,xcache.mmap_path
必须为文件名。因为 PHP 代码将在 MacBook 上运行,所以 xcache.count
应设为 2,表示 MacBook 中 CPU 的数量。要访问 XCache 统计信息页面,需改变 xcache.admin.pass
设置。运行:
$ md5 -s "<em>password</em>"<br> 登入後複製 |
此处 <em>password</em>
为您的密码。把输出复制给 xcache.admin.pass
。例如,若需将密码设定为 op3nsesam3
,可以运行:
$ md5 -s "op3nsesam3"<br>MD5 ("op3nsesam3") = cd959ac3debe8f587546a3fa353b3268<br> 登入後複製 |
然后把 cd959ac3debe8f587546a3fa353b3268
复制给 xcache.admin.pass
。
重启 Web 服务器
设置好 XCache 后,重启 Apache Web 服务器。对于大多数系统,可以以 root 用户的身份使用 apachectl restart
进行重启。
$ sudo apachectl restart<br>/usr/sbin/apachectl restart: httpd restarted<br> 登入後複製 |
创建程序以调用 phpinfo()
检验 XCache 是否已启用,需创建一个小型 PHP 程序用于调用 phpinfo()
并在 Web 浏览器中打开那个文件。这样就能看到类似下图的 XCache 版面。
监控 XCache
要监控 XCache,需安装 XCache 源代码的 admin 目录中的管理页面。把整个 admin 目录复制到 Apache 文档根目录下。通常来说,Mac OS X 中的文档根目录为 /Library/WebServer/Documents。
$ cp -pr admin /Library/WebServer/Documents<br> 登入後複製 |
复制完成后,使用 sudo apachectl restart
重启 Web 服务器。用浏览器打开 http://localhost/admin,检验管理面板是否工作正常。请参阅类似图 2 的面板。
测试应用程序
选择一个或两个应用程序进行测试。您可以使用自己的代码或者,如果想要更复杂一点,使用大型的 PHP 应用程序,例如 phpMyAdmin 或者 Serendipity。
运行性能检测
Apache HTTP Web 服务器提供了一个叫做 ab
的工具,即 Apache HTTP 服务器性能检测(benchmark)工具的缩写。ab
用于为 PHP 页面自动化处理大量的请求。phpMyAdmin 应用程序将会是一个很好的选择,因为它很可能已经安装到您的系统上了。
ab
工具易于使用:只需提供给它一个重复数和一个 URL。ab
工具对这个 URL 提交若干次请求并返回统计信息。由于 XCache 已启用,所以第一个性能检测显示了加速后的性能。
在运行 ab 之前,用浏览器导航到 http://localhost/phpmyadmin/。访问这个 PHP 页面一次,便会加载用来将此页呈现到缓存内的所有 PHP 代码。此时,运行如下的性能检测,重复 100000 次。
<br>$ ab -n 100000 http://localhost/phpmyadmin<br>...<br>Concurrency Level: 1<br>Time taken for tests: 14.597 seconds<br>Complete requests: 100000<br>Failed requests: 98262<br> (Connect: 49131, Length: 49131, Exceptions: 0)<br>Broken pipe errors: 0<br>Non-2xx responses: 50869<br>Total transferred: 25739714 bytes<br>HTML transferred: 12005084 bytes<br>Requests per second: 6850.72 [#/sec] (mean)<br>Time per request: 0.15 [ms] (mean)<br>Time per request: 0.15 [ms] (mean, across all concurrent requests)<br>Transfer rate: 1763.36 [Kbytes/sec] received<br> 登入後複製 |
有用的统计信息包括每秒的请求数和完成所有测试的总时间。对于前者,值越大越好;对于后者,值越小越好。
现在,在 php.ini 文件中禁用 XCache,然后再一次运行性能检测,如清单 8 所示。可以注释掉对 XCache 扩展的引用,或者关闭所有的 XCache 特性。再次运行性能检测之前,需重启 Apache。
<br>$ sudo apachectl restart<br>$ ab -n 100000 http://localhost/phpmyadmin<br>Concurrency Level: 1<br>Time taken for tests: 17.771 seconds<br>Complete requests: 100000<br>Failed requests: 98256<br> (Connect: 49128, Length: 49128, Exceptions: 0)<br>Broken pipe errors: 0<br>Non-2xx responses: 50872<br>Total transferred: 25741232 bytes<br>HTML transferred: 12005792 bytes<br>Requests per second: 5627.15 [#/sec] (mean)<br>Time per request: 0.18 [ms] (mean)<br>Time per request: 0.18 [ms] (mean, across all concurrent requests)<br>Transfer rate: 1448.50 [Kbytes/sec] received<br> 登入後複製 |
此处,XCache 禁用之后,每秒的请求数有所下降,表示 Apache 服务器处理每个请求需更长的时间。运行整套测试所需的时间也因此增加了。
尽 管这只是一个简单的性能检测 —— phpMyAdmin 连接数据库的功能被禁用了,这样能限制单独解释 PHP 的处理时间 —— 并且不是非常科学,但它确实向我们演示了使用 XCache 能够完成哪些任务。对于微小的投入(欣慰地是,PHP 或 Apache 不需重编译),XCache 能产生一个相对较大的回报。代码越复杂,可能的受益也越大。
若想了解 XCache 是如何有效地运行的,请访问 http://localhost/xadmin 并单击 List PHP。您能看到缓存中的 PHP 文件列表,连同 cache hit、操作码的代码大小、源文件的字节大小,等等。图 3 显示了 XCache 专门为 XAMPP 栈包构建时的结果。
图 3. XCache 管理页面反映了缓存的状态和内容前 面已经提及,XCache 是许多加速器中的一种。在强大的 Zend 软件中还有许多免费的开源替代方案和一个商业化产品可选。每种 PHP 加速器都有自己的系统需求,所以应主要根据现有的或预期的配置以及应用程序的特性选择合适的加速器。从中推荐一种很难,但是安装编译器缓存是我建议您务必 要做的。
大量的调优选择
除了缓存之外,还有许多其他途径可以加速应用程序。可以通过删除 PHP 引擎的一些很深奥的特性来简化它。例如,若不需使用 TCP/IP V6 (IPv6) 网络,便可在构建 PHP 时禁用该属性。通过在 PHP 源代码树的顶部输入 ./configure --help
可以参阅 PHP 配置选项的完整列表。无论选择哪一个配置选项,都应该将:
--enable-inline-optimization --disable-debug<br> 登入後複製 |
添加到最终的配置命令。前一选项使 PHP 有可能更快地执行(不需使用类似 Zend Engine 的软件进行额外的操作码优化);后一选项取消了 PHP 的调试模式,只有当需要解决 PHP 应用服务器本身的问题时才需开启该模式。
当然,正如一些 C 应用程序,我们能利用 C 编译器构建更佳的可执行程序。如果把 PHP 作为 Linux 或 FreeBSD 上的 Apache Dynamic Shared Object (DSO) 在 x86 处理器上运行,需考虑在 CFLAGS
(存储 C 编译器选项的环境变量)中添加 -prefer-non-pic
选项。non-pic 会使用位置独立的代码构建 PHP 并能提供 10% 的性能提升。还可以使用 CFLAGS
中的 -march
确定处理器的类型,例如 -march=opteron
表示处理器类型为 AMD Opteron。
提升速度的另一个途径是操作码优化。此处,Zend Engine 之类的软件会优化编译过程中产生的操作码,这理论上会减少代码执行的工作总量。
缓存和优化都是透明的,并不需额外的编程。要应用一些优化操作,执行结构分析,或检查代码在哪花费了时间。重复的工作既不经济又运算缓慢,这显然会产生瓶颈。通过代码优化弥补周期的不足是很有价值的 —— 但是在结构分析之前不要尝试进行任何优化。
如果 PHP 应用程序运行缓慢,可以使用分析器找出应用程序究竟在哪些方面浪费了时间。可以将语句、循环、函数、类或者是运行缓慢的库作为分析目标。如果不是时间而是内存使用出现了问题,那么一个优秀的分析器还可以显示组件的内存占用情况。
“为 PHP 应用提速、提速、再提速!” 系列文章的 第 1 部分 演示了如何使用 XCache(PHP 操作码缓存) 加速整个站点。XCache(仅是少数几种缓存包中的一种)保留了编译过程的输出,去掉了其他冗余的工作。只要页面没有发生变化,缓存后的页面就能够胜任 代理的作用。当页面发生变化时,缓存后的页面就会变为无效并被替换掉。
操作码缓存 —— 以及一个操作码优化器,通常由相同的包提供 —— 是一种加快站点响应的低成本技术。很多缓存包是免费的,并且是开源的,无需改变任何代码即可从中受益。
当 然,在某些应用程序中,相比较实际的执行时间,将 PHP 源代码文件翻译为其相应的操作码所需的时间微不足道。连接到远程数据库服务器,使用低效的 SQL 语句进行查询,以及其他大量解析和操作数据的工作都非常的繁琐,也因此增加了开销,甚至产生浪费。良好的网络设计和灵巧的数据库结构可以使时间冗长和查询 缓慢的情况有所改善,如果需要的话还可以向友好的专家请求帮助。但是,如果代码运行缓慢,您可能更希望自己处理。
但是从何开始呢?正如人们普遍认为的,在代码完成前调试代码的做法很不明智 —— 因为代码的首次实现可能会非常的迅速。当代码正确且能实现相应的功能时,不管其表面上看起来运行缓慢还是实际如此,首先要做的就是对其性能进行测试或基准测试。不执行这样的诊断而尝试去优化代码无疑是在黑暗中摸索。
一个简单的性能指标是挂钟时间(wall clock time), 或测量页面请求与完成呈现之间的实际延迟。对于某些情况 —— 比如在您自己的工作站本地运行的 Web 服务器、数据库和浏览器 —— 挂钟时间能够提供信息。然而,挂钟时间对于其他大多数情况而言并无实际意义,比如网络延迟时间、活动的 Web 服务器或者活动的数据库。
一种更精确的测量 —— 甚至可以测量运行单个源代码语句的时间 —— 可以采用代码分析器。 分析器通常被实现为 PHP 运行时引擎的扩展,记录语句开始和结束的 delta、记录程序开始和结束之间的 delta 并捕获对来到的请求形成响应的总时间。有了这种垂直度,就可以将语句、循环、函数、类或者是运行缓慢的库作为分析目标。如果不是时间而是内存使用出现了问 题,那么一个优秀的分析器还可以显示组件的内存占用情况。
PHP 的一个较流行的分析器是 Xdebug,它还为交互地调试 PHP 应用程序提供了服务器挂钩(hook)。(参见“调试的更好方法”以了解更多信息。该系列的另一部分将探讨高级交互式调试。) Xdebug 很容易从源代码构建,将其作为 Zend 扩展进行安装也非常简单。(现在已有针对某些平台的二进制文件。)当就绪后,对基于 PHP 页面的每个请求都将生成可在 KCacheGrind
中查看的数据集。
构建并安装 Xdebug
如果具备了 PHP 实用工具 phpize
和 php-config
, 而且具有对系统的 php.ini 配置文件的访问权,那么安装和设置 Xdebug 只需几分钟的时间。下面给出的指导说明针对 Linux®,不过在 Mac OS X 上的安装步骤实际上与此类似。(您可以从 Xdebug Web 站点找到针对 Microsoft® Windows® 的 Xdebug 预编译版本。)
Xdebug 的最新版本为 V2.0.0RC3(最终版本 V2.0.0 在您阅读此文时也许已经可用)。下载并解包 tarball,然后切换到源代码的子目录。确保 phpize
和 php-config
位于 shell 的 PATH,准备使用 phpize
进行构建。
<br>$ wget http://www.xdebug.org/files/xdebug-2.0.0RC3.tgz<br>$ tar xzf xdebug-2.0.0RC3.tgz<br>$ cd xdebug-2.0.0RC3/xdebug-2.0.0RC3<br>$ phpize<br>Configuring for:<br>PHP Api Version: 20020918<br>Zend Module Api No: 20020429<br>Zend Extension Api No: 20050606<br> 登入後複製 |
phpize
的产品是一个脚本 —— 名为配置 —— 它对余下的构建过程进行配置。要构建 Xdebug,在 make
后紧接着输入 ./configure
即可。
<br>$ ./configure<br>checking build system type... i686-apple-darwin8.8.1<br>checking host system type... i686-apple-darwin8.8.1<br>checking for egrep... grep -E<br>...<br>$ make<br>...<br>Build complete.<br>(It is safe to ignore warnings about tempnam and tmpnam).<br> 登入後複製 |
make
命令生成 Xdebug 扩展,xdebug.so。剩下的工作就是使用 sudo make install
进行安装。
$ sudo make install<br>Installing shared extensions: /usr/lib/php/extensions/no-debug-non-zts-20020429/<br> 登入後複製 |
注: 如果在终端窗口中运行最后一个命令,请选择并复制最后一步中发出的目录。在下一个步骤中将会用到它。
最后,要使配置数据可视化,必须使用 KCacheGrind
和 GraphViz
。包含 K Desktop Environment (KDE)的 Linux 发行版很可能已经含有了 KCacheGrind
和 GraphViz
。如果没有包含,适合您所使用的 Linux 的那些版本也不难找到。Debian 用户可以使用 Advanced Packaging Tool (APT) 快速安装 KCacheGrind
和 GraphViz
以及所有包的依赖关系。
<br>$ apt-cache search kcachegrind<br>valgrind-callgrind - call-graph skin for valgrind<br>kcachegrind - visualisation tool for valgrind profiling output<br>kcachegrind-converters - format converters for KCachegrind profiling visualisation tool<br>$ apt-cache search graphviz<br>graphviz - rich set of graph drawing tools<br>graphviz-dev - graphviz Libs and Headers against which to build applications<br>graphviz-doc - additional documentation for graphviz<br>libdeps-renderer-dot-perl - DEPS renderer plugin using GraphViz/dot<br>...<br>$ sudo apt-get install kcachegrind graphviz <br>...<br> 登入後複製 |
如果没有将 KDE 安装到系统中,KCacheGrind
、GraphViz
以及所有必要的内容将占用大约 90 MB 的磁盘空间。
配置 Xdebug
安装了 Xdebug 扩展后,就可以准备启用和配置该扩展了。在文本编辑器中打开 php.ini,并添加以下代码行。
<br>zend_extension = /usr/lib/php/extensions/no-debug-non-zts-20020429/xdebug.so<br>xdebug.profiler_output_dir = "/tmp/xdebug/"<br>xdebug.profiler_enable = Off<br>xdebug.profiler_enable_trigger = 1<br> 登入後複製 |
第一行 zend_extension
加载 Xdebug 扩展。第二行命名放置分析器输出的目录。如果需要的话,创建命名的目标并更改其模式以允许用户对 Web 服务器进行写访问。
第三行禁用了分析器。然而,第四行将在设置 HTTP GET
或 POST
参数 XDEBUG_PROFILE
时启用分析器。(如果您希望一直使用分析器,在第三行代码中将 Off
更改为 On
。)
添加这几行代码并验证了输出目录是可写的,然后重新启动 Web 服务器。对于其他 PHP 扩展,要验证 Xdebug 是否安装并可用,可以创建一个简单的骨架 PHP 程序来调用 phpinfo()
并查看结果。应该能够看到类似于图 1 所示的内容。(为简便起见,省略了完整输出的部分内容。)
您还可以向下滚动到 Zend 徽标。如果正确安装并配置了 Xdebug,它将显示在徽标的旁边。
使用分析器
要分析代码,只需将浏览器指向 PHP 应用程序即可。如果您将分析器设置为对每个触发逐个解决的方式,将 XDEBUG_PROFILE=1
追加到 URL 中,或者,像下面一样,将参数嵌入到表单中。
作为一个示例,我们来分析一下这个简单的 ACME Fibonacci Maker,fibonacci.php,如清单 5 所示。为方便起见,将 XDEBUG_PROFILE
参数设置在表单的隐藏变量内。(当代码投入生产时,很可能将禁用 Xdebug,呈现这个变量将不会造成什么损失。)
<br><?php <br /> function fib($nth = 1) {<br> if ( $nth return( $nth ); <br> }<br> <br> return( fib( $nth - 1) + fib( $nth - 2 ) );<br> }<br>?> <br> <br><br> <br> <title>ACME Fibonacci Maker</title><br> <br> <br> <h2>Try the ACME Fibonacci Maker!</h2><br> 登入後複製 if ( ! empty( $_REQUEST['n'] ) ) { $n = $_REQUEST['n'] % 10; $suffix = array( 1 => "st", 2 => "nd", 3 => "rd" ); if ( $_REQUEST['n'] 20 ) { $suffix = $suffix[$n]; } else { $suffix = 'th'; } echo ' The ' . $_REQUEST['n'] . $suffix .' Fibonacci number is '; } ?> |
将浏览器指向 http://localhost/fibonacci.php(或者合适的 URL)并输入数字 —— 比如,16。其结果 —— Fibonacci 系列的第 16 个元素 —— 如图 2 所示。
如果将分析器输出目录中的内容(名为 php.ini)列出来的话,应该能看到类似 cachegrind.out.951917687 这样名称的文件。cachegrind.out. 前缀是固定的。默认情况下,数值后缀是目录路径到 fibonacci.php 文件的 CRC32 散列。因此,如果每一个应用程序都位于自己的目录,那么每个程序的输出将根据文件名而被分隔。(如果您更喜欢将输出与时间相关联,将下面这行代码:
xdebug.profiler_output_name = timestamp<br> 登入後複製 |
添加到 php.ini。)
从终端窗口启动 KCacheGrind
并打开 cachegrind.out.951917687。将立即打开一个类似于图 3 的新窗口。
单击 Callees 选项卡,双击源代码中突出显示的行,并从 Grouping 列表选择 Source File 。所看到的视图应变为类似图 4 所示的内容。
正如您预期的一样,实际上全部的处理时间(70,989 毫秒的 99.87%)都花费在 3193 次对 fib()
函数的调用上了。要加快该应用程序(随着进一步执行 Fibonacci 序列,程序会随之变慢),应该避免重新计算 Fibonacci 数字这样代价高昂的重复工作。事实上,ACME Fibonacci Maker 能够很好地进行计算重用。
下面展示了 fib()
函数的优化版本。新的版本用内存换来了时间上的节省,因为它保留了中间的计算以便以后使用。图 5 展示了分析结果:与上次的 3192 次函数调用相比,这里仅需要 30 次调用(并且只有一半的调用需要计算结果),而时间则减少为只有 20 毫秒。
<br>function fib($nth = 1) {<br> static $fibs = array();<br><br> if ( ! empty ($fibs[$nth] ) ) { <br> return( $fibs[$nth] );<br> }<br> <br> if ( $nth $fibs[$nth] = $nth;<br> }<br> else { <br> $fibs[$nth - 1] = fib( $nth - 1 );<br> $fibs[$nth - 2] = fib( $nth - 2 );<br> $fibs[$nth] = $fibs[$nth - 1] + $fibs[$nth -2];<br> }<br> <br> return( $fibs[$nth] );<br>}<br>?><br> 登入後複製 |
虽然单次运行应用程序能够指出一些问题(可以试试上面原始的应用程序中的 Fibonacci 序列的第 50 个元素 ),通常,还是需要通过几次调用收集统计信息以及查看模式。
如果保留默认的 “crc32” 命名模式,每次运行 fibonacci.php 时,将重写数据文件。然而,可以通过在 php.ini 中设置 xdebug.profiler_append = 1
改变这种行为并将后续运行追加到相同的文件。更改之后重新启动 Web 服务器。
图 6 显示了三次运行 Fibonacci Maker 之后数据合计的示例。总时间稍大于两秒;其中 99.97% 的时间花费在了 fib()
上。图 6 显示了 Call Graph 选项卡,它由 GraphViz
的 dot
工具生成。关于 KCacheGrind
的具体用法不在本文讨论的范围之内,但是可以从网上获得其完整的文档。KCacheGrind
可以以很多种方法对数据进行交叉分析,根据您希望解决的问题选择合适的方法。
|
分析类
如 果没有具体的代码,那么很难演示具有意义的分析,下面这个示例是十分典型的代码,展示了从中所能获得的信息。清单 7 显示了一个装配玩具火箭的应用程序(人为设计)。这种玩具火箭由几个部分组成,生产每一个部分都需要一定的时间。在 PHP 中,使用类代表每个组成部分,使用实例方法表示每个部分的构造时间。您可以将这个玩具看作是一个应用程序,并把每个部分看作是该应用程序的功能。
<br><?php <br /> define( 'BOOSTER', 5 );<br> define( 'CAPSULE', 2 );<br> define( 'MINUTE', 60 );<br> define( 'STAGE', 3 );<br> define( 'PRODUCTION', 1000 );<br> <br> class Part {<br> function Part() {<br> $this->build( MINUTE );<br> }<br> <br> function build( $delay = 0 ) {<br> if ( $delay return;<br> <br> while ( $delay-- > 0 ) {<br> }<br> }<br> }<br> <br> class Capsule extends Part {<br> function Capsule() {<br> parent::Part();<br> $this->build( CAPSULE * MINUTE );<br> }<br> }<br> <br> class Booster extends Part {<br> function Booster() {<br> parent::Part();<br> $this->build( BOOSTER * MINUTE );<br> }<br> }<br> <br> class Stage extends Part {<br> function Stage() {<br> parent::Part();<br> $this->build( STAGE * MINUTE );<br> }<br> }<br> <br> class SpaceShip {<br> var $booster;<br> var $capsule; <br> var $stages;<br> <br> function SpaceShip( $numberStages = 3 ) {<br> $this->booster = new Booster();<br> $this->capsule = new Capsule();<br> $this->stages = array();<br> <br> while ( $numberStages-- >= 0 ) {<br> $stages[$numberStages] = new Stage();<br> }<br> }<br> }<br> <br> $toys = array();<br> $count = PRODUCTION;<br> <br> while ( $count-- >= 0 ) {<br> $toys[] = new SpaceShip( 2 );<br> }<br>?><br><br><br><br><title> <br>Toy Factory Output<br> </title><br><br><br> <h1>Toy Production</h1><br> <p>Built echo PRODUCTION . ' toys' ?></p><br><br><br> 登入後複製 |
运行这些代码将生成一个新的数据文件。同样,将数据加载到 KCacheGrind
。如果切换到 Source 和 Call Graph 选项卡,将看到类似图 7 所示的视图。
Flat Profile 窗格(左面)显示了应用程序调用的所有函数(方法)。最左面的列展示了近似的累计总数,第二列展示了每种方法的单独测试,第三列列出了调用该方法的次数。 在调用图表中使用有颜色的方块反映图表内容,这非常方便,能够很容易地将事件序列与其花费的时间关联起来。
很明显,构建阶段所使用的时间代价最昂贵。构建每一部分所需的系统开销(使用 Part
的构造器表示)次之。再看一下 PHP 自身的 define()
函数,它只花费了很少的开销。
最后,还可以查看内存的使用情况。从靠近顶部的下拉菜单中选择 Memory 和 Class,然后切换到顶部以及底部的 Types 和 Caller Map 选项卡。您看到的屏幕应该类似图 8。
找回周期
和 其他众多 PHP 扩展一样,Xdebug 容易构建、安装快捷且易于配置 —— 所有这些工作 10 分钟内即可完成。如果您已经优化了 Apache 安装并且对应用程序进行了缓存,但是性能仍然很差,那么可以考虑一下代码的运行。算法是否有效?代码是否过于复杂?是否重复实现了 PHP 已提供的函数?
当然,如果不能判断出应用程序的瓶颈所在,那么就必须进行查找并加以修复。不要只凭猜测 —— 要进行分析!您可能会惊讶于宝贵的计算周期是如何被轻意耗费掉的。
并且永远不要忘记:要在生产服务器中禁用 Xdebug,因为启用它总会增加系统开销。
“为 PHP 应用提速、提速、再提速!” 系列文章探讨了 XCache 和 XDebug,现在将探讨 Memcache 守护程序。Memcache 守护程序 (memcached) 是一种高性能分布式对象缓存。memcached 的安装位置介于应用程序与数据存储之间,它将把对象保存在 RAM 中。每次缓存命中将替换到数据库服务器的一次往返,使应用程序运行得更快。
本系列文章的前两篇文章提供了为 PHP 应用程序提速的技术。第 1 部分 介绍了 XCache,它是把 PHP 操作码缓存到内存中的 PHP 扩展。XCache 将避免代价高昂且(严格来说)不必要的花费来重新编译相同的 PHP 代码去交付一个页面。XCache 是免费的开源软件,安装费时不多却可提供巨大回报。第 2 部分 介绍了 XDebug,它是配置 PHP 代码的 PHP 扩展。XDebug 是类似于软件 x 光照片的内容:它将深入应用程序,揭露内部工作原理,并且揭示代码如何度过其周期。拥有了 XDebug 度量之后 —— 而不是之前 —— 您可以优化代码来调整算法、减少瓶颈并减轻过多的内存使用。
注:XCache 适于生产使用。在开发过程中使用 XDebug 最佳,因为其计算负载在活动计算机中是十分浪费的。
这一次,让我们探究第三种尤为有效的性能增强因素。名为 memcached 的 Memcache 守护程序是一种高性能的分布式对象缓存。memcached 的安装位置介于应用程序与数据存储之间,它将把对象保存在 RAM 中。
Memcache PHP 扩展将为访问缓存提供一个简单的应用程序接口 (API)。要使用缓存,需要调用 API 来确定先前是否已缓存了对象。如果已经缓存,只需检索该对象并继续进行处理。否则,转至数据库、获取必需的数据、将其映射到对象中并把它添加到缓存中。在 那里,memcached 将最小化或消除针对您先前处理过的信息的数据库查询。
如果 XCache 和 XDebug 是涡轮增压器,那么 memcached 就是喷气发动机。请准备好点燃加力燃烧室。
更高的速度需求
通 常,PHP 应用程序中最耗费时间的任务是数据检索。实际上,用于从存储库(文件或数据库服务器)中获取信息的时间远高于编译并且甚至是执行 PHP 程序所需耗费的时间。连接至数据库服务器所需的时间是一次延迟,等待查询完成添加附加停顿,并且结果的传输甚至会导致更多延迟。此外,如果代码使用对象, 则有指向对象的导致负担过重的映射平面行。
MySQL 可以使用其查询缓存为查询阶段提速。您还可以复制数据库(一个主数据库,多个副本),从而在多个 CPU 之间分担查询处理的负担。但是,一旦底层表改变,MySQL 查询缓存的内容就会过时。而且,仅当一个查询与先前查询相同时,查询缓存才会被命中。副本也有限制。例如,无法分布数据库写操作。
从根本上说,虽然查询缓存和数据库副本都是有目的的并且在整体工作负载管理策略中占有地位(查询缓存将消耗一些内存,但是在其他方面却很节省;副本将把灾难停机的风险降至最低),但是连接和传输时间不变。
Memcache PHP 扩展将把对象缓存到 RAM 中。每次缓存命中将替换到数据库服务器的一次往返,使应用程序运行得更快。您很可能会发现 memcached 也(间接地)提高了数据库服务器的性能;由于 memcached 将用作替代持久性存储,因此到达数据库服务器的请求更少,使数据库服务器可以更高效地响应已收到的查询。
您可以在一台或多台服务器上运行 memcached,并且将在所有节点之间复制缓存的内容。如果服务器出现故障,客户机 API 软件将把缓存读写操作重新路由到正常运行的备用服务器上。
不同于 XCache,您必须修改代码才能集成 memcached。不过,如果您已经小心地隔离了一些对象方法内的数据库访问代码,则修改将很可能十分轻微且集中。
Danga Interactive 编写的 Memcache 守护程序是由 Berkeley Software Distribution (BSD) License 的自由条款许可的免费开源软件。守护程序应当可以轻松地构建在 UNIX® 和 Linux® 系统上,也可以构建在 Mac OS X 和 Microsoft® Windows® 上。很多 Linux 发行版都提供了 memcached 包;请查看包存储库。如果使用 Mac OS X 或 Windows 并且更喜欢便捷的预构建二进制,则可以通过简单的 Google 搜索在 Web 上找到此类软件。
(重新)构建 PHP
让 我们在 Debian Linux 上构建、安装和部署 memcached。要加速执行过程并允许您独立于现有 Web 服务器基础设施来测试 memcached,请使用 XAMPP Apache 发行版作为构建的基础。XAMPP 十分易于安装并且包含 Apache V2、MySQL、PHP V4 和 V5、Perl、许多库和许多 Web 应用程序(例如 phpMyAdmin)。如果从未从头开始构建 Linux、Apache、MySQL 和 PHP (LAMP) 堆栈,或者如果需要避免与此类努力相关联的争辩,则以 XAMPP 开始是十分理想的。
注:如果您以前通过源代码构建了 PHP 并保留了文件,则只需把 --enables-memcache
选项添加到配置开关列表中,并且跳过构建 memcached 和 PHP Memcache 扩展以前所示的步骤。
要构建和部署 memcached,您需要 XAMPP 发行版,包括 XAMPP 开发文件、XAMPP 附带的 PHP 版本的源代码以及 memcached 的源代码和 Memcache 的 PHP 扩展。您可以从 XAMPP 下载 XAMPP 二进制和 XAMPP 开发文件(构建附加组件的必备文件)。您还可以使用 wget
快速获取软件:
$ wget 'http://www.apachefriends.org/download.php?xampp-linux-1.6.tar.gz' <br>$ wget 'http://www.apachefriends.org/download.php?xampp-linux-devel-1.6.tar.gz'<br> 登入後複製 |
前一个 tarball(tarball 是压缩的 .tar 文件并且通常以后缀 .tar.gz 为结尾)包含二进制;后一个 tarball 包含针对 XAMPP 系统构建代码所需的题头文件。
虽然可以把 XAMPP 锚定在文件系统中的任意位置,但是请把压缩包安装到 /opt 中。同时把开发文件安装到 /opt 中。使用 /opt 将使剩下的构建过程变得更轻松。对 tar 使用 -C
选项把文件直接解压缩到 /opt 中,如下所示:
<br>$ sudo mkdir /opt<br>$ tar xzf xampp-linux-1.6.tar.gz -C /opt<br>$ tar xzf xampp-linux-devel-1.6.tar.gz -C /opt<br>$ ls -CF /opt/lampp<br>RELEASENOTES error/ info/ logs/ phpsqliteadmin/<br>backup/ etc/ lampp* man/ sbin/<br>bin/ htdocs/ lib/ manual/ share/<br>build/ icons/ libexec/ modules/ tmp/<br>cgi-bin/ include/ licenses/ phpmyadmin/ var/<br> 登入後複製 |
接下来,下载并解压缩 XAMPP 附带的 PHP 版本的源代码(XAMPP V1.6 绑定了 PHP V4.4.6);从 PHP.net 下载 PHP V4.4.6 的代码。wget
将再次使您可以轻而易举地完成任务:
$ wget http://us2.php.net/get/php-4.4.6.tar.bz2/from/www.php.net/mirror<br>$ tar xjf php-4.4.6.tar.bz2<br>$ cd php-4.4.6<br> 登入後複製 |
接下来,修改 XAMPP 的 PHP 构建脚本重新构建 PHP 以启用 Memcache。您可以在 /opt/lampp/share/lampp/configures.tar.gz 中找到原始构建脚本(和其他构建脚本)。使用以下代码解压缩 PHP V4 构建脚本:
$ tar xzfv /opt/lampp/share/lampp/configures.tar.gz \<br> php/configure-php4-oswald<br> 登入後複製 |
打开 configure-php4-oswald 并添加 --enable-memcache
(如 果系统没有那些数据库,您还可能发现有必要删除选项,例如特定于 Oracle 和 PostgreSQL 的那些选项)。清单 2 展示了在测试系统上用于重新构建 PHP 的修改后的脚本。(PHP 构建过程依赖于许多实用程序和开发库,例如 Flex、Bison、libxml 和 PCRE。您可能需要安装附加软件包才能准备好进行这些构建,这取决于 Linux 发行版和 PHP 配置的内容)。
<br>(<br> cd /opt/lampp/bin<br> rm phpize phpextdist php-config php<br> rm -rf /opt/lampp/include/php<br>)<br><br>make distclean<br><br>export PATH="/opt/lampp/bin:$PATH" <br>export CFLAGS="-O6 -I/opt/lampp/include/libpng \<br> -I/opt/lampp/include/ncurses \<br> -I/opt/lampp/include -L/opt/lampp/lib" <br><br>./configure \<br> --prefix=/opt/lampp \<br> --with-apxs2=/opt/lampp/bin/apxs \<br> --with-config-file-path=/opt/lampp/etc \<br> --with-mysql=/opt/lampp \<br> --enable-inline-optimation \<br> --disable-debug \<br> --enable-memcache \<br> --enable-bcmath \<br> --enable-calendar \<br> --enable-ctype \<br> --enable-dbase \<br> --enable-discard-path \<br> --enable-exif \<br> --enable-filepro \<br> --enable-force-cgi-redirect \<br> --enable-ftp \<br> --enable-gd-imgstrttf \<br> --enable-gd-native-ttf \<br> --with-ttf \<br> --enable-magic-quotes \<br> --enable-memory-limit \<br> --enable-shmop \<br> --enable-sigchild \<br> --enable-sysvsem \<br> --enable-sysvshm \<br> --enable-track-vars \<br> --enable-trans-sid \<br> --enable-wddx \<br> --enable-yp \<br> --with-ftp \<br> --with-gdbm=/opt/lampp \<br> --with-jpeg-dir=/opt/lampp \<br> --with-png-dir=/opt/lampp \<br> --with-tiff-dir=/opt/lampp \<br> --with-freetype-dir=/opt/lampp \<br> --without-xpm \<br> --with-zlib=yes \<br> --with-zlib-dir=/opt/lampp \<br> --with-openssl=/opt/lampp \<br> --with-expat-dir=/opt/lampp \<br> --enable-xslt \<br> --with-xslt-sablot=/opt/lampp \<br> --with-dom=/opt/lampp \<br> --with-ldap=/opt/lampp \<br> --with-ncurses=/opt/lampp \<br> --with-gd \<br> --with-imap-dir=/opt/lampp \<br> --with-imap-ssl \<br> --with-imap=/opt/lampp \<br> --with-gettext=/opt/lampp \<br> --with-mssql=/opt/lampp \<br> --with-mysql-sock=/opt/lampp/var/mysql/mysql.sock \<br> --with-mcrypt=/opt/lampp \<br> --with-mhash=/opt/lampp \<br> --enable-sockets \<br> --enable-mbstring=all \<br> --with-curl=/opt/lampp \<br> --enable-mbregex \<br> --enable-zend-multibyte \<br> --enable-exif \<br> --enable-pcntl \<br> --with-mime-magic \<br> --with-iconv <br><br>make <br><br>sudo make install <br><br>exit 1<br> 登入後複製 |
在脚本的末尾,您的 XAMPP 安装将有一个新的独立运行的能够使用 memcache 的 PHP V4 副本。如果需要测试构建,请停止所有正在运行的 Apache 和 MySQL 副本,包括在 XAMPP 外部的那些副本,并且运行以下命令:
$ sudo /opt/lampp/lampp start<br> 登入後複製 |
这将启动 XAMPP 版本的 Apache 和 MySQL,包括新的 PHP V4 模块。如果需要使 Apache 生产服务器的运行不受干扰,则可以编辑文件 /opt/lampp/etc/httpd.conf 并把 Listen
端口参数改为 8080
(或其他可用端口)。然后您可以用以下命令单独启动 XAMPP 的 Apache 服务器:
sudo /opt/lampp/bin/apachectl start<br> 登入後複製 |
把浏览器指向 http://localhost,然后您应当会看到类似于图 1 的内容。
图 1. XAMPP for Linux 初始页面:检验 XAMPP 和 PHP 是否已成功安装要检验您的 PHP 构建是否已安装,请单击左侧的 phpinfo() 链接。您应当会看到类似图 2 中所示的统计信息。PHP 的版本应当是 4.4.6,构建选项应当包括 --enable-memcache
,并且 php.ini 的工作版本应当驻留在 /opt/lampp/lib 中。
构建 PHP Memcache 扩展
新的 PHP 就绪后,下一步是构建和安装 PHP Memcache 扩展。该扩展将提供访问 Memcache 守护程序的服务所需的 API。
首先将检索 Memcache 扩展的源代码。您可以再次使用 wget
来获得源代码,请使用以下命令:
$ wget http://pecl.php.net/get/memcache-2.1.0.tgz<br> 登入後複製 |
构建 Memcache 扩展的过程与构建其他 PHP 扩展的过程完全相同:
phpize
,后接 ./configure
、make
和 make install
。phpize
。phpize
的所有目录之前:$ cd memcache-2.1.0<br>$ export PATH=/opt/lampp/bin:$PATH<br>$ phpize<br>$ ./configure<br>$ make<br>...<br>$ sudo make install<br>Installing shared extensions: <br>/opt/lampp/lib/php/extensions/no-debug-non-zts-20020429/<br> 登入後複製 |
构建将把一个名为 memcache.so 的新文件放入扩展目录。要加载并应用扩展,您必须编辑 php.ini。打开 XAMPP PHP 配置文件 /opt/lampp/etc/php.ini 并添加清单 3 中的代码行。
清单 3. 编辑 php.ini<br>extension=memcache.so<br>memcache.allow_failover = 1 <br>memcache.max_failover_attempts=20 <br>memcache.chunk_size =8192 <br>memcache.default_port = 11211 <br> 登入後複製 |
第 1 行将加载 Memcache 扩展。其他四行是用于控制扩展的参数。按顺序,从顶部到底部:
memcache.allow_failover
1
(true)。memcache.max_failover_attempts
memcache.allow_failover
为 false,则将忽略此参数。默认值为 20
。memcache.chunk_size
8192
字节 (8 KB),但是如果设置为 32768
(32 KB),则可以获得更好的性能。memcache.default_port
11211
。要确定构建现在是否能够完全运行,请使用以下命令重新启动 XAMPP Apache Web 服务器:
$ sudo /opt/lampp/bin/apachectl restart<br> 登入後複製 |
如果重新访问 XAMPP phpinfo()
页面,您应当会看到类似图 3 中的 Memcache 部分。
构建 Memcache 守护程序
此过程(表面上有些冗长)还有一个额外的步骤:构建和部署为数据管理 RAM 缓存的 Memcache 守护程序。守护程序依赖于 libevent,因此必须构建和部署该库,然后再编译 memcached:
$ wget http://www.monkey.org/~provos/libevent-1.3b.tar.gz<br>$ wget http://www.danga.com/memcached/dist/memcached-1.2.1.tar.gz<br> 登入後複製 |
接下来,解压缩 tarball 来为每个包生成一个目录:
$ tar xzf memcached-1.2.1.tar.gz <br>$ tar xzf libevent-1.3b.tar.gz<br> 登入後複製 |
要继续执行操作,请依次构建每个包,从库开始。要使所有文件包含在 /opt 中,请在运行配置时使用 --prefix
选项。下面的指令将构建和安装 libevent。
<br>$ cd ../libevent-1.3b<br>$ ./configure --prefix=/opt/lampp<br>...<br>$ make<br>$ sudo make install<br>...<br>/usr/bin/install -c .libs/libevent.lai /opt/lampp/lib/libevent.la<br>/usr/bin/install -c .libs/libevent.a /opt/lampp/lib/libevent.a<br>chmod 644 /opt/lampp/lib/libevent.a<br>ranlib /opt/lampp/lib/libevent.a<br>PATH= 登入後複製 |