J
jacob navia
The first version of the container library was only that, a first version,
where the main objective was to show how it could be implemented.
One thing that immediately ringed bells in my mind was the fact that
the list module (even if in my machine was as fast as the C++ module)
in other (probably slower) machines like solaris. Besides, C++ used only
about 700K, but my container used 1200K. Definitely too much.
First thing I discovered is that under C99
struct list_element {
struct list_element *Next;
char Data[];
};
sizeof(struct list_element) --> sizeof(void *)
Since I wanted to run under C89, I changed this to
struct list_element {
struct list_element *Next;
char Data[1];
};
without realizing that now the sizeof(struct list_element)
was 2*sizeof(void *). This produced an overhead of already 33%
more memory consumption when the size of the data was just an integer
(4 in a 32 bit system).
Second thing I realized is that I was allocating each piece of
the list, each list_element, using malloc to minimize memory
usage. Problem is, the overhead of malloc was adding enormously
to the total memory consumption. Obviously I forgot the first
lesson of malloc usage:
DO NOT USE MALLOC VERY OFTEN!
(And this includes also GC_malloc. When I turn on the GC,
performance is significantly slower than with malloc, probably
because the GC malloc is more expensive than the standard
malloc)
I decided then to build a simple heap management for container allocation.
I allocated 1000 pointers to blocks of data that could contain up to
1000 list elements.
This DECREASED memory consumption from 785 000 using malloc, to
408 000 bytes. Almost a 50% savings!
The same program in C++ (using optimized or not optimized settings)
takes 1 177 172 bytes (using Visual C++ 2008). I suppose that other
C++ implementations take less but not significantly less.
Obviously this simple schema will get more complicated when you
consider what happens when you free list elements. C++ takes a
significant time to close down, since probably the destructors
are getting called for each list element. In C, I have to figure
out a good way to free things, maintain a free list, etc. Even
if the overhead will never attain the C++ heights, it will surely
slow down the software a bit.
This primitive heap manager can be expanded also for a mark/release
style of programming, where you allocate things from a list and
when you destroy the list all the memory used by the list is
freed in a few calls to free.
where the main objective was to show how it could be implemented.
One thing that immediately ringed bells in my mind was the fact that
the list module (even if in my machine was as fast as the C++ module)
in other (probably slower) machines like solaris. Besides, C++ used only
about 700K, but my container used 1200K. Definitely too much.
First thing I discovered is that under C99
struct list_element {
struct list_element *Next;
char Data[];
};
sizeof(struct list_element) --> sizeof(void *)
Since I wanted to run under C89, I changed this to
struct list_element {
struct list_element *Next;
char Data[1];
};
without realizing that now the sizeof(struct list_element)
was 2*sizeof(void *). This produced an overhead of already 33%
more memory consumption when the size of the data was just an integer
(4 in a 32 bit system).
Second thing I realized is that I was allocating each piece of
the list, each list_element, using malloc to minimize memory
usage. Problem is, the overhead of malloc was adding enormously
to the total memory consumption. Obviously I forgot the first
lesson of malloc usage:
DO NOT USE MALLOC VERY OFTEN!
(And this includes also GC_malloc. When I turn on the GC,
performance is significantly slower than with malloc, probably
because the GC malloc is more expensive than the standard
malloc)
I decided then to build a simple heap management for container allocation.
I allocated 1000 pointers to blocks of data that could contain up to
1000 list elements.
This DECREASED memory consumption from 785 000 using malloc, to
408 000 bytes. Almost a 50% savings!
The same program in C++ (using optimized or not optimized settings)
takes 1 177 172 bytes (using Visual C++ 2008). I suppose that other
C++ implementations take less but not significantly less.
Obviously this simple schema will get more complicated when you
consider what happens when you free list elements. C++ takes a
significant time to close down, since probably the destructors
are getting called for each list element. In C, I have to figure
out a good way to free things, maintain a free list, etc. Even
if the overhead will never attain the C++ heights, it will surely
slow down the software a bit.
This primitive heap manager can be expanded also for a mark/release
style of programming, where you allocate things from a list and
when you destroy the list all the memory used by the list is
freed in a few calls to free.