
Why you should use talloc for your next C project - bluetech
http://blogs.fedoraproject.org/wp/sgallagh/2010/03/17/why-you-should-use-talloc-for-your-next-project/
======
latitude
talloc is based on my library called halloc [1], which stands for
_hierarchical allocator_. The idea is that every malloc'ed block can be
assigned a parent block. Freeing the parent block also frees all its children
in a recursive fashion, and that's about it. I cooked up halloc several years
ago when implementing IKE (the IPsec keying protocol) library and it proved to
be very useful for reducing the verbosity of the code without loosing any
clarity.

Tridge fluffed up halloc to make the API more fit and convenient for Samba
code, and talloc came out. I still like my version better though because it is
simpler and (subjectively) more elegant.

[1] <http://swapped.cc/halloc>

~~~
FooBarWidget
No offense intended, but I have to say I was a little bit disappointed by
halloc. I expected it to use a custom allocation scheme, like malloc()ing
large chunks of memory in one go in order to reduce malloc() time and space
overhead and to increase locality of reference. Instead it looks like it just
malloc()s every object separately. I'm sure some people will find halloc
useful, but in my C++ project where I already use smart pointers I didn't see
any advantage in using halloc.

It doesn't look like talloc is any better.

~~~
rwmj
Do you have evidence that rolling your own suballocator under malloc is better
than just using malloc. By "evidence" I mean you actually measured it, not
arm-waving.

~~~
FooBarWidget
Yes. For example my app has a hash table which it fills upon every request.
After this initial filling, not more hash table entries are set. At the end of
the request the hash table is deleted, and a new one is created at the next
request. I improved performance a bit by clearing the hash table instead of
deleting and recreating it. What gave me an even bigger boost in performance
was by using a custom allocator that allocates big chunks of memory in a
single operation.

The thing is my app is already not doing a lot of work per request so almost
any speedup is noticeable.

~~~
scott_s
You saw benefit because you implemented a custom allocator whose behavior was
optimized to the memory access pattern that you planned to use with it. Any
decent implementation of malloc already allocates large chunks of contiguous
memory from the operating system and tries to maintain locality among
allocations. It's also possible that you saw performance improvement from the
removal of a function call - it's possible that the call to your custom
allocator could be inlined, whereas malloc has to be a function call.

My understanding of halloc and talloc is that they are _general purpose_
allocators. A pure library approach - that is, without source code analysis
and code generation - won't be able to tailor the allocation scheme based on
the data structures themselves.

~~~
latitude
FooBarWidget got a good point though.

When two blocks are explictly tied into a parent-child relationship, it gives
the allocator valuable locality information. It is another matter that halloc
separates allocation and block binding steps, so the relationship information
is not available at the allocation time, and so it cannot utilize these
locality hints.

------
alextp
This feels slightly untrustworthy. What if there are still external references
to subfields of a struct? I think it's probably safer to just write explicit
constructors (that allocate and initialize) and destructors (to free properly)
for the things you use.

~~~
jpr
Why does everyone conflate allocation and initialization?

~~~
regularfry
RAII, at a guess.

------
nviennot
Writing a pair of constructor/destructor is not a problem.

tmalloc() adds a layer of complexity that is unhealthy to C programs. Are the
few seconds you save by using tmalloc() really worth the hours spent to debug
memory leaks 6 months later when the project got big and complex with multiple
collaborators ? No.

Introducing side-effets under the table is the best way to confuse other C
programmers when they read your code. It's not worth it.

Memory management is part of C, and will always be. If you don't like it,
switch to another language.

~~~
ryannielsen
What side effects were introduced "under the table"? talloc introduces
entirely new functions, it doesn't override standard malloc/free. All of the
new behaviors are rather explicit.

As for large project with collaborators: if the project doesn't already have
coding standards or doesn't have a code review process for new members then
they're already headed down a bad path. And anyone blindly introducing talloc
into an existing project without first getting agreement from other
participants and taking steps to smooth integration is irresponsible.

The issues you give aren't reasons to dismiss talloc. Bugs, implementation
flaws, design limitations are some valid reasons. By your argument, anything
layered on top of the C standard library should be avoided.

~~~
nviennot
1) freeing memory automatically for you. That's under the table :)

2) Absolutely

3) No. it's just that memory management related issues are really nasty. On
the other hand, adding a new printf() function won't do much harm.

