作为一个从CPython 2.6.4时代就开始盘解释器源码都快盘出腻子了的老农,实在想吐槽一下了……这三种东西的适用范围根本就不一样。
CPython List的sort实现在这里https://github.com/python/cpython/blob/384621c42f9102e31ba2c47feba144af09c989e5/Objects/listobject.cgithub.com
看看代码就会很清楚,这玩意的实现就简单,单线程,无并行,无cache优化,以归并排序为基础,会检查数组是否所有成员类型一致,会检查list是否已排序(所以拿个已经排序好了的list,包括全等的list去做排序检查就是没有意义的事情)。
那么为什么这么多人说pandas快?DataFrame的实现,sort_values是怎么做的呢?调了numpy的argsort。那么numpy的argsort怎么实现的呢?https://github.com/numpy/numpy/tree/578f4e7dca4701637284c782d8c74c0d5b688341/numpy/core/src/npysortgithub.com
这里对应着numpy里主要排序算法的实现,C写的挺漂亮的排序,就拿默认的quicksort实现来说,甚至做了少量排序数时使用插入排序的替换,这跟主流c++ stl里的sort也差不远了,大多数的排序,单核情况下能做到这样就很不错了。
所以为什么一些特殊情况下,会出现List更快的实际结果呢?比较一下会发现,如果扔个已经排好序的序列去sort,list是很快的。一个720000元素的列表,如果是随机数,排个200~800ms在主流计算机上(包括服务器,毕竟这玩意也就用一个核)很正常;如果是已经排好序的(包括所有元素相同的),则会被短路,直接返回结果,从而得到一个极快的表现(如20~80ms)。而pandas则会比较老实地调argsort排一遍,势必慢一些。
最后来看看mysql。这玩意源码太长太长,就不翻源码了,简而言之,对于没有索引的列,mysql的实现里是把数据从外部存储里捞进来,多线程进行排序的;对于有索引的列,压根不需要排序,在存进去的时候就写入了索引(B+Tree)里,所谓排序,不过是把索引捞出来按照索引读数据罢了;而B+Tree这种还针对磁盘IO的特点优化存储布局,更能体现优势。所以在有索引的情况下,mysql的所谓“排序速度”比的不过是IO速度,读盘+通过socket发给client的时间而已;没有索引的情况下,则是读数据+并行排序+归并的速度(这里其实还有多线程通信的开销)。所以在这里,拿MySQL来比排序速度比的都不是一个东西,缺乏实际意义。
由于MySQL(以及各类其他数据库)排序的时候并不需要把所有内容全堆入内存,又有并行加持,拿去排百万条级(M行级)的数据压根不体现威力,拿去比比看亿万条十亿万条那种压根做不到一次性吞进内存的数据规模还差不多。