千家信息网

PHP排序稳定性实例分析

发表于:2024-10-25 作者:千家信息网编辑
千家信息网最后更新 2024年10月25日,今天小编给大家分享一下PHP排序稳定性实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解
千家信息网最后更新 2024年10月25日PHP排序稳定性实例分析

今天小编给大家分享一下PHP排序稳定性实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

PHP排序稳定性问题

最近在工作中碰到一个挺有意思的问题,线上输入是一串排好序的关联数组,经过一系列处理后输出的数组却是乱序,且本地运行无法复现。查看相关代码后,最让人在意的是这一段:

$categories = Arr::sort($categories, function ($node) {    return $node['default'];}, true);

作用是按default优先级将元素提到前面,首先确认了下线上的illuminate/support版本和本地一致,查看Arr::sort()源码:

...$descending ? arsort($results, $options)            : asort($results, $options);

最终还是调用 php 的asort,线上是 php5 而本地和测试因为最近考虑升级都换成了 php7,最后在 php5 环境下成功复现,确定出是sort问题。

在排序前后相等的元素相对位置不变即说这个算法是稳定的。

对快速排序有一定了解的话可以知道,快速排序是不稳定的所以这段代码在元素default优先级都相同的情况下输出将不会是之前的顺序,但在 php7 环境下测试结果为什么会保留原来的顺序呢。难道关于我之前理解的天底下所有默认排序都是快速排序这一点是错的吗?

好吧,让我们来快速看看 php 源码是如何解决这个问题的。到 github 官方的 php-src 直接搜索asort in this repository,找c文件,找到这个函数定义在 arr.c:814

PHP_FUNCTION(asort){        zval *array;        zend_long sort_type = PHP_SORT_REGULAR;        bucket_compare_func_t cmp;        ZEND_PARSE_PARAMETERS_START(1, 2)                Z_PARAM_ARRAY_EX(array, 0, 1)                Z_PARAM_OPTIONAL                Z_PARAM_LONG(sort_type)        ZEND_PARSE_PARAMETERS_END();        // 设置比较函数        cmp = php_get_data_compare_func(sort_type, 0);        zend_hash_sort(Z_ARRVAL_P(array), cmp, 0);        RETURN_TRUE;}

可以看到最终调用的是zend_hash_sort(),我们继续搜索:

发现这个函数是zend_hash_sort_ex()的套娃,最后找到zend_hash.c:2497

ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort, bucket_compare_func_t compar, zend_bool renumber){        Bucket *p;        uint32_t i, j;        IS_CONSISTENT(ht);        HT_ASSERT_RC1(ht);        if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) {                /* Doesn't require sorting */                return;        }        // 这里的hole指数组元素被unset掉产生的洞        if (HT_IS_WITHOUT_HOLES(ht)) {                /* Store original order of elements in extra space to allow stable sorting. */                for (i = 0; i < ht->nNumUsed; i++) {                        Z_EXTRA(ht->arData[i].val) = i;                }        } else {                /* Remove holes and store original order. */                for (j = 0, i = 0; j < ht->nNumUsed; j++) {                        p = ht->arData + j;                        if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;                        if (i != j) {                                ht->arData[i] = *p;                        }                        Z_EXTRA(ht->arData[i].val) = i;                        i++;                }                ht->nNumUsed = i;        }        sort((void *)ht->arData, ht->nNumUsed, sizeof(Bucket), (compare_func_t) compar,                        (swap_func_t)(renumber? zend_hash_bucket_renum_swap :                                ((HT_FLAGS(ht) & HASH_FLAG_PACKED) ? zend_hash_bucket_packed_swap : zend_hash_bucket_swap)));        ...

好耶!,官方注释里就有说明是怎么实现排序的稳定性,在排序之前用这个Z_EXTRA保留了原数组元素到下标的映射。

但当我搜索这块代码提交信息时发现了一个问题:稳定排序相关的 rfc 在https://wiki.php.net/rfc/stable_sorting,可以看到是发生在今年五月份且针对 php8.0 的。

?? 那之前的 php7 排序稳定是怎么回事。

马上构造个例子:

$count = 10;$cc = [];for ($i=0; $i<$count; $i++) {    $cc[] = [        'id' => $i,        'default' => rand(0, 10),    ];}$cc = Arr::sort($cc, function ($i) {   return $i['default'];});dd($cc);

经过多次在 php7 下的测试发现:当$count比较小的时候,排序才是稳定的,但$count较大情况下的排序又变成不稳定。也就是线上排序不稳定而本地无法复现其实是用例的数组长度太短所致。

看到这里可以确定是快速排序长度阈值优化的问题,最后查了下相关资料。php7 中的sort是基于LLVMlibc++std::sort实现。当元素数量小于等于16时候有特殊优化,大于16才走快速排序,而 php5 是直接就走快速排序的。

 $i,        'default' => rand(0, 10),    ];}usort($cc, function($a, $b){    if ($a['default'] == $b['default']) return 0;    return ($a['default'] < $b['default']) ? 1 : -1;});print_r($cc);

最后在 php8 环境下试了试,排序绝对稳定

以上就是"PHP排序稳定性实例分析"这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。

排序 元素 问题 数组 稳定性 知识 篇文章 代码 函数 环境 搜索 测试 实例 实例分析 分析 优先级 内容 官方 情况 时候 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 云服务器上有什么赚钱 网络技术的职业岗位 任天行网络安全管理系统使用说明 计算机网络安全损失 软件开发人员工资退税 通讯技术与大数据库 域名和服务器的主体不一样 网络安全专业成人大学 计算机网络技术大二有高数吗 重庆洋飞硕网络技术有限公司 网络安全生态需要价值保障 网络安全个人要注意什么 网络安全赛赛道 sql语句新建数据库和表 生命安全网络安全要牢记 阿里云服务器怎么选 网络安全工程师职称补贴 戴尔服务器带tesla显卡 安徽服务器电源特点 hpe服务器报修电话 魔兽世界tbc服务器人数查询 网络技术工程师咨询 数据库左右连接图书借阅表习题 刀山服务器拆机 技嘉AMD服务器平台BMC密码 山西计算机网络技术专升考哪些本 数据库数据纵向变横向 上海品星互联网科技有限公司 新都区科锋网络技术服务部 蘑菇街查看物流网络安全吗
0