------
Locke1689
One tricky thing here is that this does break the normal memory management
semantics. That is, while one nice thing about malloc/free is that you can
drop pretty much _any_ implementation of a user-space memory allocator in
there without changing your code, this is no longer the case. Whatever
replacement allocator you use will have to change its interface, and probably
its data structure, to fit the new model.

Edit: Also, how is this better than doing something like this

    
    
      struct some_complex_struct {
      ...
      };
    
      free_some_complex_struct(struct some_complex_struct * scs) {
      ...
      }

~~~
daxelrod
I don't understand why talloc preclues a different malloc/free implementation.

According to the article, talloc is a malloc wrapper. If that's the case, why
shouldn't you be able to drop in whatever allocator you want? talloc should
still use it under the covers.

~~~
Locke1689
Ah, I didn't see it's a wrapper. In that case, what's the point of using it at
all? If it doesn't help to optimize the memory allocation, it seems more like
a macro and less like a library.

~~~
xyzzyz
Author of the article says quite clearly why he thinks it is useful.

~~~
Locke1689
But he doesn't quite make it clear if he wants it done for every memory
allocation, or that specific type of allocation. The two have very different
results.

------
dhruvbird
How does this work for a case like DOM nodes? Where each child is constantly
re-assigned parents? Is there any way to reset the parent?

~~~
bluetech
Yes, and you can also have multiple parents with a reference-count semantics.

    
    
      void * talloc_steal(const void * new_ctx, const void * ptr);
    
      The talloc_steal() function changes the parent context of a talloc
      pointer. It is typically used when the context that the pointer is
      currently a child of is going to be freed and you wish to keep the
      memory for a longer time.
    
      NOTE: It is possible to produce loops in the parent/child relationship
      if you are not careful with talloc_steal(). No guarantees are provided
      as to your sanity or the safety of your data if you do this.
    

There's also talloc_reparent where you explicitly choose the parent to change.

~~~
dhruvbird
This is nice!! Thanks!!

------
mtk
was surprised to see no mention of:

<http://www.gnu.org/s/hello/manual/libc/Obstacks.html>

seems like a very similar concept / means to an end?

------
tanoku
Why you shouldn't use talloc for your next C project:

GPL v3.

~~~
JoshTriplett
Talloc uses the LGPL, not the GPL.

~~~
haberman
Even LGPL is a pretty steep price for the value being offered. The LGPL
requires that is it possible to substitute a modified version of the library,
which can be a logistical problem. Do you really want to have to link your
malloc() as a shared library? How do you let the user substitute inline
functions? What do you do when people start bugging you because they weren't
able to substitute a modified version of the library due to some technical
edge case?

Too much legal baggage for such a small piece of functionality.

~~~
JoshTriplett
I wouldn't call talloc a small piece of functionality. Also, the LGPL
explicitly states that dynamic linking suffices, and also explicitly covers
the inclusion of bits from header files. I do agree that the LGPL makes it
non-trivial to statically link a proprietary application with the library;
personally, I think it would help if the FSF had standard exception language
libraries could use if they don't care about relinking and only care about
requiring source for modified versions of the library. That said, I don't
write proprietary software, so that doesn't affect me. :)

~~~
haberman
> Also, the LGPL explicitly states that dynamic linking suffices

Good point, I thought that the requirement was substitutability and dynamic
linking was just given as an example of how a library can be substitutable,
but it appears that dynamic linking is sufficient.

> and also explicitly covers the inclusion of bits from header files.

It's not clear to me exactly the meaning of these sections; if including bits
from header files is truly allowed, then you could effectively circumvent the
substitutability goal by simply inlining the whole library.

> That said, I don't write proprietary software, so that doesn't affect me. :)

I don't either, but I write a BSD-licensed library that I absolutely want to
be usable with proprietary software without any difficulty.

~~~
JoshTriplett
> Good point, I thought that the requirement was substitutability and dynamic
> linking was just given as an example of how a library can be substitutable,
> but it appears that dynamic linking is sufficient.

You can satisfy the requirement without dynamically linking (by shipping
object files or source code instead), making dynamic linking sufficient but
not necessary. In practice, though, nobody seems to attempt anything other
than dynamic linking against an LGPL library, or shipping enough source code
for static linking (even if not all the source code).

