Ben Pfaff(e)k dio:
> Howard Hinnant <> writes:
>
>> If you go through the exercise of writing a reusable dynamic array in C
>> (as people do, e.g.
>> http://geta.life.uiuc.edu/~gary/prog...ca105b/Array.c ),
>> then you eventually end up with code that looks something like
>> "addToArray" found at the above link:
>
> I kind of like the x2nrealloc function that some GNU software
> uses. It's a little more flexible and simpler to use than the
> typical dynamic array. Here's the documentation:
The C reallocation feature, in my opinion, misses some important points
that make memory allocation suboptimal, and disallows some C++
features:
-> Not all objects stored in the buffer can be binary copied. An struct
can have a pointer to itself, for example. This problem is obvious when
using C allocation functions to build C++ allocators for non POD
objects. Realloc binary copies automatically data, so we can't use
realloc for non binary copyable objects. We need a memory
reallocation/allocation function that has an option to disable data
copying.
-> We can't specify both a minimum size for allocation/reallocation and
a preferred size. We have to guess a reallocation size (for example,
doubling the size). Most of the times, we need to insert N extra
objects in a buffer with S objects, and currently we call realloc
doubling the size -> S*2. However, maybe the current block can be
expanded between N and S*2. Obviously, we prefer an expansion than a
reallocation:
allocate_at_least(p
,S+N /*min size*/
,S*2 /*preferred size*/
&allocated);
The meaning: if the current block can be expanded at least to S+N, do
it, otherwise try to allocate S*2, otherwise, allocate S+N, otherwise
return 0. Checking for expansion is very cheap, and if the next block
is free with a size between N and S, we can avoid the fragmentation and
reuse that space. This makes buffer expansion more probable, minimizes
allocations and improves performance.
-> In many realloc implementations, we can't expand backwards. Imagine
the following common situation, after some allocation/deallocation
operations:
| free1 | current_buffer | free2 |
free1 and free2 are not big enough to hold S+N elements, but free1 +
current_buffer + free2 is big enough. This reallocation is very fast
(surely constant time) in most implementations (for example using a
doubly linked list of memory blocks). Apart from this, locality is
improved since the previous block can be in the same memory page. Less
size overhead, less fragmentation, more locality and avoiding an
unneeded allocation. Unconditional backwards reallocation can disallow
any reallocation for complex c++ objects (for example, objects whose
constructor can throw) if we need strong exception guarantee. So I
would make backwards expansion optional.
----
The overhead of memory allocation is in many known applications the
biggest bottleneck. Minimizing unneeded allocations and using expansion
possibilities will reduce memory usage, will improve locality and will
improve speed.
Regards,
Ion