具有固定插入次数的映射的内存分配
我想将 n 个元素插入到预先知道 n 的映射中。我不想在每次插入时分配内存。我想要一开始就分配所有内存。有办法做到这一点吗?如果是这样,怎么办?编写某种内存分配器会有帮助吗?
我运行 GMan 的代码并得到以下输出。 GetMem 是从调用“new”时打印的,FreeMem 是从调用删除时打印的。 size 是请求的字节数,ptr 是返回的指针。显然,分配/释放是在插入期间进行的。你如何解释这一点?
GetMem 大小 40,指针 0x8420008
GetMem 大小 40,指针 0x8420038
GetMem 大小 120,指针 0x8420068
GetMem 大小 120,指针 0x84200e8
FreeMem 指针 0x8420068
FreeMem 指针 0x8420038
FreeMem 指针 0x8420008
插入:[0,0]
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
插入:[1,2]
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
插入:[2,4]
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
插入:[3,6]
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
插入:[4,8]
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
插入:[5,10]
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
GetMem 大小 40,指针 0x8420008
FreeMem 指针 0x8420008
FreeMem 指针 0x84200e8
St9bad_alloc
I want to insert n elements into a map where n is known ahead of time. I do not want memory allocation at each insertion. I want all memory allocation at the beginning. Is there a way to do this? If so, how? Will writing some sort of memory allocator help?
I ran GMan's code and got the following output. GetMem is printed from a call to "new" and FreeMem is printed from a call to delete. size is the number of bytes requested and ptr is the pointer returned. Clearly, allocation/deallocation is going on during insertion. How do you explain this?
GetMem size 40, ptr 0x8420008
GetMem size 40, ptr 0x8420038
GetMem size 120, ptr 0x8420068
GetMem size 120, ptr 0x84200e8
FreeMem ptr 0x8420068
FreeMem ptr 0x8420038
FreeMem ptr 0x8420008
Inserting: [0,0]
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
Inserting: [1,2]
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
Inserting: [2,4]
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
Inserting: [3,6]
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
Inserting: [4,8]
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
Inserting: [5,10]
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
GetMem size 40, ptr 0x8420008
FreeMem ptr 0x8420008
FreeMem ptr 0x84200e8
St9bad_alloc
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
对分配测试的响应
将此添加到我在下面给出的任一示例中:(
注意,这些不是分配运算符的完全正确替换,并且用于演示。)
运行运行时大小的示例程序会给出以下输出:
请注意,一旦开始插入,就不会再进行分配。您应该没有获得内存分配。您如何生成输出?
分配器
您需要的是一个新的分配器。这是我现在制作的东西,所以它相对未经测试,但对我来说看起来不错。
它创建一个 freelist 并使用它来释放内存。分配器的构造需要 O(N),但分配和释放都是 O(1)。 (非常快!)此外,一旦构建完成,就不再进行内存分配。尽管空闲列表具有平均局部性,但它可能会击败您通常使用默认分配器从映射中获得的结果。
在这里:
并使用:
现在我已经将大小设置为编译时常量,但是您想要运行时版本,请告诉我,我会写下来。这是一个在运行时获取大小的版本:用途:
如果没有更多剩余插槽,运行时版本可能会分配更多空间,这可能很有用。但我把这个留给你。 (不要调整向量的大小!你可能会丢失整个缓冲区。你可能需要一个向量的
列表
。)请注意,如果你可以使用Boost,他们的内存中有这样一个分配器池库。也就是说,它们的分配器会跟踪您请求内存的顺序,以确保反向构造销毁顺序。这将分配和释放变为 O(N)。 (在我看来,这是一个糟糕的选择。)实际上,我围绕
boost::pool
编写了自己的分配器来解决这个问题。Response to allocation test
Add this to either of the samples I give below:
(Note, these are not fully correct replacements of the allocation operators, and are for demonstration.)
Running the runtime-sized sample program gives me the following output:
Note there are no allocations once insertion has begun. You should be getting no memory allocations. How are you generating your output?
The allocators
What you need is a new allocator. Here's something I made now, so it's relatively untested, but it looks good to me.
It creates a freelist and uses that to give out memory. Construction of the allocator takes O(N), but both allocations and deallocations are O(1). (Very fast!) Also, once it's constructed, no more memory allocations take place. Though freelists have average locality, it probably beats what you'd normally get out of a map with the default allocator.
Here it is:
And use:
For now I've made the size a compile-time constant, but you want a run-time version just let me know and I'll write that up.Here's a version that takes a size at run-time:Use:
The run-time version could possibly allocate more space if there are no more remaining slots, which can be useful. But I leave that to you. (Don't resize the vector! You'll possibly lose your entire buffer. You'll need a
list
of vectors, likely.)Note if you can use Boost, they have such an allocator in their Pool library. That said, their allocator keeps track of the order you request memory, to ensure reverse construction destruction order. This turns allocations and deallocations into O(N). (A terrible choice in my opinion.) I actually wrote my own allocator around
boost::pool<>
to get around this.使用哈希表。
unordered_map
可能不合适,因为它允许单独分配每个对象,并使用桶的“封闭寻址”而不是一个平坦的内存块。请参阅 http://en.wikipedia.org/wiki/Hash_table#Open_addressing 了解您应该考虑的结构类型的描述。实现具有恒定访问时间和恒定插入时间的关联容器并不太困难。
不过,对删除的支持可能有点混乱,您需要在哈希表中分配相当大的空白空间,也许是您实际使用的空间的两倍,以保持地址空间整洁。
Use a hashtable.
unordered_map
might be inappropriate as it allows each object to be allocated separately, and uses "closed addressing" with buckets instead of one flat block of memory.See http://en.wikipedia.org/wiki/Hash_table#Open_addressing for a description of the kind of structure you should consider. It's not too difficult to implement an associative container with constant access time and constant insertion time.
Support for deletion can be a little messy, though, and you will need to allocate considerable empty space in the hash table, perhaps double what you actually use, to keep the address space uncluttered.
map
不需要像vector
那样的方式。由于map
在内部实现为树,因此插入很便宜(您不需要移动整个块)。另一方面,对于超过当前保留边界的向量插入,需要移动所有先前分配的元素。也就是说,如果分配性能对您来说非常重要,您可以编写一个自定义分配器,例如从预分配的缓冲区进行分配。如果您正确实现此功能,它将比
map
使用的默认new
更快。然而,我怀疑你是否必须走这么远。This isn't required for a
map
the way it's required for avector
. Sincemap
is implemented as a tree internally, insertions are cheap (you don't move around whole chunks). On the other hand, for avector
insertions that take it over the currently reserved bound require to move all the previously allocated elements.That said, if allocation performance is super crucial to you, you can write a custom allocator that, say, allocates from a pre-allocated buffer. If you implement this correctly, it will be faster than the default
new
used bymap
. However, I doubt you must go this far.std::map
不提供任何内置支持来执行此操作。但是,如果元素数量足够小,那么您可以创建成对的 std::vector 并使用 vector::reserve 方法来保留所需的空间。std::map
doesn't provide any built in support for doing this. But, if the number of elements is small enough, then you can createstd::vector
of pairs and usevector::reserve
method to reserve the needed space.