在连续的内存块中创建具有可变元素的链表
Posted
技术标签:
【中文标题】在连续的内存块中创建具有可变元素的链表【英文标题】:Making a linked list with mutable elements in a contiguous block of memory 【发布时间】:2021-11-29 08:31:50 【问题描述】:我正在阅读有关在实时上下文中使用内存池的信息,我想实现文章Making a Pool Allocator 中的代码。我已经陷入了困境:制作一个由固定大小的块组成的循环单链表,它们都位于一个连续的块中。再往下,链表用于跟踪下一个空闲内存块;分配的块从列表中弹出;已释放的块被添加回列表中。
对于固定大小的元素的连续块,我显然考虑了不可变元素的Vector
。我可以使用数组索引作为地址,而不是使用可变节点创建循环链表:
struct ChunkT
next::Int
data::T
end
function Pool(T, size)
pool = Chunk.(2:size+1, zero(T))
pool[size] = Chunk(1, zero(T))
return pool
end
intpool = Pool(Int, 5)
pool[size] = Chunk(1, zero(T))
行让我停顿了一下:不可变 Chunk
s 的一个缺点是,每次我对 data
进行操作时,我都需要访问和编辑 pool
,而我不想这样做无论如何都限制为不可变的data
。不幸的是,元素通常独立存在并且可以比容器寿命更长,例如return pool[1]
,因此可变元素与包含的结构或数组分开分配在堆上。但是,在这个用例中,元素只存在于数组中。
这可能是一个长镜头,但有没有办法制作一个可变元素的链表,这些元素分配在一个连续的内存块中?
【问题讨论】:
【参考方案1】:一段时间后,我发现我在要求一些奇怪的东西:一个所谓的“可变”对象,它不是在堆上独立分配的,因此没有通常意义上的地址。我真正需要的是可变性的语法;变异pool
就好了,索引可以作为地址。我可以通过为代表pool[index]
的类型重载getproperty
和setproperty!
来实现赋值突变。这几乎不是最终草案,但它解决了我的问题中提出的问题:
# include the original post's code here
struct PoolRefT
pool::VectorChunkT
index::Int64
end
# points to heap-allocated Vector, but not heap allocated in Julia >=1.5
function Base.getproperty(p::PoolRefT, name::Symbol) where T
if name == :data
getfield(p.pool[p.index], name)
else # :pool, :index
getfield(p, name)
end
end
function Base.setproperty!(p::PoolRefT, name::Symbol, newdata::T) where T
if name == :data
oldvalue = p.pool[p.index]
newvalue = Chunk(oldvalue.next, newdata)
p.pool[p.index] = newvalue
end
end
# intended usage
x = PoolRef(3, intpool)
x.data += 33
x.data
对于具有多个数据字段的Chunk
,创建一个仅编辑选择字段的新实例既复杂又浪费,因此在这种情况下,我可以为每个字段保留单独的池。如 Julia 1.5 Highlights 中所述,我也不确定在堆栈上分配不可变结构或在数组中而不是在堆中内联分配不可变结构的确切“大小限制”,但将字段拆分为池也会有所帮助。
当然,这种解决方法的替代方法是在 C 中管理内存并将其包装在 Julia 中,但我会在另一天了解。
【讨论】:
以上是关于在连续的内存块中创建具有可变元素的链表的主要内容,如果未能解决你的问题,请参考以下文章