遭遇php的in_array低性能问题

PHP的性能一直在提高。然而,若是用的不恰当,或是一个不留神,还是可能会踩到PHP内部实现方面的坑的。我在前几天的一个性能问题上就碰到了

PHP的性能一直在提高。然而,若是用的不恰当,或是一个不留神,还是可能会踩到PHP内部实现方面的坑的。我在前几天的一个性能问题上就碰到了。

事情是这样子的,一位同事反馈我们的一个接口每次返回需要5秒之久,我们一起review了代码,“惊喜”的发现居然在循环(大约900次)中调用了一个读缓存的操作,而这个缓存的key并没有改变,因此我们把这段代码移到了循环外面,再测,接口返回时间降到了2秒,呜呼!虽然提升了1倍,但明显不是我们能接受的结果! 
出现性能问题的代码量并不大,我们排除了IO问题以后,写了一段测试代码,果然问题很快重现。

<?php 
$y="1800"; 
$x = array(); 
for($j=0;$j<2000;$j++){ 
$x[]= "{$j}"; 
}

for($i=0;$i<3000;$i++){ 
if(in_array($y,$x)){ 
continue; 


?>

shell$ time /usr/local/php/bin/php test.php

real 0m1.132s 
user 0m1.118s 
sys 0m0.015s

对的,我们用的就是字符串型的数字,从缓存拿出来就是这样子的啦!所以这里是特意转成字符串的(如果直接是数字,并不会出现这个问题 ,各位可以自行验证)。可以看出时间耗掉了1秒,才3000次循环,后面的sys用时也注定我们用strace不会拿到什么有效信息。

shell$ strace -ttt -o xxx /usr/local/php/bin/php test.php 
shell$ less xxx

我们只看到这两次系统调用之间的延时非常大,却并不知道干了什么?一筹莫展了,幸好,Linux下的调试利器除了strace还有ltrace(当然还有dtrace,ptrace,不在本文讨论范围了,略去)。

引用:strace用来 跟踪一个进程的系统调用或信号产生的情况,而 ltrace用来 跟踪进程调用库函数的情况(via IBM developerworks)。

为了排除干扰因素,我们将$x直接赋值为array(“0″,”1″,”2″,……)的形式,避免过多的malloc调用影响结果。执行

shell$ ltrace -c /usr/local/php/bin/php test.php

如图2

我们看到库函数__strtol_internal的调用非常之频繁,达到了94%,太夸张了,然后我又查了一下这个库函数__strtol_internal是干嘛的,原来是strtol的别名,简单的说就是把字符串转换成长整形,可以猜测PHP引擎已经检测到这是一个字符串型的数字,所以期望将他们转换成长整型来比较,这个转换过程中消耗了太多时间,我们再次执行:

shell$ ltrace -e "__strtol_internal" /usr/local/php/bin/php test.php

可以轻松抓到大量下图这样的调用,到此,问题找到了,in_array这种松比较,会将两个字符型数字串先转换为长整型再进行比较,却不知性能就耗在这上面了。

知道了症结所在,我们解决的办法就很多了,最简单的就是为in_array加第三个参数为true,即变为严格比较,同时还要比较类型,这样避免了PHP自作聪明的转换类型,跑起来果然快多了,代码如下:

<?php
$y="1800";
$x = array();
for($j=0;$j<2000;$j++){
        $x[]= "{$j}";
}

for($i=0;$i<3000;$i++){
        if(in_array($y,$x,true)){
                continue;
        }
}
?>

shell$ time /usr/local/php/bin/php test.php

real 0m0.267s 
user 0m0.247s 
sys 0m0.020s

快了好多倍啊!!!可以看到sys耗时几乎没有太大变化。我们再次ltrace一把,还是要把$x直接赋值,排除malloc调用的干扰,因为我们实际应用中是从缓存里一次拉出来的,所以也不存在示例代码中这样的循环来申请内存的情况。 
再次执行

shell$ ltrace -c /usr/local/php/bin/php test.php

如下图:

__ctype_tolower_loc占用了最多的时间!查了一下库函数__ctype_tolower_loc是干嘛的:简单的理解是将字符串转换成小写,那么这说明in_array比较字符串不区分大小写吗?其实这个函数调用已经和我们这个in_array感觉联系不大了,关于in_array的实现,还是去看看PHP的源码,大概理解的更为透彻了,好了,没法往下说了,欢迎与我交流,写的不对的地方请多多斧正。

———————2013.08.29分割线——————————

晚上又翻了以下PHP 5.4.10的源码,对in_array的兴趣真大啊,哈哈,位于./ext/standard/array.c的第1248行,可以看到他调用了php_search_array函数,下面的array_serach也是调的这个,只是最后一个参数不同!经过一番跟踪,在in_array松比较的情况下,他最终调用的函数 zendi_smart_strcmp(果然是个“聪明”函数)进行比较,位于./Zend/zend_operators.c,我们用ltrace抓到的大量转换成整型的操作就是那个is_numeric_string_ex的行为。

函数is_numeric_string_ex是在./Zend/zend_operators.h中定义的,在前面进行了一堆的判断和转换之后,在232行调用了strtol,就是我们在文章中提到的系统函数了,将字符串转换成长整型,有图有真相

原文地址:https://www.cnblogs.com/ouruola863/p/8587348.html

时间: 2024-10-27 01:38:26

遭遇php的in_array低性能问题的相关文章

云计算之路-阿里云上-容器难容:自建docker swarm集群遭遇无法解决的问题

我们从今年6月开始在生产环境进行 docker 容器化部署,将已经迁移至 ASP.NET Core 的站点部署到 docker swarm 集群上.开始我们选用的阿里云容器服务,但是在使用过程中我们遭遇了恐怖的路由服务(acsrouting)路由错乱问题 —— 请求被随机路由到集群中的任一容器,虽然后来阿里云修复了这个问题,但我们对容器服务失去了信心,走上了用阿里云服务器自建 docker swarm 集群的道路. 用上自建 docker swarm 集群之后,本以为可以在云上容器中过上安稳的日

in_array()和explode()的使用笔记

今天使用explode函数是因为,在使用in_array()函数时候,in_array()的第二个参数是个数组,bool ( mixed $needle , array $haystack [, bool $strict ] ) 但是我通过查询得到的一个参数是字符串.字符串中的每个值都是通过逗号分隔.将其变成数组得需要分隔他们.而explode这个函数正好分隔字符串然后以数组的形式返回.

CSDN用户首次遭遇连续被封杀……

我的CSDN用户注册几年了,今天还是第一次遭遇用户被封杀. 个人空间也被关闭了! 遭遇封杀之前我一直在问答频道回答问题,在回答一个问题时,提交答案页面如下图提示: 遇到类似的问题,习惯性的刷新了几下,结果-- 就跳转到了登录页面,提示用户被封杀. 联系管理员解封后,我又回答刚刚出问题的那个问题,提交相同的内容,结果又被封了.确定是以下内容导致的问题: 直接贴XML不行,还是上图片中吧. 具体原因可能是这段XML中存在的URL太多了.

C#分隔字符串时遭遇空值

在C#中分隔字符串时,按特定字符进行分隔的时候可能会遇到空值,如何我现在传入的是Id的字符串,如:"1501,1502,1503,,1505",以逗号分隔,由于各种原因,导致传入的字符串中有连续的逗号,如果我要利用这一串字符串得到ID数组作为Sql查询的条件,那么我就必须对这种情况进行处理. 实例: //数组转换成字符串,字符串切割成数组.string[] array = new[] {"1m2", "", "ww", &qu

Spring MVC遭遇checkbox的问题解决方案

Spring MVC遭遇checkbox的问题是:当checkbox全不选时候,则该checkbox域的变量为null,不能动态绑定到spring的controller方法的入参上,并抛出异常. 解决方案: 1.javascript方式提交,提交前拼提交参数串,拼完后通过ajax方式提交.可以使用controller请求参数绑定. 缺点:逐个提取表单参数,并对checkbox选项参数进行判断拼装(字符分割),最终提交到后台太麻烦. 2.添加checkbox的同名隐藏域,从而使提交过去数据永不为n

遭遇垃圾短信?安全卫士帮你拦截

手机安全问题一度成为用户关注的焦点.在开展的"用户最关注的手机安全隐患"调研中显示,不少手机用户遭遇了手机吸费和流量等问题,垃圾短信.骚扰电话更是防不胜防.被央视曝光的几款APP仅是冰山一角,运行在手机终端内的恶意代码软件,已经构成了对用户信息的严重威胁. 多手机用户都有这样的经历,刚刚买了房子或者买了车没多久,就会收到各种推销金融产品.贷款的短信或者电话.很明显用户在购买汽车和房产的时候,登记了自己真实的姓名和电话,而这些个人信息被商业机构出售牟利,导致个人信息的外泄,同时遭遇骚扰电

(实用篇)php数组查找函数in_array()、array_search()、array_key_exists()使用

php在数组中查找指定值是否存在的方法有很多,记得很久以前我一直都是傻傻的用foreach循环来查找的,下面我主要分享一下用php内置的三个数组函数来查找指定值是否存在于数组中,这三个数组分别是 in_array(),array_search(),array_key_exists(). 首先分别介绍一下各自的定义与作用 in_array(value,array,type) 该函数的作用是在数组array中搜索指定的value值,type是可选参数,如果设置该参数为 true ,则检查搜索的数据与

PHP in_array不兼容问题

做过日本的手机端,就因为in_array这个方法在我的环境下没有问题 结果到日本那边就是出问题,一直纠结的我啊,现在特贴出当初的兼容方法 function in_into($key,$array){  $flg = false;   for($i=0;$i< count($array);$i++) {         if($key == $array[$i]) {           $flg = true;          break;        } } return $flg;} 例

遭遇sql server 2005 启动包未能正确加载需要重新安装错误,重装.NET FRAMEWORK经历分析

开发的机器,系统情况如下: 1.server 2003 sp2 x86 2.补丁安装360 3.升级到IE8 因为担心server 2003 sp2 不能够自动update,最近都是用360打补丁,比较快,但是问题很多,首先是.NET 2.0 SP2的更新会引起IIS6.0 的崩溃,然后就是SQL SERVER 2005 MANAGEMENT STUDIO 的启动失败,又不想重装系统,找了很多的工具,最重要的windows installer clean up.cleanup_tool,步骤如下