. .

C: Speicherallokation: malloc() vs. talloc()


Als ich vor Kurzem auf gentoo Linux ein `emerge -quDN world‘ machte, habe ich ausnahmsweise mal zugeschaut, was da gerade alles aktualisiert wird.
Da fiel mir ein Paket namens `sys-libs/talloc‘ auf – und fragte mich, ob das tatsächlich etwas mit Speicherallocation zu tun haben könnte.

Kurz gegoogelt bin ich auf einen Artikel von Stephen Gallagher aufmerksam geworden: Why you should use talloc for your next project.

talloc ist eine Speicherimplementierung vom Samba-Projekt. talloc bietet hier die Möglichkeit, reservierten Speicher in einem zusammenhängenden Bereich zu erhalten. Welche Vorteile daraus gezogen werden können, ist an Hand dieses kleinen Beispiels ersichtlich:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#include <talloc.h>

struct user_ {
    uid_t uid;
    char *username;
    size_t num_groups;
    char **groups;
};

double
get_duration_in_ms(struct timeval *started) {
    long seconds, useconds;
    double duration_msecs;
    struct timeval ended;

    gettimeofday(&ended, NULL);

    seconds = ended.tv_sec - started->tv_sec;
    useconds = ended.tv_usec - started->tv_usec;

    duration_msecs = ((seconds) * 1000 +
            useconds/1000.0) + 0.5;

    return duration_msecs;
}

/* ************************************************* */

int main() {
    struct user_ *user = talloc(NULL, struct user_);
    int i;
    double duration;
    struct timeval started;
    gettimeofday(&started, NULL);

    user->uid = 1000;
    user->num_groups = 320000;

    user->username = talloc_strdup(user, "Test user");
    user->groups = talloc_array(user, char*,
            user->num_groups);

    for (i = 0; i < user->num_groups; i++) {
        user->groups[i] = talloc_asprintf(user->groups,
                "Test group %d", i); 
    }   

    /* now free allocated memory */
    talloc_free(user);

    duration = get_duration_in_ms(&started);
    printf("duration talloc: `%f' ms\n", duration);

    return EXIT_SUCCESS;
}

Hier stellt man fest, dass nur ein einziges free() in Form von talloc_free() angegeben wurde.
Normalerweise würde der oben gelistete Code so aussehen, um den verwendeten Speicher korrekt und vollständig wieder freizugeben:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>

/* struct user_ and get_duration_in_ms() see above   */

/* ************************************************* */

int main() {
    struct user_ *user = malloc(sizeof(struct user_));
    int i;
    double duration;
    struct timeval started;
    gettimeofday(&started, NULL);

    user->uid = 1000;
    user->num_groups = 320000;

    user->username = strdup("Test user");
    user->groups = (char**) malloc(user->num_groups *
            sizeof(char *));

    for (i = 0; i < user->num_groups; i++) {
        asprintf(&user->groups[i],
                "Test group %d", i); 
    }   

    /* now free allocated memory */
    for (i = 0; i < user->num_groups; i++) {
        free(user->groups[i]);
    }   

    free(user->groups);
    free(user->username);
    free(user);

    duration = get_duration_in_ms(&started);
    printf("duration malloc: `%f' ms\n", duration);

    return EXIT_SUCCESS;
}

Würde man im unteren Beispiel wie im obigen Beispiel (Zeile 55) nur `user‘ befreien, würden hier konkret 320002 Bereiche nicht mehr freigeben sein (wenn das Programm nicht enden würde).
Valgrind gibt darüber Aufschluss, wieviel Speicher zurückbliebe:


==3204== definitely lost: 2,560,010 bytes in 2 blocks
==3204== indirectly lost: 5,648,874 bytes in 319,999 blocks
==3204== possibly lost: 16 bytes in 1 blocks

talloc_free() allerdings übernimmt durch den zusammenhängenden Aufbau eben schon die Freigabe des verwendeten Speichers von `user‘, inklusive aller Elemente von user->groups.

Das hat nun den Vorteil, dass man weniger free()-Aufrufe selbst tätigen muss.

Der Luxus, der einem mit talloc() geboten wird, hat aber natürlich auch seine Nachteile – die der schlechteren Performance.

Spielt die Performance in einem Projekt also nicht die Hauptrolle, ist talloc() sicherlich eine schöne Alternative zur normalen Speicherverwaltung der glibc (ptmalloc2).


Eine Antwort auf C: Speicherallokation: malloc() vs. talloc()

  1. I’m a little surprised by that measurement. Usually we’ve found talloc to be about 4% slower than malloc. The more-than-double time you see there is surprising, to say the least.

    talloc can also be made much higher performance with the use of talloc_pool. This allows you to allocate a chunk of memory once and then continue to reuse it until it’s full (at which time, talloc calls will automatically start allocating outside the pool)

    There’s a fantastic guide to talloc written by a colleague of mine here:
    http://talloc.samba.org/talloc/doc/html/libtalloc__tutorial.html

    He covers the talloc pools in Chapter 5:
    http://talloc.samba.org/talloc/doc/html/libtalloc__tutorial.html

    Also, Chapter 7 (Best Practises) is a must-read if you are interested in talloc:
    http://talloc.samba.org/talloc/doc/html/libtalloc__bestpractices.html

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>