一个无锁消息队列引发的血案(一)——地:起因

目录

(一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer 

(四)RingQueue(上) 自旋锁  (五)RingQueue(中) 休眠的艺术

(六)RingQueue(中) 休眠的艺术 [续]

起因

  某一日,从云风的SkyNet群(QQ群)里知道 DouBan(豆瓣) 上有人喷云风,是从 ”《怎样识别水货程序员》说的是真的吗?” 这个《知乎》帖子开始的,

当初也是看了个大概,实在对知乎、豆瓣之类的不太感冒,文字又长,瞄了一下完事。其实我一开始没弄清是在豆瓣还是知乎上喷他,今天才看明白经过。

知乎上提到的两个(豆瓣)原帖分别是:《怎样识别水货程序员》和 《怎样识别正品程序员》,这两个原帖我好像看过第一个,今天才认真看了看,

原来讨论还挺多的……想了解来龙去脉的可以去看看。

下面是《知乎》上的帖子截图:


  一开始喷公认的水货,XX,还有有争议的陈皓(注意是陈皓,不是陈硕!尼玛的我都弄错了);然后是比较隐藏的水货,陈硕(楼主:陈硕有些文章还是不错的,某些文章的确是大自然的搬运工,搬得差了一点,不过好歹也搬了,也比不搬的搬运工要好一点,多少给人一些启示);然后就是最隐蔽的云风了,他说的的确也有些道理,云风在我看来,近些年写的东西你说不痛嘛,也有点氧,你说有点氧嘛却又挠得不舒服,只是当消遣来看看,偶尔关注下。不过多少还是有些实力,只不过云风太飘逸,也略微显得有些虚。

  无论是陈硕还是云风,作为开发者,我多少还是有几分敬仰的(我改写folly的string类的时候,还搜到了不少陈硕写的东西,多多少少还是给了我些帮助),喷人者 Sinclair 多少还是有些偏激,不过有些话也有些道理,所以也不太能反驳,反正我们就看看热闹呗。

  看了下回复,闲得蛋疼的人还真多,就算我是个不上班,作息随意的人都没有这个闲工夫来闲扯这些,不是今天要写这个东西,我都懒得看。

  然后是灌水第二贴,开始喷云风了,这才露出真面目。开始讨论的云风的悲观锁,乐观锁,还说云风把cas改成spinlock,整个过程,倒是符合云风的做风,虽然我并不清楚事情的来龙去脉。然后此人开始卖弄自己的英文和专业术语,可怜我的英文四级都没过,有个把单词不查字典咱还不认识,好高端。。。真是有点无语,有本身通篇英文嘛,咱也还是能看懂的。他提到的云风的问题我也没法反驳,好在我对缓存的认识也是颇深的,具体可以看我的项目(https://github.com/shines77/fast_matmult 里面的 matmult_s_row_tiling_K_sse2_2x4_packed.cpp ,看我是怎么把L1, L2 Cache一点一点榨干的,当然这个比GotoBLAS2最快的方法还是差那么一点点,不过这个方法是我自己发现的(其实也不能叫发现,只能叫实现),GotoBLAS2的那个思路的确比较难想到,我的项目里也有他的方法的实现,在 gemm_kernel_2x4_penryn.cpp 里)。对于缓存和CPU的一些描述,对错都不重要,但是你秀这么多英文就是你的不对了哦。(看帖子里他说他听同学说 pastebin.com 被墙了,莫非这位老兄不在国内,怪不得E文用得这么多,原来是海龟啊)

血案开始

  我长话短说吧,不然你会看不下去了,于是此人开始贴代码啦。来证明他不是“水货程序员”,是真正的“并发专家”。知乎上那个帖子原来是评论 Sinclair 的各个版本的代码的,看着有点令人糊涂,update1, update2, update3,我还以为是 Sinclair 自己写的说明。其实我是有 Sinclair 那个贴代码的帖子的,也有他的代码。好了,不罗嗦,上他的贴代码的帖子吧:《实打实的代码,实打实的性能分析

  这个帖子你是必须要看的,因为这就是本文标题提到的“血案”正文,当初我也没看完,我大概看到q3.h, qlock.h就没往下看了(或者还有一个原因是有些部分可能也是他后来才更新的),就往后面看两人的正面交锋了。就在写这篇文章的前几天,秉着负责的态度,我重新看了一遍这个帖子的正文,才发现,其实后面还更新了a2.c, a3.c,我后来做的一些工作他也做了,比如合理性验证,不过我觉得我做得更好一点,你看我的代码便知。我把各个线程pop出来的消息保存到各个线程的局部list里面,这样线程之间记录list的时候并不需要原子操作,而且区分各个线程的id编号是极容易的事情。关于合理性的验证,除了和他一样的检测flag的唯一性以外,我还想了一个更进一步的验证方式,详细可以看我在RingQueue_Test()里注释掉的一些代码。不过想想,检测flag的唯一性(其实我的代码里是叫编号唯一性,我给每个消息编了个号,从1~MSG_TOTAL_LENGTH)已经基本上满足合理性要求了,也就算了,因为那个方法理解上虽不难,但代码实现起来却比较麻烦。

代码

  下面开始上代码:

  在《实打实的代码,实打实的性能分析》一文里,提到了3个版本,一个是qlock.h(朱欣愚的版本),一个是q.h(云风的版本),一个q3.h(Sinclair 的版本),还有测试代码 a.c,当初我是拿到这四个文件就开始测试了(一开始我也只是看看而已,并没有编译和Run,我也不记得什么时候开始弄的)。由于 Sinclair 的帖子贴的地址是 pastebin.com 的,需要翻~墙才能看到,所以我把他们都上传到github了,地址如下:

https://github.com/shines77/RingQueue/blob/master/douban/q.h

https://github.com/shines77/RingQueue/blob/master/douban/q3.h

https://github.com/shines77/RingQueue/blob/master/douban/qlock.h

https://github.com/shines77/RingQueue/blob/master/douban/a.c

https://github.com/shines77/RingQueue/blob/master/douban/a2.c

https://github.com/shines77/RingQueue/blob/master/douban/a3.c

https://github.com/shines77/RingQueue/blob/master/douban/mq.c (这个是云风在大战尾声写的版本,据他自己说,是随手写的,这个的出处也在那个豆瓣帖子里能找到)

云风

  如果写到这里,你还不知道云风是谁,那么科普一下,云风,本名吴云洋,知名游戏开发者,网名CloudWu,他的博客:http://blog.codingnow.com/,曾经是网易技术骨干,后来在杭州创办网易杭州分公司,负责研发。1999年初开始制作二维游戏引擎--风魂系列,被多家公司和小组用于游戏制作。百度百科:http://baike.baidu.com/view/2750617.htm(网易云风),云风的GitHub:https://github.com/cloudwu,201x年离开网易,合伙创办广州简悦,著有开源服务器框架SkyNet,开源2D游戏引擎ejoy2d等,已上线的游戏比较火的有《陌陌争霸》、《狂刃》(代理)、《天天来战》等。

交战

  我们可以看到,在《实打实的代码,实打实的性能分析里,云风第一个回复就应战了,时间是13日早上10点多,

由于某些原因,我是第三天(即15日)下午3、4点左右才知道的(我特意查阅了Q群聊天记录求证的),大概那几天我在忙

jemalloc的Windows版(Visual Studio版),大概中午过一点我才会起来并上网。

而后云风提到这个是skynet的某个历史版本,有个隐晦的bug,因为后来改了就没有修复,再后面这句话是比较值得玩味的:

这就是大师的做事风范。。。而 Sinclair 这一番回复也引起了 SkyNet 群的一阵唏嘘:

双方都是大神啊,我等小民,屌丝自然是看不太明白。。。300M等于多少?我告诉你,3亿!!!什么东西有这么多用户?

你没看错,是一台机器!!并发请求300K = 30万。

大师又发话了,尤其请注意最后画红线的地方(这绝不是空穴来风,晚一点我会给你SkyNet群里更详细的聊天截图,

几乎几天就会重复一次,大师警言,必须听好啊):

 然后是两人的互相扯皮,直到云风这个回复,倒是开始有点进入状态了,我也是看到这里才找到点感觉,看代码看评论,有点应接不暇:

不过云风这个言论就。。呵呵了。。。

我很白目的发了言,这说明我大概下午3点多上的,后来还prompt(推广)了一下jemalloc-win32的项目,有够白目的。。。:

然后包括云风在内的群里部分人都在讨论这个问题,我自然也不会放过啊,不管怎么说都得关心下嘛,

我觉得这种讨论,不管怎么样,只要结论是正确的或方向正确的,都是有益的。哪怕是很小的问题。

 

看到这样的言论,我也是不好说什么,你能理解我为什么要写这个东西了吧,这还是前菜,还有很多。

云风说的我也基本认可,但是也不认可,看来这次他倒是道出了一些实话,但这远远还不够,虽然我知道你知道,

但是的确,由于每个人处于的阶段或工作不同,你不能这么武断的下一个结论,至于是什么结论,你后面会看到很多的。

SkyNet群里潜藏着一些脑残粉,虽然我也不好说有多少,但是的确有,不是??至于为什么有脑残粉,我也不多说了。

只能说云风太飘逸了,大侠后面没有几个跟班的,这不符合武侠小说的路数。

 

请注意下面截图中的最后一句话,当然其他话也很重要:

 

当你读完整篇文章以后,你将知道我为什么把这一句划起来。。。

 

好了,还是回归正题,前面说到云风 2014-12-15 14:07:20 回的那个帖子,多少回归了一点理性,有些实际的根据(至少有实际的模拟),漫天空谈没有意义。

我也是在这个时候边看 Sinclair 的代码,边看两人的交锋。我也对 Sinclair 的边界判断表示疑问,通过仔细分析以后,才知道其实 Sinclair 这个写法,队列的实际长度只有(QSzie - 1)个元素,原因大致跟云风那个回复说的有点沾边,我想这绝不是 Sinclair 的设计初衷,不过他也利于了unsigned int的一些特性,虽有缺陷,但写法也是有点技巧的,但是通过仔细分析,我们可以找到更合理同时也准确的写法。有问题的是这一句:

if ((mask + tail - head) < 1U) return -1;

 

慢着,不好意思,插播一下,看到这样的言论,你说我们有不喷他的理由吗?

 

 

 

对于第二点,你真的用多核解决了性能问题吗?我怕是增加了哦!对于第三点,这个技巧在 Sinclair 的代码是广泛用到的,我也非常同意,云风其实不懂Cache,但看他以前的blog,倒是应该知道的,这里原因不明,可能他考虑不够细致吧。这个以空间换效率的事情还是非常值得做的,非常非常值得!!

刚才看QQ群聊天记录才发现,云风公司的同事 8楼有点高(曾经看过他用写的lua版gevent之类的,看来也不是等闲之辈) 写了个测试,只看到push和pop的完成数量,以及测试完成的时间,由于看不到代码,不好评价,我只是想说,这玩意不是很有概念,是很难写正确的。而 Sinclair 的 q3.h 我后来的验证也表示是正确的,不过有一段时间我的测试结果以为 q3.h 是错误的(原因是运行很久都不能完成,后来得知 q3.h 的使用是有条件限制的,结果虽是正确的,但有可能会LiveLock(活锁),我以后会说到,你也可以从我的代码里得到这个结论。还有一个问题就是,其未出现bug不代表就一定是正确,因为我后来仔细分析过后,至少他的逻辑跟我最终设计的逻辑有些差别,的确是可能测试未能触及极端的情况,没能表现出来,但我后来放弃了他这样的思路,因为这个思路再怎样设计都不够高效。)

8楼有点高 提到的这篇文章后来我也看了(陈皓写的,看得出来是大自然的搬运工,不过搬得还行,http://coolshell.cn/articles/8239.html),这也给我们认清这个问题提供了一个更清晰的思路。而老邓提到的两个亲和是不对的,但是padding和缓存读写冲突是对的,likely这种都不是事啦,不重要。看来老邓的总结能力还是很强的。亲和指的是CPU亲和,即让线程或进程只运行在指定的一个或几个CPU核心上。后来他也提到了自己要用C++写一个FixedQueue,倒也勾起了我后来写RingQueue的想法。老邓还真是一个牛比的人(我是真心的,每个人都有自己的特点和特长),我某些灵感都是从他那里来的。。。

(前面我打住那部分明天再讲……)

RingQueue

  好了,其实前半部分的内容两天前就已经就写好了,我一直不知道怎么继续写下去,现在我想好了,今天乱七八糟了补了一些,希望不要太介意。今天就先谈到这里,给你们一点时间去看代码,去研究,看你能不能超过云风,超过 Sinclair 。我写的 RingQueue 的GitHub地址是:https://github.com/shines77/RingQueue,如果是不支持GBK编码的编辑器或系统,可以下载UTF-8编码版:https://github.com/shines77/RingQueue-utf8。还是很不错的,我敢说是很棒的混合自旋锁,后面你就会看到是为什么。你现在也可以自己去下载回来看看,支持Makefile,支持CodeBlocks,支持Visual Studio 2008, 2010, 2013等,还支持CMake,支持Windows, MinGW, cygwin, Linux, Mac OSX等等,当然可能不支持ARM,没测试环境。

  明天还有更加精彩的,未完待续……敬请期待……

(注:RingQueue依然是一个研究与测试的程序,请不要上纲上线,不过你有好的建议,可以告诉我,目前有一个东西一直没弄进去,就是CPU是单核的时候是要判断一下的,直接跳过Spin(自旋)。)

更新记录

2015/01/04

修正一个错误:《无锁队列的实现》 http://coolshell.cn/articles/8239.html 的作者,是陈皓(hào),不是陈硕(shuò),原来我一直以为 coolshell 的是陈硕,其实是陈皓。。。(错了这么多年了)。陈硕我是知道的,Muduo的作者。下次起名字能不能不要这么像。。。我承认我脑残了。

目录

(一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer 

(四)RingQueue(上) 自旋锁  (五)RingQueue(中) 休眠的艺术

(六)RingQueue(中) 休眠的艺术 [续]

 

下一篇:一个无锁消息队列引发的血案(二)——月:混合自旋锁

.

posted @ 2015-01-01 21:24  shines77  阅读(18825)  评论(26编辑  收藏  举报