C++ 将链表嵌入数据结构的优点是什么?

C++ 将链表嵌入数据结构的优点是什么?,c++,optimization,intrusive-containers,C++,Optimization,Intrusive Containers,在阅读FreeBSD中的内核数据结构时,我偶然发现了MBuf。MBuf包含一个指向MBuf链中下一个MBuf的指针,实现一个链表。每个MBuf本身也包含特定于链接列表中该节点的数据 我更熟悉将容器类型与值类型分离的设计(考虑std::list,或System.Collections.Generic.LinkedList)。我正在努力理解将容器语义嵌入到数据类型中的价值主张——获得了什么样的效率?这真的是为了消除节点实例指针存储吗 获得了什么样的效率?这真的是为了消除节点实例指针存储吗 我认为缓存

在阅读FreeBSD中的内核数据结构时,我偶然发现了
MBuf
MBuf
包含一个指向
MBuf
链中下一个
MBuf
的指针,实现一个链表。每个
MBuf
本身也包含特定于链接列表中该节点的数据

我更熟悉将容器类型与值类型分离的设计(考虑
std::list
,或
System.Collections.Generic.LinkedList
)。我正在努力理解将容器语义嵌入到数据类型中的价值主张——获得了什么样的效率?这真的是为了消除节点实例指针存储吗

获得了什么样的效率?这真的是为了消除节点实例指针存储吗

我认为缓存未命中率越低,总体性能越好(即使链表通常不是缓存友好的数据结构)。
这样,您就不必再按照一个指针来查找内存中的某个位置的数据,并将它们靠近每个节点的处理器。

此外,如果您在连续的内存区域中构造节点,并使用两个指针管理它们(让我们称它们为空闲列表和使用中列表,听起来熟悉吗?),您可以在性能方面得到提升(至少只要列表中不包含很多项,否则风险是在内存中来回跳跃)。在这种情况下,In和deletition具有恒定的时间(当然,除非在插入到特定位置之前必须搜索列表中的节点),这是另一个优势。

假设您有一个指向列表中节点的迭代器/指针。为了获取数据,您必须:

  • 从节点读取指向数据的指针
  • 取消对刚刚读取的指针的引用,并读取实际数据
另一方面,如果列表概念“嵌入”到数据结构中,则可以在单个内存操作中读取对象,因为它与节点本身在一起

分离列表节点及其数据的另一个问题是列表节点本身很小(通常只有2或3个指针)。因此,在内存中保留这样一个小结构的内存开销可能会很重要。您知道,诸如
new
malloc
之类的操作实际上消耗的内存比分配的内存多,系统使用它们自己的树结构来跟踪内存空闲的位置和不空闲的位置

在这种情况下,将事情分组到单个分配操作中是有益的。您可以尝试将多个列表节点保存在小型捆绑包中,也可以尝试将每个节点与其分配的数据连接起来

类似的策略可以在入侵指针(相对于共享指针)或将对象和智能指针数据打包在一起的
std::make_shared
中看到


zett42做了一个注释,说明
std::list
T
与节点数据一起保存。这实现了我上面解释的单个内存块,但有一个不同的问题:
T
不能是多态的。如果您有一个类
a
及其派生
B
,则
node
不是
node
B
的派生,只将表示
a
的部分复制到节点中
如果要在单个列表中保存多态对象,典型的解决方案是实际拥有
std::list),其中节点信息实际上是
A
对象的一部分。然后,每个节点都可以是
a
的派生节点,而不会出现问题。

侵入式链表的一大优点是,您可以创建一个预先存在的对象列表,而无需任何新的分配。要使用std::指针列表执行此操作,需要分配内存


Boost有一个侵入式列表实现,并提供了使用理由

链表是一种非常简单的结构,通常只需添加一个指向结构的下一个指针,就可以完成,而不是处理一个抽象类。我非常确定,
std::list
内部不会将内存分配给与“上一个”和“下一个”指针不同的值,而是分配一个包含值和指针的连续内存块。我可能错了,但这从性能的角度来看是有意义的。刚刚检查过,VS2015附带的
std::list
实现具有以下内部节点数据类型:
struct\u list\u node{u Voidptr\u Next;\u Voidptr\u Prev;\u Value\u type\u Myval;}。。。因此与
MBuf
没有根本区别。
std::list
只是很好地封装了所有内容,因此只要看一下公共接口,就会发现值类型是“隔离的”。至少在VS2015实现的
std::list
中,上一个和下一个指针以及节点的实际数据都包含在一个“节点”结构中,因此,“真实”数据没有单独的分配:
struct_List_node{u Voidptr_Next;_Voidptr_Prev;_Value_type_Myval;}