关于改善xhprof使用情况的设想

自从去年将xhprof用在生产环境以来,对生产环境的程序调试,性能优化都带来很多便利。但是在使用过程中,还是有一些细节需要改善。 问题 xhprof的profile日志直接以文件形式保存在生产服务器上,需要定时清理,或者收集起来移动到查看日志的工具机上。 由于xhprof生成的profile是一个大数组,所以保存到文件时使用了标准的php serialize,日志文件偏大,一个不留神就容易占用很多服务器磁盘空间。 查看日志列表时,一个个点开查看比较费劲。 针对这几个问题,我有一些小小的设想。 日志存放 部署一个中央日志服务器,采用facebook的scribe来收集日志。生产环境的服务器产生的xhprof日志,都写入到scribe的客户端,由客户端自动同步到中央日志服务器的scribe上,不占用本地的存储空间。在代码上的改动也比较小,只要基于iXHProfRuns接口实现一个XhprofRuns类,调整save_run方法的存储方式即可。 更换序列化方法 xhprof默认是将profile信息用php原生的序列化方法处理后进行保存,而我在前两天比较过igbinary vs serialize vs json_encode的性能和占用字节数,这个测试里igbinary在各方面都有一定优势,尤其是占用存储空间会大幅度减小,所以我只要更换序列化方法为igbinary_serialize即可获得改善。 优化列表展示 我已经厌倦挨个查看profile日志的大图,费时费力还没有针对性。所以我现在的做法是,在profile日志的列表中将前1000个日志的总体执行时间直接输出到列表中,并且将执行时间过长的日志用红色粗体标识。做了这个小小的改动之后,当我想要去视察一下运行情况时,就把日志列表中那些红通通的链接点开看看就行了,真正的省时省力。 如何从xhprof日志文件中获取执行时间?简单的代码如下 /** * 由xhprof日志获得执行时间 * * @param string $log xhprof日志的文件路径 * @return int 执行时间 */ function getSpentTime($log) { $profile = unserialize(file_get_contents($log)); return $profile[‘main()’][‘wt’] / 1000; }

php的echo为什么这么慢

作为一个行走江湖多年的老中医,今天受命去解决一例前端页面展现缓慢的问题。问题页的情况如下: apache + php 使用smarty模板输出内容 页面最终输出内容较大,80k+ 页面执行时间在500ms以上 祭出法宝xhprof对问题页面做了细致检查,发现页面的瓶颈竟然是模板(编译后的)中的一个echo语句,这个echo语句输出的字符串比较大,大概是50k+字节,花费时间为400多毫秒,占整个页面执行时间的80%。这样的echo输出在站点首页中其实是很常见的事情,没有数据库操作,按道理执行时间不应该这么长。 于是猛力使用搜索技能,最终在php手册的echo部分找到了一些蛛丝马迹,早在2003年就有前辈认为通过echo输出大字符串到客户端会引起服务器的性能问题,据我测试,在这个场景下使用print其实也是一样的慢。建议的解决办法是把字符串切割成更小的字符串输出,展现速度会有提升,输出函数如下: <?php function echobig($string, $bufferSize = 8192) { $splitString = str_split($string, $bufferSize); foreach($splitString as $chunk) echo $chunk; } ?> 但是上面的处方不太对症,整个echobig的输出时间仍然在400毫秒左右,没有太大改善。 考虑到是输出大量内容到客户端比较慢,于是检查了apache的配置,原来还没打开deflate进行压缩,遂启用之。再次使用xhprof进行检查,这条echo的输出时间降低到5ms左右。 400ms到5ms,一个配置问题会产生80倍的差距,还真是省老钱了。这个故事告诉我们,压缩输出真的很重要。 Update 实际上这个问题也可以通过调整webserver的output buffer来解决一部分,参考: Why echo is slow in PHP and how to make it really fast 加速PHP的ECHO

在生产环境中使用php性能测试工具xhprof

