除了sort()函数外,大家平时利用比较多的该数usort()函数了,由于它可以许可你自定义排序规则。自然而然,对付下面这个场景,须要对每个学生的考试分数从高到低进行排名时,出于惯性,就会很自然连续利用usort()函数。简短的实当代码是:
<?php// 学生数组$students = array( array(39;name' => '张三', 'point' => 76), array('name' => '李四', 'point' => 98), array('name' => '小明', 'point' => 95), array('name' => '小红', 'point' => 83), array('name' => '阿布', 'point' => 88),);// 按分数从高到低排序usort($students, function($left, $right) { if ($left['point'] == $right['point']) { return 0; } return $left['point'] > $right['point'] ? -1 : 1;});// 输出print_r($students);
上面代码运行后,能得到精确的排序结果。看起来没什么问题,对吧?是的,确实看起来没什么问题。由于这里只有5个学生。但是,假设开拓的系统是大型的系统,假设要排序的学生有广东省的全部高考学生,以2017年为例,广东省高考人数大概是75.7万,这时性能又会如何?
让我们大略来做一个仿照的小实验。用事实结合xhprof性能剖析工具得出的数据来说话。
先轻微加以改装,在进行排序的前后加上xhprof的性能剖析代码。
// 开始xhprof性能剖析xhprof_enable();usort($students, function($left, $right) { if ($left['point'] == $right['point']) { return 0; } return $left['point'] > $right['point'] ? -1 : 1;});// 结束xhprof性能剖析$xhprof_data = xhprof_disable();
关于xhprof的利用,网上已经有很多资料解释,这里不再详细展开。终极,我们可以看到这样的性能剖析报告:
图4-1 对5个学生利用usort()排序
接下来,我们可以把学生的数量加大一点。先增加到万的级别,通过指数爆炸很随意马虎做到这一点,以上面5个学生为根本,通过对这5个学生的数据进行11次翻倍后,就可以得到10240组数据。把下面造数据的代码放在$students变量声明后即可。
// 5 (2 ^ 11) = 10240for ($i = 0; $i < 11; $i ++) { $students = array_merge($students, $students);}
再来看一下,这时期码运行后的性能剖析报告是若何的。
图4-2 对一万多个学生利用usort()排序
我们暂时先不来比拟这些数据,连续完成末了一批排序——仿照近70万学生的成绩排序。连续把上面循环的次数从原来11次加大到17次,就可以得到655360组数据。这时,你会创造页面相应已经明显变慢了。在我测试时,基本利用了13秒。终极的xhprof性能剖析报告如下:
图4-3 第三组剖析,对65万多个学生利用usort()排序
末了,通过这三组数据,我们来做个汇总和比较,就能明显创造问题所在了。比拟几个关键的性能指标:实行韶光、内存利用情形和函数调用次数,可以得到以下表格:
表4-3 不同排序函数的性能比较
可以创造,随着学生数量的增加,实行韶光也明显相应变大。特殊对付函数调用次数,增长的幅度更为明显,并且是绝对值。即不管是什么配置的做事器,函数调用次数都是不变的。那么对付第三组,实行了靠近12.9秒,韶光都到哪里去了呢?
学过操作系统的同学都知道,每次函数的调用都存在高下文切换的开销,频繁的函数调用,会产生频繁的堆栈操作。这也是为什么C/C++措辞会支持内联函数。再来看一下第三组的调用链就能知道大部分韶光,靠近95.9%都花在了函数的调用上。剩下的4.1%韶光则花在了自定义函数本身的实行上。
图4-4 第三组的调用链
既然usort()函数存在性能问题,那么该当改用哪个排序函数更得当、更优呢?还记得我们前面学过的array_multisort()函数吗?来看下它的效果若何。根据前面所学的知识,不难把实现改成:
<?php// 学生数组$students = array( array('name' => '张三', 'point' => 76), array('name' => '李四', 'point' => 98), array('name' => '小明', 'point' => 95), array('name' => '小红', 'point' => 83), array('name' => '阿布', 'point' => 88),);// 制造更多的学生数据// 5 (2 ^ 11) = 10240// 5 (2 ^ 16) = 655360for ($i = 0; $i < 17; $i ++) { $students = array_merge($students, $students);}// 开始xhprof性能剖析xhprof_enable(XHPROF_FLAGS_MEMORY + XHPROF_FLAGS_CPU);//usort($students, function($left, $right) {// if ($left['point'] == $right['point']) {// return 0;// }//// return $left['point'] > $right['point'] ? -1 : 1;//});$points = array();foreach ($students as $it) { $points[] = $it['point'];} array_multisort($points, SORT_DESC, SORT_NUMERIC, $students);// 结束xhprof性能剖析$xhprof_data = xhprof_disable();// print_r($students);$XHPROF_ROOT = realpath(dirname(__FILE__));include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php";include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php";// save raw data for this profiler run using default// implementation of iXHProfRuns.$xhprof_runs = new XHProfRuns_Default();// save the run under a namespace "xhprof_foo"$run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_foo");echo "http://localhost/?run=$run_id&source=xhprof_foo\n";
代码改好后,再来看一下xhprof的性能剖析报告。有没创造,实行韶光已经降为只有约2.7秒了!
比原来的12.9秒,速率上提升了79%!
并且函数调用次数仅有3次!
但也不要愉快太早,由于array_multisort()函数会花费更多的内存。这正是范例的空间换韶光的做法。但作为真实的终端用户,他不会关心我们的做事器利用了多少内存,他只关心打开的网站是否顺畅,能不能在更短的韶光内浏览到他感兴趣的商品。如果弗成,他就会离我们而去。
图4-5 对65万多个学生改用array_multisort()排序
通过上面的数据比拟,以及和改进后的方案比拟,不难总结出,对付同一个函数,不同级别的数据量,其须要的实行韶光是大有不同的。选择得当的底层函数,对项目、对系统、对用户都是非常有益的。大略来说,在大型系统开拓中,要慎用usort()函数。末了,为了方便大家查看,贴一下终极完全的代码。
<?php// 学生数组$students = array( array('name' => '张三', 'point' => 76), array('name' => '李四', 'point' => 98), array('name' => '小明', 'point' => 95), array('name' => '小红', 'point' => 83), array('name' => '阿布', 'point' => 88),);// 制造更多的学生数据// 5 (2 ^ 11) = 10240// 5 (2 ^ 16) = 655360for ($i = 0; $i < 17; $i ++) { $students = array_merge($students, $students);}// 开始xhprof性能剖析xhprof_enable(XHPROF_FLAGS_MEMORY + XHPROF_FLAGS_CPU);//usort($students, function($left, $right) {// if ($left['point'] == $right['point']) {// return 0;// }//// return $left['point'] > $right['point'] ? -1 : 1;//});$points = array();foreach ($students as $it) { $points[] = $it['point'];} array_multisort($points, SORT_DESC, SORT_NUMERIC, $students);// 结束xhprof性能剖析$xhprof_data = xhprof_disable();// print_r($students);$XHPROF_ROOT = realpath(dirname(__FILE__));include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php";include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php";// save raw data for this profiler run using default// implementation of iXHProfRuns.$xhprof_runs = new XHProfRuns_Default();// save the run under a namespace "xhprof_foo"$run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_foo");echo "http://localhost/?run=$run_id&source=xhprof_foo\n";
大概还会存在其他的排序陷阱,大家可以在实际项目开拓中,多加留神,平时多总结。接下来,连续谈论关于数组更多高等的用法。