纯功能(持久)环形缓冲区
Posted
技术标签:
【中文标题】纯功能(持久)环形缓冲区【英文标题】:Purely functional (persistent) ring buffer 【发布时间】:2018-10-19 18:36:19 【问题描述】:我想使用纯函数数据结构实现一个环形缓冲区,并进行以下操作
按索引进行高效随机访问 添加到前面 从后面移除之所以使用持久化数据结构,是因为我有一个写线程和多个读线程,我想避免读线程阻塞写线程。
这可以通过让读取器线程仅在拍摄快照时持有锁然后使用快照进行处理来轻松完成。
支持这些操作的现有可用数据结构有哪些?
双向链表不能高效地进行索引查找,并且是 O(n) Clojure PersistentVector 基于 Phil Bagwell 理想哈希树,支持 log32N 中的索引访问,并且 subvec 可用于从头开始删除元素。 哈希数组映射的 trie 也可以通过将整数存储为键来使用,但可能效率不高。在这种情况下还可以使用哪种其他纯函数式数据结构?
【问题讨论】:
你能描述一个用例吗?听起来你们有矛盾的目标。 读者还是要屏蔽作者;您不能冒险在读取期间写入更新会使索引无效。 或更准确地说,现有读取会阻塞写入器,但读取请求不能抢占待处理的写入请求,以避免饥饿。 chepner:该对象是不可变的,并且永远不会就地更新。作者无法修改它,它只能创建一个与旧版本共享某些对象的新版本。读取器和写入器将在更新指向最新版本的指针时简单地锁定。拍摄快照后无需锁定即可使用快照。 为什么需要随机访问?似乎没有它,您可以只使用几个concurrent queues之一。 【参考方案1】:finger tree(在标准库中为Data.Sequence
)是持久随机访问序列的首选。我认为它满足您的标准--随机访问索引是 O(log n)(更具体地说,是索引与边缘距离的日志),其他是 O(1)。我不知道有比这更好的持久性数据结构。
【讨论】:
非常感谢 luqui,所以手指树比 clojure PersistentVector 更好? 稍微阅读后,似乎 clojure 持久向量只是 32 叉树。手指树在两端推和弹方面会更好;随机访问应该具有可比性。 @skyde,Data.Sequence
中的手指树有相当糟糕的常数因子。您最好吃(小)对数成本来访问末端,以换取从更粗的树更好的随机访问。当然,这完全取决于您的使用模式。
@dfeuer 你能否澄清你所说的 Data.Sequence 有相当糟糕的常数因子是什么意思。你的意思是随机访问会比 32 叉树慢吗?
Clojure 持久向量不能用作队列,因为 subseq 维护对整个底层向量的引用,因此从后面“弹出”的所有项目都不会被垃圾收集:(以上是关于纯功能(持久)环形缓冲区的主要内容,如果未能解决你的问题,请参考以下文章