> It's not clear to me exactly the meaning of these sections; if including
> bits from header files is truly allowed, then you could effectively
> circumvent the substitutability goal by simply inlining the whole library.

Substitutability represents a technical goal, and the library has to support
that goal for it to prove useful. If the library provides the entirety of its
functionality via inlines in header files, they clearly don't care about
substitutability. And if you modify the library to move all of it into header
files, you still have to supply the source for the modified version of the
library, so you've broken substitutability but you haven't broken the
copyleft. As far as I can tell, the LGPL's goal of substitutability seems
primarily aimed at cases where you don't intend to substantially modify the
library; after all, you can also change the library's API/ABI and thus make
the original no longer substitutable. (Personally, I don't really see the
point of the substitutability provisions anymore; I really just want a
copyleft on the library itself.)

> I don't either, but I write a BSD-licensed library that I absolutely want to
> be usable with proprietary software without any difficulty.

Fair enough. However, in that case the copyleft on the LGPL software seems
equally onerous, since users of your library would need to ship the source of
talloc, even though they don't need to ship the source of your library. (One
other interesting provision: the users of your library couldn't use a license
that prohibits reverse-engineering.)

In any case, yes, talloc's license effectively makes it unusable by libraries
that want to provide an all-permissive license. If you really want to use
talloc, you might consider writing to the authors of talloc; they might prove
sympathetic to the needs of a BSD-licensed library, and grant a suitable
exception. (Personally, I'd like to see some standard wording for an exception
similar to the one used in UPX, namely that you can use it under an all-
permissive license as long as you use an unmodified version.)

------
CountHackulus
Or you could use C++ and RAII. It's an excellent fit for the problem that's
been presented.

Though I do understand, there are some very special projects that can't use
C++, and must use C. I'd say in most cases, the extra machinery you get with
C++ outweighs the small performance penalty you get by going with straight C.

~~~
copper
FWIW, with C++, the advantages of linking to tcmalloc make it the default for
any code that I write that isn't intended to run as a daemon -- and even
there, unless I hit tcmalloc's limit of not releasing memory back, it's still
a viable choice.

~~~
ballard
Google perftools are bloody awesomesauce.

------
malkia
How does it deal with cycles, or leaning ownership?

~~~
chmike
This is exactly the qestion I was asking myself. What if the child block needs
to be deallocated before the parent block ? What if block dependency is
changed ?

These functions are to be used with particular conditions which aren't clearly
specified. When the conditions are met then it is a clear benefit to use them.
But these add complexity and new pitfalls to inexperienced programmers.

------
dhruvbird
On the whole, I think talloc() is targetting a very specific use-case and if
you have that it would be great to use (just like obstack).

------
zbowling
why I'm glad to be writing objective-c and getting free reference counting
built it :-)

~~~
hardy263
Except apple advises you not to use reference counting when trying to debug.
So when reference counting doesn't work, you still have to go back and
manually count the references yourself...

~~~
ryannielsen
Apple doesn't advise developers to avoid ref counting, it advises them to
avoid using -retainCount. -retainCount is a weak debugging tool, at best. It
doesn't take into account autorelease pools, to start, and any framework-
specific memory management is ignored. Its use is discouraged for good reason.

For better tools, check out Instruments, MallocDebug or leaks. Or read Apple's
Memory Usage Performance Guidelines, specifically the "Finding Memory Leaks"
section. [1] Apple's Technical Note TN2124 "Mac OS X Debugging Magic" even
has, in the Cocoa section, an example of how -retainCount can be misleading
and confusing. [2]

Nowhere does Apple advise Cocoa developers to avoid ref-counting. Cocoa's ref-
counting/GC'd memory management system is considered a huge advantage for the
platform.

[1]
[http://developer.apple.com/library/mac/#documentation/Perfor...](http://developer.apple.com/library/mac/#documentation/Performance/Conceptual/ManagingMemory/Articles/FindingLeaks.html)
[2]
[http://developer.apple.com/library/mac/#technotes/tn2004/tn2...](http://developer.apple.com/library/mac/#technotes/tn2004/tn2124.html)

------
Eliezer
I look forward to seeing the rest of Common LISP implemented in C too.

~~~
kragen
Eliezer, did somebody break into your account to post that comment? Since when
does Common Lisp have any kind of manual deallocation, let alone manual
deallocation of hierarchical memory pools?

