遭遇php的in_array低性能问题

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,就是我们在文章中提到的系统函数了,将字符串转换成长整型,有图有真相

(0)

相关推荐

  • 2个自定义的PHP in_array 函数,解决大量数据判断in_array的效率问题

    但是如果数组比较大的时候,性能就会下降,运行的就会久一点,那如果针对在大数组情况下做优化呢,下面说两种方法(都是通过自定义函数来实现): 1.数组key与value翻转,通过isset判断key是否存在于数组中 复制代码 代码如下: /** * in_array is too slow when array is large */public static function inArray($item, $array) {    $flipArray = array_flip($array); 

  • php数组函数序列之in_array() - 查找数组中是否存在指定值

    in_array()定义和用法 in_array() 函数查找数组中是否存在指定值. 语法 in_array(value,array,type)参数 描述 value 必需.规定要在数组搜索的值. array 必需.规定要搜索的数组. type 可选.如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同. 说明 如果给定的值 value 存在于数组 array 中则返回 true.如果第三个参数设置为 true,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true.

  • PHP IN_ARRAY 函数使用注意事项

    另外,我们在 控制结构比较两个数值是否一直的时候,也应该尽量使用 === 来代替 ==(当然,这个也根据具体的业务逻辑选用比较合适的). 小弟来解释下 为什么 var_dump(in_array(0, array('s' )); 这句话的结果是bool(true). 因为in_array会将0 和's' 进行比较,0是number类型,'s'是string类型,根据php manual 中"Comparison Operators" 一章的说明可知,number 和string进行 比

  • 使用js判断数组中是否包含某一元素(类似于php中的in_array())

    while case速度最快 复制代码 代码如下: function contains(arr, str) {    var i = arr.length;    while (i--) {           if (arr[i] === str) {           return true;           }       }       return false;}

  • PHP函数in_array()使用详解

    PHP有一个系统函数is_array()可以判断一个值是否在数组中. 语法如下: 复制代码 代码如下: in_array(value,array,type) return boolen 参数说明: value :要搜索的值 array : 被搜索的数组 type : 类型,true全等 ,false非全等(默认) 示例一:普通使用 代码: 复制代码 代码如下: $str = 1;   $arr = array(1,3,5,7,9);   $boolvalue = in_array($str,$a

  • php数组函数序列之in_array() 查找数组值是否存在

    in_array() 定义和用法 in_array() 函数在数组中搜索给定的值. 语法 in_array(value,array,type) 参数 描述 value 必需.规定要在数组搜索的值. array 必需.规定要搜索的数组. type 可选.如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同. 说明 如果给定的值 value 存在于数组 array 中则返回 true.如果第三个参数设置为 true,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true.

  • 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的js数组的in_array函数自定义方法

    PHP的数组函数in_array()非常方便,可JS就不是了.其实我很不喜欢JS的数组~ 别说了,直接上方法 复制代码 代码如下: Array.prototype.in_array = function(e) { for(i=0;i<this.length;i++) { if(this[i] == e) return true; } return false; } 或者 复制代码 代码如下: Array.prototype.in_array = function(e) { for(i=0;i<

  • PHP中in_array函数使用的问题与解决办法

    先介绍一下需求背景: 发票方式: 0=捐赠(不要问我为什么,历史原因) 1=对中寄送 2=索取 3=电子发票 现在要对用户提交的数据进行检测: php;auto-links:false;">if(!in_array($_POST['invoice_action'], array(0,1,2,3))){ throw new Exception('请选择正确的发票方式'); } 这个时候出现一个问题,如果压根就不存在$_POST['invoice_action']这个值,为什么没有抛出异常?

  • php in_array 函数使用说明与in_array需要注意的地方说明

    in_array (PHP 4, PHP 5) in_array - 检查数组中是否存在某个值 说明 复制代码 代码如下: bool in_array ( mixed $needle , array $haystack [, bool $strict ] ) 在 haystack 中搜索 needle ,如果找到则返回 TRUE,否则返回 FALSE. 如果第三个参数 strict 的值为 TRUE 则 in_array() 函数还会检查 needle 的类型是否和 haystack 中的相同.

随机推荐