« Lua 5.2.2 中的一处 Bug | 返回首页 | 招聘 Lua 开发人员一名 »

XOR 链表

前两天阿楠同学想用链表实现一个消息队列,虽说是链表,但是没有从中间删除节点或添加节点的需求。只需要先压入的消息先处理。为了完成这个需求,最后实现了一个双向链表。

当然这个东西是用 Lua 写的,做一个双向链表也不算复杂。采用单向链表也可以做到,只需要多记录一个尾节点。不过今天想说的是,如果这个玩意在 C 里实现的话,其实只需要用一个指针的空间就可以搞定一个双向队列,可以从任意一头进出数据,可以以两个方向遍历,而实现需要的代码量却和单向链表类似。因为算法非常有趣,在 C 语言之外很少见到,知道并使用过的人也就不太多了。这就是所谓的 XOR Linked List

我们不需要在链表节点上保存前序和后继两个指针,而改而保存这两个指针的 XOR 值。把两个指针强制转换为 uintptr_t ,做位运算 ^ 即可。

这个数据结构可以工作起来,依赖位运算的一个特性: A^(A^B) == B ,我们知道前序地址或后继地址中的任意一个,都可以用这个值推算出另一个来。这样的链表,从前向后与从后向前遍历的算法是一样的,区别只在于初始参数。

每个链表对象,我们保存链表的首节点和尾节点的指针。由于首节点没有前序节点,它的链接信息只需要记录后续节点( XOR NULL 不会改变它)即可。当链表内只有一个节点时,链接信息记录的就是它自己的地址了。

对于队列,我们只需要在新节点加入时,替换掉首节点,链接信息设为原来的头节点,并把原头节点的链接数据域 xor 上新加入的节点地址即可。

而出队列的时候,只需要用尾节点的链接域替换掉它并用 xor 更新新的尾节点链接域的数据即可。


XOR 链表的实现很巧妙有趣,但并不意味着实现很复杂。它比传统的双向链表实现起来要简单的多(甚至更容易实现为无锁的数据结构,传统的单向链表很容易实现为无锁数据结构,而双向链表就麻烦的多 )。当然,局限性也很明显。

它很难在 C/C++ 之外的其它语言中实现,而且链接信息很隐讳,会导致调试起来更困难一些。

我们几乎只能在遍历链表的过程中去插入和删除单个节点(当然很多情况下,我们也只需要在这个场合做这些事情),插入一个节点需要同时知道两个节点,我们才能把新节点插入其间,删除节点也必须有它前或后的节点信息才行。

但这个数据结构在某些场景毕竟是有用的,比起用 xor 交换两个变量来说,XOR 交换算法 才真的像道智力题。

Comments

10年了
对于你的文件我有不同的看法:1你来自北京《可是为什么要到南京发展呢?还有你的文件版本实在是不堪路眼,太多的崽子要你的圣体。也许我不认识你你也不认识我的名字,但是我对于你的论文实在太精辟了
对于你的文件我有不同的看法:1你来自北京《可是为什么要到南京发展呢?还有你的文件版本实在是不堪路眼,太多的崽子要你的圣体。也许我不认识你你也不认识我的名字,但是我对于你的论文实在太精辟了
发现错别字:'出使参数'
感谢云风,我现在正在实现一个消息缓冲队列,正好给我启发了。
x86下还好吧,MemoryBarrier()是个编译器屏障,指令别优化错了就行,指令是对的,cpu执行的时候访存就不会乱序。
@浅浅水 在目前CPU都普遍乱序优化的情况下,我倒是很想知道你不用lock如何保证正确性?不是看理论就好的好么?早期的论文都是基于顺序执行和纯正常思考情况下设计的。现在绝大部分CPU都有乱序执行功能。你不用lock如何保证互斥性?C语言还要一个关键字volatile来保证变量访问不被优化-就这也无法能在多核而且都具有乱序执行下保证安全。纯学术派在一定程度上总是以为自己很权威,但是在现实面前往往非常悲剧。我只能说他们真的只适合做理论研究。关于这个乱序执行的问题文章中几乎没什么针对性的说明。
@浅浅水 你的skynet cpp版打算开源吗?支持window平台吗?
这个设计想法是不错。
linux 内核的struct list_head?
但并意味着实现很复杂。 C/C++ 只外
@irachex i==j时应该不会出现仍是原值不会变成0
10分钟前刚在wikipedia看到这个说法,这里就有文章可以看了,赞命运
满足群性质的集合和二元运算都可以像xor那样交换http://chris-taylor.github.io/blog/2013/02/25/xor-trick/
@浅浅水 谢谢你给的论文, 我错了,我把链接给到正文里去了。
XOR swap有个坑:当应用于一个数组时(比如qsort),swap a[i]和a[j],当i == j时就会都变成0了
这跟保存列表的倒数第二个指针有啥区别。。。
好精巧的设计,回头我去实现个无锁的版本看看(估计是很久之后),因为最近忙着复刻你的skynet cpp版(设计已经有了巨大的变动。不过是受skynet的启发)。。~ bty:双向链表也是可以做到无锁,采用环形链表,居论文讲性能还要略高,http://people.csail.mit.edu/edya/publications/OptimisticFIFOQueue-journal.pdf

Post a comment

非这个主题相关的留言请到:留言本