xhprof是facebook开源出来的一个php性能测试工具,也可以称之为profile工具,这个词不知道怎么翻译才比较达意。跟之前一直使用的xdebug相比,有很多类似之处。以前对xdebug有一些记录还可以供参考,但是它的缺点是对性能影响太大,即便是开启了profiler_enable_trigger参数,用在生产环境中也是惨不忍睹,cpu立刻就飙到high。 而xhprof就显得很轻量,是否记录profile可以由程序控制,因此,用在生产环境中也就成为一种可能。在它的文档上可以看到这样一种用法: 以万分之一的几率启用xhprof,平时悄悄的不打枪。 if (mt_rand(1, 10000) == 1) { xhprof_enable(XHPROF_FLAGS_MEMORY); $xhprof_on = true; } 在程序结尾处调用方法保存profile if ($xhprof_on) { // stop profiler $xhprof_data = xhprof_disable(); // save $xhprof_data somewhere (say a central DB) … } 也可以用register_shutdown_function方法指定在程序结束时保存xhprof信息,这样就免去了结尾处判断,给个改写的不完整例子: if (mt_rand(1, 10000) == 1) { xhprof_enable(XHPROF_FLAGS_MEMORY); register_shutdown_function(create_funcion(”, “$xhprof_data = xhprof_disable(); save $xhprof_data;”)); } 至于日志,我暂时用的是最土的文件形式保存,定期清除即可。 BTW:xhprof生成的图形方式profile真是酷毙了,哪段代码成为瓶颈,一目了然。

hidef —— 进一步提高define的性能

用apc_define_constants可以提升批量define的性能,但是每个请求,都免不了进行 读cache -> 批量define 这样一个过程,因此,这也算不上提升define性能的终极手段。 在翻看apc的手册时,无意中看到有这么一小段介绍: For a better-performing solution, try the » hidef extension from PECL. 这里提到了一个叫hidef的扩展,看说明上的意思,应该是可以通过ini文件来批量定义常量。 Allow definition of user defined constants in simple ini files, which are then processed like internal constants, without any of the usual performance penalties. hidef的使用 首先需要定义一个ini文件,里面需要定义常量的基本类型 [hidef] float PIE = 3.14159; int ANSWER = 42; 然后apache启动的时候,会初始化这些常量,这样就不用每个php进程来重复定义常量了。 性能真的有提升么 […]

用apc来提高define的性能

php下的define()性能是很烂的,速度慢,尤其是当一个程序中有大量的常量需要定义,这个情况尤为明显。而apc这个扩展不仅仅提供了php的opcode缓存,同样也可以提供一些基本的cache功能,在apc 3.0.0版本之后,它甚至可以缓存常量。 一个用apc批量定义常量的例子 <?php $constants = array( ‘ONE’ => 1, ‘TWO’ => 2, ‘THREE’ => 3, ); apc_define_constants(‘numbers’, $constants); echo ONE, TWO, THREE; ?> 这个例子中,使用apc_define_constants批量定义常量,它的头一个参数”numbers”是cache的key,第二个参数则是常量定义组成的数组。它在这一次操作中,就已经把这组常量加入到cache中了。 用apc批量载入常量 在批量定义过常量之后,以后要载入这些常量,可以用对应的方法apc_load_constants来装入。 <?php apc_load_constants(‘numbers’); echo ONE, TWO, THREE; ?> 这样做会比用php挨个define快上很多。 实际应用 实际的应用场合中,可能需要检查一下常量有没有被定义,然后再进行后续逻辑,比如: if(!apc_load_constants(‘numbers’)) { $constants = array( ‘ONE’ => 1, ‘TWO’ => 2, ‘THREE’ => 3, ); apc_define_constants(‘numbers’, $constants); } […]

查看xdebug profile文件的几个程序

在优化php代码执行效率过程中,有个好办法是利用xdebug生成profile文件,然后查看整个程序的瓶颈在哪里。现在xdebug profile的查看程序有好几个,在这里罗列一下. Wincachegrind Wincachegrind是windows下的profile查看程序,使用起来感觉还不错,profile文件太大的话偶尔会崩溃。 今天在高春辉的博客上看到这些: 最近又开始拿 Xdebug 和 wincachegrind 对项目的 PHP 代码进行分析和优化,但是发现和自己输出的执行时间总是相差十倍,差的不是零头,而是十倍。 上网搜索了一下,原来在 Xdebug 2.0.0RC4 版本开始,对 profiler 日志中的时间单位进行了修改。 (“Use µ seconds instead of a tenths of µ seconds to avoid confusion in profile information. ”) 而 wincachegrind 又不再升级维护了,所以凡是用 2.0.0RC4 以及之后版本的 Xdebug 输出的 profiler 日志用 wincachegrind 来分析的话,都会有十倍的时间差距。 他已经提供了hack后的版本,可以解决时间差距的问题,有兴趣的同学可以试试。 CachegrindVisualizer CachegrindVisualizer是一个xdebug的profile文件查看客户端,采用Adobe的AIR制作。 更详细的介绍可以看以前写的关于CachegrindVisualizer的介绍。 Kcachegrind Kcachegrind是linux下的一个图形化profile查看工具,功能很强劲。 Callgrind uses runtime […]

如何避免使用php的require_once

我们知道,在php中使用require_once/include_once虽然方便,但是代价昂贵,据测试数据来看,require_once比require慢3-4倍,所以在php开发中,我们应该尽量使用require/include。 列一下俺常用的避免require/include的方法。 使用__autoload php5可以使用__autoload来避免require,用的好的话,代码里头甚至看不到几个require,实在是安逸啊。测试结果表明,使用__autoload之后的new Foo; 比 require_once ‘foo.php’; new Foo; 大概要快3倍左右。 补充:为了避免autoload冲突,可以考虑使用spl_autoload_register(PHP 5 >= 5.1.2)来改变魔术函数__autoload的行为。 使用defined检测是否载入过 在代码开头使用defined检测是否定义过对应的常量,如果有的话,直接return。 <?php if(!defined(‘_MYCLASS_’)) return; define(‘_MYCLASS_’, 1); class MyClass { … } ?> 测试了一下,defined的性能也不是太好… require前检查 用class_exists或者function_exists检查一下,确认没有载入过再出手,至少比require_once能快上3倍。php4也可以用上。 class_exists(‘myClass’) or require(‘/path/to/myClass.class.php’);

用xdebug优化php的三个小窍门

xdebug的2.0正式版已经发布了。这个工具用在php的代码调试,优化方面效果很不错。下面贴上俺使用过程中的几个小窍门。 xdebug生成profile文件,可以用KCachegrind来查看,但是这个工具只在linux下面可用,没有windows下的版本。这里推荐一个win下的免费工具——wincachegrind,也可以查看xdebug的profile文件,用来分析php代码运行情况足够用了(偶尔不太稳定)。 xdebug一般情况下只会对一个请求做profile记录,如果需要查看几个请求的运行情况合集,可以设置xdebug.ini的 xdebug.profiler_aggregate = 1 记得重启你的apache。 如果在xdebug.ini里设置了 xdebug.profiler_enable = 1 那么每次程序运行期间xdebug都会记录profile,这样对程序的运行速度有很大的影响。为了避免这一情况发生,可以让xdebug仅在需要的时候运行——设置 xdebug.profiler_enable_trigger = 1 这样,只有你用get/post方式提交XDEBUG_PROFILE变量的情况下,xdebug才会开始干活。 另:将最新版本的xdebug和APC同时使用,没有出现兼容性问题,运行良好。

优化AWSTATS性能的7个建议

对于一个访问量巨大的网站来说,使用awstats来分析其日志是一件很痛苦的事情,这往往需要耗费很多时间才能得到一个结果,偶尔还会让机器内存耗尽。在awstats的文档中,找到几个关于性能优化的建议,如下: 关掉DNSLookup(DNSLookup不能设置为1). 虽然这样你会丢失一些关于访问者的国家信息,但是我觉得这无关紧要,大部分网站的访问者集中在国内。这样能给awstats性能带来小小提升。 小心使用这三个参数: URLWithQuery, URLReferrerWithQuery ,URLWithAnchor (如果你不知道它们有什么作用最好设置为0) 使用最新版本的perl (例如Perl 5.8比5.6要快上5%) 。另外比较重要的一点,用标准版本的perl代替ActiveState版本的perl,因为ActiveState 5.006有内存泄漏方面的问题,它在解析日志的时候会越来越慢最终一行日志也跑不动。 回卷日志。尽可能的把日志分成更小的一份,这样awstats能处理的快一些,可以适当提高awstats解析频率. 确认HostAliases参数是完整的 使用最新版本的AWStats (例如AWStats 6.0比5.9快15%). 某些geeks,你可以考虑采用不同的参数来重新编译perl,例如使用”use64bitint=define usemymalloc=y”编译perl,这样perl的速度能提升10%. 原文见awstats benchmark