偶见有人在发起一个排序比赛,对27W左右的词进行排序输出,要求读取+排序+输出的时间最少。
例子代码有一个简单的Java版本实现,大概的时间是在4xxms,然后是Go语言实现,大约在3XX ms。Go语言不懂,就不折腾了。Java的版本还是可以看看。时间分布上,读取2.8M的文件大约150ms,写大约100毫秒,感觉明显可以优化。
第一步,修改读取方法,使用NIO的内存映射,直接开内存读取,时间下降到70ms,写入50ms左右,合计300多一点
第二步,修改排序算法,修改原来的多线程,对读取的时候数据进行分组,各个线程直接排序即可,最后再归并,总时间下降到260ms左右
第三步,尝试了读取的时候就分给多线程去插入SortedTree,归并输出,效果不明显,没什么提升。
第四步,尝试Java的Fork/Join调度框架,优化线程,可惜没写好,只当成多线程在用,提升了一点点。
第五步,修改了Fork/Join的分组部分,递归方式分组,方便一个线程执行完自己任务后,可帮助执行其他任务,避免全局的Join等待, 优化提升到了233ms左右
第四步,去掉了NIO读取逻辑,直接开BYTE数组读取,写入也类似, IO时间下降到10ms,夸张啊,是否使用BufferReader几乎没影响。总体时间下降到140 ms左右,还是非常明显的提升。
第五步,发现很用同学的时间已经进入100ms以内了,我优化了排序逻辑,主要的时间消耗在String比较上,根据文件特点,我优化了排序的比较逻辑,读取的时候就对ascii进行转义,long64位,short16位,文件的最大字符串长度为15,且不重复。这一点好像有点作弊,应该有一段逻辑进行探测计算才正规。每5位可以表示任何一个a~z,后面补0,这样位移消耗不大,且方便比较。只是Java无符号型处理起来麻烦,我C#也比较熟悉,就改C#写了。,先比前long,一样的情况下才比后面的short,时间:12+38+10=60ms左右,有点累了,还有项目的工作要做。
这个时候,我忽悠了跟多人进来,希望看到更好的结果。开始的方案,大家都差不多,多线程+快排,时间也你追我赶。后面GCC的优势开始起来,类似的思路,开始在多核上排序,47ms,44ms,41ms...
临近比较收官,忽悠到了YW的参与,第二日只用了不到300行的代码,成绩46ms,优化后,进入33ms,原来的第一名开始坐不住了,优化。。。最后的成绩22ms,