我正在尝试提出用于高吞吐量 C++ 服务器的最佳数据结构。该数据结构将用于存储从几到几百万个对象的任何内容,并且不需要排序(尽管可以非常便宜地提供唯一的排序键)。
要求是它能够支持高效的插入(最好是 O(1))、中等效率的删除和高效的遍历。它不需要支持查找操作(删除可能需要的除外)。
不同之处在于,当其他线程枚举数据结构时,它对于修改必须是线程安全的。这意味着简单的红黑树不起作用,因为一个线程无法在不弄乱其他线程持有的任何游标的情况下插入元素(并执行必要的树旋转)。
It is not使用读/写锁并推迟写操作直到所有读者都完成是可以接受的,因为读操作可能会长期存在。当有读者存在时发生的插入对于该读者是否可见并不重要。
内存占用也很重要,显然越小越好!
有哪些建议?
对评论的回应:
感谢您的回答。
不,插入不能使现有迭代器无效。迭代器可能会也可能不会看到新的插入,但它们必须看到如果插入没有发生时它们会看到的所有内容。
删除是必需的,但是由于更高级别的规则,我可以保证迭代器永远不会在可删除的项目上停止。
为游标锁定每个节点会对性能产生太大影响。可能有多个线程同时读取,并且多个线程在锁中使用的任何类型的内存热点都会杀死内存带宽(正如我们艰难地发现的那样!)。即使使用调用 InterlockedIncrement 的多个线程进行简单的读取器计数也无法完全扩展。
我同意链接列表可能是最好的方法。删除很少见,因此为支持 O(1) 删除而支付后向指针的内存代价是昂贵的,我们可以根据需要单独计算它们,因为删除往往是批量操作。
幸运的是,插入链表不需要读者进行任何锁定,只要在头指针更改之前更新插入节点中的指针即可。
锁定-复制-解锁的想法很有趣。所涉及的数据量太大,无法作为读者的默认设置,但当作家与读者发生冲突时,它可以用于作家。读/写锁将保护整个结构,并且如果与读取器发生冲突,则写入将克隆数据结构。写入比读取少得多。