使用Huagepage和PGO来提升PHP7的执行性能
Hugepage
PHP7刚刚发布了RC4,包含一些bug修复和一个我们最新的性能提升成果,那就是”HugePageFyPHPTEXTsegment”,通过启用这个特性,PHP7会把自身的TEXT段(执行体)”挪“到Huagepage上,之前的测试,我们能稳定的在Wordpress上看到2%~3%的QPS提升。
关于Hugepage是啥,简单的说下就是默认的内存是以4KB分页的,而虚拟地址和内存地址是需要转换的,而这个转换是要查表的,CPU为了加速这个查表过程都会内建TLB(TranslationLookasideBuffer),显而易见如果虚拟页越小,表里的条目数也就越多,而TLB大小是有限的,条目数越多TLB的CacheMiss也就会越高,所以如果我们能启用大内存页就能间接降低这个TLBCacheMiss,至于详细的介绍,Google一搜一大堆我就不赘述了,这里主要说明下如何启用这个新特性,从而带来明显的性能提升。
新的Kernel启用Hugepage已经变得非常容易了,以我的开发虚拟机为例(UbuntuServer14.04,Kernel3.13.0-45),如果我们查看内存信息:
$cat/proc/meminfo|grepHuge
AnonHugePages:444416kB HugePages_Total:0 HugePages_Free:0 HugePages_Rsvd:0 HugePages_Surp:0 Hugepagesize:2048kB
可见一个Hugepage的size是2MB,而当前并没有启用HugePages.现在让我们先编译PHPRC4,记得一定不要加:–disable-huge-code-pages(这个新特性是默认启用的,你加了这个就关了)
然后配置opcache,从PHP5.5开始Opcache已经是默认启用编译的,但是是编译动态库的,所以我们还是要在php.ini中配置加载下。
zend_extension=opcache.so
这个新特性是做在Opcache里的,所以也要通过Opcache启用这个特性(通过设置opcache.huge_code_pages=1),具体的配置:
opcache.huge_code_pages=1
现在让我们配置OS,分配一些Hugepages:
$sudosysctlvm.nr_hugepages=128 vm.nr_hugepages=128
现在让我们再次检查内存信息:
$cat/proc/meminfo|grepHuge
AnonHugePages:444416kB HugePages_Total:128 HugePages_Free:128 HugePages_Rsvd:0 HugePages_Surp:0 Hugepagesize:2048kB
可以看到我们分配的128个Hugepages已经就绪了,然后我们来启动php-fpm:
$/home/huixinchen/local/php7/sbin/php-fpm
[01-Oct-201509:33:27]NOTICE:[poolwww]'user'directiveisignoredwhenFPMisnotrunningasroot [01-Oct-201509:33:27]NOTICE:[poolwww]'group'directiveisignoredwhenFPMisnotrunningasroot
现在,再次检查内存信息:
$cat/proc/meminfo|grepHuge
AnonHugePages:411648kB HugePages_Total:128 HugePages_Free:113 HugePages_Rsvd:27 HugePages_Surp:0 Hugepagesize:2048kB
说到这里,如果Hugepages可用,其实Opcache也会用Hugepages来存储opcodes缓存,所以为了验证opcache.huge_code_pages确实生效,我们不妨关闭opcache.huge_code_pages,然后再启动一次后看内存信息:
$cat/proc/meminfo|grepHuge
AnonHugePages:436224kB HugePages_Total:128 HugePages_Free:117 HugePages_Rsvd:27 HugePages_Surp:0 Hugepagesize:2048kB
可见开启了huge_code_pages以后,fpm启动后多用了4个pages,现在我们检查下php-fpm的text大小:
$size/home/huixinchen/local/php7/sbin/php-fpm
textdatabssdechexfilename 1011456569520013152810941293a6f36d/home/huixinchen/local/php7/sbin/php-fpm
可见text段有10114565个字节大小,总共需要占用4.8个左右的2M的pages,考虑到对齐以后(尾部不足2MPage部分不挪动),申请4个pages,正好和我们看到的相符。
说明配置成功!Enjoy:)
但是有言在先,启用此特性以后,会造成一个问题就是你如果尝试通过Perfreport/anno去profiling的时候,会发现符号丢失(valgrind,gdb不受影响),这个主要原因是Perf的设计采用监听了mmap,然后记录地址范围,做IP到符号的转换,但是目前HugeTLB只支持MAP_ANON,所以导致Perf认为这部分地址没有符号信息,希望以后版本的Kernel可以修复这个限制吧..
GCCPGO
PGO正如名字所说(ProfileGuidedOptimization有兴趣的可以Google),他需要用一些用例来获得反馈,也就是说这个优化是需要和一个特定的场景绑定的.
你对一个场景的优化,也许在另外一个场景就事与愿违了.它不是一个通用的优化.所以我们不能简单的就包含这些优化,也无法直接发布PGO编译后的PHP7.
当然,我们正在尝试从PGO找出一些共性的优化,然后手工Apply到PHP7上去,但这个很明显不能做到针对一个场景的特别优化所能达到的效果,所以我决定写这篇文章简单介绍下怎么使用PGO来编译PHP7,让你编译的PHP7能特别的让你自己的独立的应用变得更快.
首先,要决定的就是拿什么场景去FeedbackGCC,我们一般都会选择:在你要优化的场景中:访问量最大的,耗时最多的,资源消耗最重的一个页面.
拿Wordpress为例,我们选择Wordpress的首页(因为首页往往是访问量最大的).
我们以我的机器为例:
Intel(R)Xeon(R)CPU X5687 @3.60GHzX16(超线程),
48GMemory
php-fpm采用固定32个worker,opcache采用默认的配置(一定要记得加载opcache)
以wordpress4.1为优化场景..
首先我们来测试下目前WP在PHP7的性能(ab-n10000-c100):
$ab-n10000-c100http://inf-dev-maybach.weibo.com:8000/wordpress/
ThisisApacheBench,Version2.3<$Revision:655654$> Copyright1996AdamTwiss,ZeusTechnologyLtd,http://www.zeustech.net/ LicensedtoTheApacheSoftwareFoundation,http://www.apache.org/ Benchmarkinginf-dev-maybach.weibo.com(bepatient) Completed1000requests Completed2000requests Completed3000requests Completed4000requests Completed5000requests Completed6000requests Completed7000requests Completed8000requests Completed9000requests Completed10000requests Finished10000requests ServerSoftware:nginx/1.7.12 ServerHostname:inf-dev-maybach.weibo.com ServerPort:8000 DocumentPath:/wordpress/ DocumentLength:9048bytes ConcurrencyLevel:100 Timetakenfortests:8.957seconds Completerequests:10000 Failedrequests:0 Writeerrors:0 Totaltransferred:92860000bytes HTMLtransferred:90480000bytes Requestspersecond:1116.48[#/sec](mean) Timeperrequest:89.567[ms](mean) Timeperrequest:0.896[ms](mean,acrossallconcurrentrequests) Transferrate:10124.65[Kbytes/sec]received
可见Wordpress4.1目前在这个机器上,首页的QPS可以到1116.48.也就是每秒钟可以处理这么多个对首页的请求,
现在,让我们开始教GCC,让他编译出跑Wordpress4.1更快的PHP7来,首先要求GCC4.0以上的版本,不过我建议大家使用GCC-4.8以上的版本(现在都GCC-5.1了).
第一步,自然是下载PHP7的源代码了,然后做./configure.这些都没什么区别
接下来就是有区别的地方了,我们要首先第一遍编译PHP7,让它生成会产生profile数据的可执行文件:
$makeprof-gen
注意,我们用到了prof-gen参数(这个是PHP7的Makefile特有的,不要尝试在其他项目上也这么搞哈:))
然后,让我们开始训练GCC:
$sapi/cgi/php-cgi-T100/home/huixinchen/local/www/htdocs/wordpress/index.php>/dev/null
也就是让php-cgi跑100遍wordpress的首页,从而生成一些在这个过程中的profile信息.
然后,我们开始第二次编译PHP7.
$makeprof-clean $makeprof-use&&makeinstall
好的,就这么简单,PGO编译完成了,现在我们看看PGO编译以后的PHP7的性能:
$ab-n10000-c100http://inf-dev-maybach.weibo.com:8000/wordpress/
ThisisApacheBench,Version2.3<$Revision:655654$> Copyright1996AdamTwiss,ZeusTechnologyLtd,http://www.zeustech.net/ LicensedtoTheApacheSoftwareFoundation,http://www.apache.org/ Benchmarkinginf-dev-maybach.weibo.com(bepatient) Completed1000requests Completed2000requests Completed3000requests Completed4000requests Completed5000requests Completed6000requests Completed7000requests Completed8000requests Completed9000requests Completed10000requests Finished10000requests ServerSoftware:nginx/1.7.12 ServerHostname:inf-dev-maybach.weibo.com ServerPort:8000 DocumentPath:/wordpress/ DocumentLength:9048bytes ConcurrencyLevel:100 Timetakenfortests:8.391seconds Completerequests:10000 Failedrequests:0 Writeerrors:0 Totaltransferred:92860000bytes HTMLtransferred:90480000bytes Requestspersecond:1191.78[#/sec](mean) Timeperrequest:83.908[ms](mean) Timeperrequest:0.839[ms](mean,acrossallconcurrentrequests) Transferrate:10807.45[Kbytes/sec]received
现在每秒钟可以处理1191.78个QPS了,提升是~7%.还不赖哈(咦,你不是说10%么?怎么成7%了?呵呵,正如我之前说过,我们尝试分析PGO都做了些什么优化,然后把一些通用的优化手工Apply到PHP7中.所以也就是说,那~3%的比较通用的优化已经包含到了PHP7里面了,当然这个工作还在继续).
于是就这么简单,大家可以用自己的产品的经典场景来训练GCC,简单几步,获得提升,何乐而不为呢