中间件团队组织的Coding4Fun的第三期已经结束,这次参与的同学很活跃,报名的有20多人,最终有17人在gitlab上提交了代码。把这次活动以及大家的经验总结一下。
任务:
里有一个大文本,文件请从 http://10.125.9.144:8000/document.zip 获取,在解压后大约有20m(实际比赛时文件是1.1G)。 文本中都是英文单词,空格以及英文的标点符号: [.,;-~"?'!] (句号,逗号,分号,破折号,波浪号,双引号,问号,单引号,感叹号) 请统计出该文本中最常出现的前10个单词(不区分大小写)。 请注意,在统计中这20个单词请忽略(the, and, i, to, of, a, in, was, that, had, he, you, his, my, it, as, with, her, for, on)
说明:
1) 编程语言不限 2) 最终的测试机器为多核环境(单机) 3) 不得借助外部服务(调用远程服务) 4) 可引用外部框架或库(限开源) 5) 结果不正确直接淘汰 6) 用时最短者获胜(前三名有奖) 7) 代码优雅凝练(明显比前三名的代码量少且易懂)并且性能不太离谱,可获最佳代码奖
因为此次不限编程语言,为避免不同语言的运行时/虚拟机启动时间差异,没有统一的计时方式,计时方式为在main的入口和出口自行统计。在比赛的机器上(linux系统,16核,8G内存)运行3次,取时间最短的那次做最终成绩。
最终获奖结果:
涧泉:1936ms c++ 子观:2489ms c++ 雪酒:2565ms c++ 玄胤:2615ms c 平威:2792ms java 劈空:2860ms c++ 游骥:3207ms java 长源:3220ms java 叔至:3334ms java 寒玄: 4336ms go 娜米: 4分32秒 perl 一行代码
第一名:涧泉,唯一突破2秒的,奖品是机械键盘
他采用并发方式,用ConcurrentTrieNode
实现了线程安全的Trie
树。详细经验见:wordcount设计与优化。涧泉同学很低调,在Coding4Fun的交流群里也很沉默,不过看到他blog里说的,就能体会到一个追求完美的码农的心态了:
程序开始是用JavaConcurrentHashMap实现的。为了提升性能翻译成C++,过程可谓大费周折,相当痛苦,我会告诉你我大半夜还在调segmentalfault吗?
第二名:子观
子观同学也是一匹黑马,刚开始用python实现的,成绩几乎垫底;在提交代码的前一天晚上通宵debug,实现了大逆转。
子观的经验见:coding4fun比赛总结
第三名:雪酒
雪酒同学暑期来阿里实习,8月底选择了回学校读研究生,我们会把T恤寄给你,也欢迎有空回来转转。他的经验如下:
策略1:尽最大的努力减少动态申请内存
理由:在初始实验之中,我是每分离出一个单词就要malloc内存,时间就花销很长,因为这种小内存的分配的时候,系统会好很长的时间,而且容易产生内存碎片,于是我就想到了尽量减少malloc的使用,在程序实现之中,除了第一次malloc了一个与文件一样大的内存外,从此没有使用过malloc函数,还好如今的gcc支持c99,可以让我预先定义动态数组,这样子我除了那个在堆上的总buffer,其余的存储变量几乎都是在栈上,不仅仅节约了时间,而且让我减少了对内存的管理。
策略2:hash表和trie树同时使用
理由:trie树的优点就不用说了,查找是很快的,缺点是比较的明显,第一,耗内存,第二还是耗内存,由于我采用了策略1的策略,所以,我不能再每个字母节点都malloc内存,我一开始就把我的trie树大小给定死了的,当trie树需要节点的时候,就直接按照预先在静态区分好了的给,如果超过了预先定的数目,那么就段错误了……=。=,由于trie树比较耗内存,所以我就不想每个线程都来一个trie树,万一内存使用过多,导致频繁的换页那就亏大了。所以,我在主线程中使用的trie树,其余线程中,使用的是hash表,这样子就能大规模的减少进程对内存的占用了(虽然我一口气吃的内存也是很多),而且trie树和hash表同时使用还可以利用trie树查找快的这么一个特点(其实现在也不能确定trie树是否能比hash快)。
策略3:待排序的数组去除掉无用的,空的节点
理由:若是使用hash表,我最后排序的时候中间会出现很多的空的节点,把这些空节点一起算进来进行排序的话会有一部分排序是无意义的,要是一开始使用没有无意义的节点的数组,那么就可以减少不必要的循环。
第四名:玄胤
玄胤同学是利用openMP的并行库+无锁hashtable来实现的,详见:c4f玄胤总结,玄胤同学除了会写代码,还会弹弹琴什么的,文艺青年一枚
简洁代码奖:娜米
娜米是此次比赛里唯一的女生,很偷懒的就用了一行perl程序解决了,虽然效率差了几个数量级,但据说娜米妹子也是精通算法的,只是这次比赛没投入精力。娜米凭她的古灵精怪获得了最简洁代码奖,希望下次有更多的妹子参与。
这次比赛很热闹,没有语言的限制,C/C++,Java,Go,Erlang,Python,Perl等百花齐放。最后来一张获奖者合照(有个别同学没在):
补充,长源和平威用java实现的性能也很不错,大力推荐一下。
长源同学采用了简化版的trie,他的分享:coding4fun-词频统计的优化思路/
平威选用的hashmap,不过他使用ByteString代替String,详见:#coding4fun#词频统计优化思路