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