Simple Memory Allocator Challenge

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Michael B Allen

    Simple Memory Allocator Challenge

    Hi,

    I've tried to write the *simplest* memory allocator possible. I think it
    would be useful in many cases such as allocating memory on stack as a
    poor man's garbage collection perhaps.

    I was hoping the clc crowd had some ideas for making it even simpler!

    In-lined below the full program (132 lines). It's a circular singular
    linked list of "cells" inspired by the one in Plauger's text
    "The Standard C Library".

    I look forward to hearing your ideas,

    Mike

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>

    #define C2P(c) ((char *)(c) + 4)
    #define P2C(p) (struct cell *)((char *)(p) - 4)
    #define ISADJ(c1,c2) ((struct cell *)(C2P(c1) + (c1)->size) == (struct cell *)(c2))

    struct cell {
    size_t size;
    struct cell *next;
    };
    struct simple {
    struct cell *tail;
    };

    void
    simple_init(str uct simple *s, void *mem, size_t size)
    {
    s->tail = mem;
    s->tail->size = size - 4;
    s->tail->next = s->tail;
    }
    void *
    simple_alloc(st ruct simple *s, size_t size, int zero)
    {
    struct cell *c1, *c2, *c3;
    zero = 0;

    if (size < 4) {
    size = 4; /* must accomodate next pointer */
    }

    c1 = s->tail;
    while (c1->next->size < size) {
    if (c1->next == s->tail) {
    errno = ENOMEM;
    return NULL;
    }
    c1 = c1->next;
    }
    c2 = c1->next;
    if (c2->size > (4 + size)) { /* split new cell */
    c3 = (struct cell *)(C2P(c2) + size);
    c3->size = c2->size - (size + 4);
    c3->next = c2->next;
    c2->size = size;
    c1->next = c3;
    } else { /* use the entire cell */
    c1->next = c2->next;
    if (c2 == s->tail) {
    s->tail = c1;
    }
    }

    return C2P(c2);
    }
    int
    simple_free(str uct simple *s, void *ptr)
    {
    struct cell *c1, *c2, *c3;
    int j1, j2;

    /* splice the cell back into the list */
    c1 = s->tail;
    c2 = P2C(ptr);

    if (c2 > c1) { /* append to end of list */
    if (ISADJ(c1,c2)) { /* join with last cell */
    c1->size += 4 + c2->size;
    return 0;
    }
    c2->next = c1->next;
    c1->next = c2;
    s->tail = c2;
    return 0;
    }

    while (c1->next < c2) { /* find insertion point */
    c1 = c1->next;
    }
    c3 = c1->next;

    j1 = ISADJ(c1,c2); /* c1 and c2 need to be joined */
    j2 = ISADJ(c2,c3); /* c2 and c3 need to be joined */

    if (j1) {
    if (j2) { /* splice all three cells together */
    c1->next = c3->next;
    c1->size += 4 + c3->size;
    }
    c1->size += 4 + c2->size;
    } else {
    c1->next = c2;
    if (j2) {
    c2->next = c3->next;
    c2->size += 4 + c3->size;
    } else {
    c2->next = c3;
    }
    }

    return 0;
    }

    int
    main(void)
    {
    char mem[0xFFFFF];
    struct simple s;
    void *ptr;
    int i;
    size_t size;

    errno = 0;

    simple_init(&s, mem, 0xFFFFF);

    for (i = 0; i < 10000; i++) {
    if (i % 2) {
    simple_free(&s, ptr);
    }
    size = (3 << (i & 3)) + (i & 0xA8); /* generate quantized sizes */
    printf("size=%d \n", size);
    if ((ptr = simple_alloc(&s , size, 0)) == NULL) {
    perror("simple_ alloc");
    break;
    }
    }

    return EXIT_SUCCESS;
    }
  • Eric Sosman

    #2
    Re: Simple Memory Allocator Challenge

    Michael B Allen wrote:[color=blue]
    >
    > Hi,
    >
    > I've tried to write the *simplest* memory allocator possible. I think it
    > would be useful in many cases such as allocating memory on stack as a
    > poor man's garbage collection perhaps.
    >
    > I was hoping the clc crowd had some ideas for making it even simpler!
    >
    > In-lined below the full program (132 lines). It's a circular singular
    > linked list of "cells" inspired by the one in Plauger's text
    > "The Standard C Library".
    >
    > I look forward to hearing your ideas,
    > [code snipped][/color]

    Idea #1: Get rid of the magic number `4' lest you come
    to grief on a system whose pointers are not four bytes wide.

    Idea #2: Think about data alignment, lest you come to
    grief on systems that need it.

    Idea #3: Reconsider that `ENOMEM' error code, lest you
    come to grief on systems that don't define it.

    Idea #4: Re-examine the use of the argument `zero' in
    the simple_alloc() function, lest people speculate about
    what you've been smoking and demand a toke or two.

    Idea #5: Speculate on possible uses for the value
    returned by simple_free().

    I might come up with more Ideas, but I'm already weary
    of this code -- and I haven't even *begun* to examine it
    for correctness!

    --
    Eric.Sosman@sun .com

    Comment

    • Michael B Allen

      #3
      Re: Simple Memory Allocator Challenge

      On Wed, 22 Oct 2003 17:00:02 -0400, Eric Sosman wrote:
      [color=blue][color=green]
      >> I've tried to write the *simplest* memory allocator possible. I think
      >> it would be useful in many cases such as allocating memory on stack as
      >> a poor man's garbage collection perhaps.[/color][/color]
      <snip>[color=blue]
      >
      > Idea #1: Get rid of the magic number `4' lest you come
      > to grief on a system whose pointers are not four bytes wide.
      >
      > Idea #2: Think about data alignment, lest you come to
      > grief on systems that need it.[/color]

      1 and 2 done. I meant to fix this after I completed the algorithm. I just
      forgot.
      [color=blue]
      > Idea #3: Reconsider that `ENOMEM' error code, lest you
      > come to grief on systems that don't define it.[/color]

      Not sure what to do about this.
      [color=blue]
      > Idea #4: Re-examine the use of the argument `zero' in
      > the simple_alloc() function, lest people speculate about what you've
      > been smoking and demand a toke or two.
      >
      > Idea #5: Speculate on possible uses for the value
      > returned by simple_free().[/color]

      4 and 5 done (removed). I started out working on a generic "allocator"
      interface and got side-tracked. However, if by chance you realized the
      zero parameter was meant to indicate that the memory should be set to
      zero (like calloc) and still believe such an interface would be offesive,
      then please explain why.

      Update in-lined below. Interesting it is indeed simpler as it is now only
      131 lines! Acutually it's 103 if you don't count the driver program.

      Mike

      #include <stdlib.h>
      #include <stdio.h>
      #include <errno.h>

      #define ALIGNMASK 1U
      #define ALIGN(s) (((s) + ALIGNMASK) & ~ALIGNMASK)
      #define POFF ALIGN(sizeof(si ze_t))
      #define MINSIZ ALIGN(sizeof(st ruct cell *))
      #define C2P(c) ((char *)(c) + POFF)
      #define P2C(p) ((struct cell *)((char *)(p) - POFF))
      #define ISADJ(c1,c2) ((struct cell *)(C2P(c1) + (c1)->size) == (struct cell *)(c2))

      struct cell {
      size_t size;
      struct cell *next;
      };
      struct simple {
      struct cell *tail;
      };

      void
      simple_init(str uct simple *s, void *mem, size_t size)
      {
      s->tail = mem;
      s->tail->size = size - POFF;
      s->tail->next = s->tail;
      }
      void *
      simple_alloc(st ruct simple *s, size_t size)
      {
      struct cell *c1, *c2, *c3;

      size = size < MINSIZ ? MINSIZ : ALIGN(size);

      c1 = s->tail;
      while (c1->next->size < size) {
      if (c1->next == s->tail) {
      errno = ENOMEM;
      return NULL;
      }
      c1 = c1->next;
      }
      c2 = c1->next;
      if (c2->size > (POFF + size)) { /* split new cell */
      c3 = (struct cell *)(C2P(c2) + size);
      c3->size = c2->size - (size + POFF);
      c3->next = c2->next;
      c2->size = size;
      c1->next = c3;
      } else { /* use the entire cell */
      c1->next = c2->next;
      if (c2 == s->tail) {
      s->tail = c1;
      }
      }

      return C2P(c2);
      }
      void
      simple_free(str uct simple *s, void *ptr)
      {
      struct cell *c1, *c2, *c3;
      int j1, j2;

      /* splice the cell back into the list */
      c1 = s->tail;
      c2 = P2C(ptr);

      if (c2 > c1) { /* append to end of list */
      if (ISADJ(c1,c2)) { /* join with last cell */
      c1->size += POFF + c2->size;
      return;
      }
      c2->next = c1->next;
      c1->next = c2;
      s->tail = c2;
      return;
      }

      while (c1->next < c2) { /* find insertion point */
      c1 = c1->next;
      }
      c3 = c1->next;

      j1 = ISADJ(c1,c2); /* c1 and c2 need to be joined */
      j2 = ISADJ(c2,c3); /* c2 and c3 need to be joined */

      if (j1) {
      if (j2) { /* splice all three cells together */
      c1->next = c3->next;
      c1->size += POFF + c3->size;
      }
      c1->size += POFF + c2->size;
      } else {
      c1->next = c2;
      if (j2) {
      c2->next = c3->next;
      c2->size += POFF + c3->size;
      } else {
      c2->next = c3;
      }
      }
      }

      int
      main(void)
      {
      char mem[0xFFFFF];
      struct simple s;
      void *ptr;
      int i;
      size_t size;

      errno = 0;

      simple_init(&s, mem, 0xFFFFF);

      for (i = 0; i < 10000; i++) {
      if (i % 2) {
      simple_free(&s, ptr);
      }
      size = (3 << (i & 3)) + (i & 0xA8); /* generate quantized sizes */
      printf("size=%d \n", size);
      if ((ptr = simple_alloc(&s , size)) == NULL) {
      perror("simple_ alloc");
      break;
      }
      }

      return EXIT_SUCCESS;
      }

      Comment

      • Eric Sosman

        #4
        Re: Simple Memory Allocator Challenge

        Michael B Allen wrote:[color=blue]
        >
        > On Wed, 22 Oct 2003 17:00:02 -0400, Eric Sosman wrote:
        > [...][color=green]
        > > Idea #3: Reconsider that `ENOMEM' error code, lest you
        > > come to grief on systems that don't define it.[/color]
        >
        > Not sure what to do about this.[/color]

        One possibility would be to test whether the C implementation
        provides an `ENOMEM' error code:

        #ifdef ENOMEM
        errno = ENOMEM;
        #endif

        Of course, even there you're guessing about what the code
        actually signifies. strerror(ENOMEM ) might conceivably
        return "Error: meditational syllable surrounded by spaces"
        or some such gibberish. Still, it seems pretty likely that
        an error code named `ENOMEM' has something to do with a
        shortage of memory.

        Another possibility would be to forego setting `errno'
        altogether. The ordinary malloc() and friends are not
        required to do so, and sometimes don't: On at least one
        implementation I use, strerror(errno) after a failed malloc()
        returns "no error". Is it necessary for "the *simplest*
        memory allocator possible" to do more?
        [color=blue][color=green]
        > > Idea #4: Re-examine the use of the argument `zero' in
        > > the simple_alloc() function, lest people speculate about what you've
        > > been smoking and demand a toke or two.[/color]
        >
        > 4 and 5 done (removed). I started out working on a generic "allocator"
        > interface and got side-tracked. However, if by chance you realized the
        > zero parameter was meant to indicate that the memory should be set to
        > zero (like calloc) and still believe such an interface would be offesive,
        > then please explain why.[/color]

        That was my guess as to the intent of the `zero' argument,
        but in fact the code made no effective use of it at all.

        A zeroing allocator doesn't seem offensive to me, but neither
        does it seem particularly useful. Yes, I do occasionally find a
        reason to use calloc() -- but only occasionally, and I would
        shed no tears if it suddenly vanished. A simple memset() after
        an ordinary malloc() would do just as well, even if it might be
        very slightly less efficient.

        --
        Eric.Sosman@sun .com

        Comment

        • Michael B Allen

          #5
          Re: Simple Memory Allocator Challenge

          On Thu, 23 Oct 2003 17:56:20 -0400, Eric Sosman wrote:
          [color=blue][color=green][color=darkred]
          >> > Idea #4: Re-examine the use of the argument `zero' in
          >> > the simple_alloc() function, lest people speculate about what you've
          >> > been smoking and demand a toke or two.[/color]
          >>
          >> 4 and 5 done (removed). I started out working on a generic "allocator"
          >> interface and got side-tracked. However, if by chance you realized the
          >> zero parameter was meant to indicate that the memory should be set to
          >> zero (like calloc) and still believe such an interface would be
          >> offesive, then please explain why.[/color]
          >
          > That was my guess as to the intent of the `zero' argument,
          > but in fact the code made no effective use of it at all.
          >
          > A zeroing allocator doesn't seem offensive to me, but neither
          > does it seem particularly useful. Yes, I do occasionally find a reason
          > to use calloc() -- but only occasionally, and I would shed no tears if
          > it suddenly vanished. A simple memset() after an ordinary malloc()
          > would do just as well, even if it might be very slightly less efficient.[/color]

          Actually I tested the difference between calloc and malloc w/ memset on
          Linux w/ glibc 2.2 and found that an increase in size had little or no
          effect on it's performance whereas malloc w/ memset took consideribly
          longer. With a large enough allocation (I beleive it was 100MB) calloc
          was virtually instantanious when malloc w/ memset locked up my machine for 10
          minutes before hitting the power button. Obviously there's some VM tricks
          being used there.

          Mike

          Comment

          • Christian Bau

            #6
            Re: Simple Memory Allocator Challenge

            In article <pan.2003.10.24 .03.17.23.38367 2.1434@ioplex.c om>,
            Michael B Allen <mba2000@ioplex .com> wrote:
            [color=blue]
            > On Thu, 23 Oct 2003 17:56:20 -0400, Eric Sosman wrote:
            >[color=green][color=darkred]
            > >> > Idea #4: Re-examine the use of the argument `zero' in
            > >> > the simple_alloc() function, lest people speculate about what you've
            > >> > been smoking and demand a toke or two.
            > >>
            > >> 4 and 5 done (removed). I started out working on a generic "allocator"
            > >> interface and got side-tracked. However, if by chance you realized the
            > >> zero parameter was meant to indicate that the memory should be set to
            > >> zero (like calloc) and still believe such an interface would be
            > >> offesive, then please explain why.[/color]
            > >
            > > That was my guess as to the intent of the `zero' argument,
            > > but in fact the code made no effective use of it at all.
            > >
            > > A zeroing allocator doesn't seem offensive to me, but neither
            > > does it seem particularly useful. Yes, I do occasionally find a reason
            > > to use calloc() -- but only occasionally, and I would shed no tears if
            > > it suddenly vanished. A simple memset() after an ordinary malloc()
            > > would do just as well, even if it might be very slightly less efficient.[/color]
            >
            > Actually I tested the difference between calloc and malloc w/ memset on
            > Linux w/ glibc 2.2 and found that an increase in size had little or no
            > effect on it's performance whereas malloc w/ memset took consideribly
            > longer. With a large enough allocation (I beleive it was 100MB) calloc
            > was virtually instantanious when malloc w/ memset locked up my machine for 10
            > minutes before hitting the power button. Obviously there's some VM tricks
            > being used there.[/color]

            linux seems to have this horrible property that it lets you allocate
            memory that turns out to be unusable. Seems to have happened here,
            although if 100 MB is a problem then this is most likely a relatively
            old machine.

            If you call free (calloc (...)) then this probably happens quite
            quickly, but is pointless anyway. Try calloc'ing 100 MB, then _use_ that
            memory and see how long it takes.

            Comment

            • Michael B Allen

              #7
              Re: Simple Memory Allocator Challenge

              On Fri, 24 Oct 2003 03:27:38 -0400, Christian Bau wrote:
              [color=blue][color=green][color=darkred]
              >> > tears if it suddenly vanished. A simple memset() after an ordinary
              >> > malloc() would do just as well, even if it might be very slightly
              >> > less efficient.[/color]
              >>
              >> Actually I tested the difference between calloc and malloc w/ memset on
              >> Linux w/ glibc 2.2 and found that an increase in size had little or no
              >> effect on it's performance whereas malloc w/ memset took consideribly
              >> longer. With a large enough allocation (I beleive it was 100MB) calloc
              >> was virtually instantanious when malloc w/ memset locked up my machine
              >> for 10 minutes before hitting the power button. Obviously there's some
              >> VM tricks being used there.[/color]
              >
              > linux seems to have this horrible property that it lets you allocate
              > memory that turns out to be unusable. Seems to have happened here,
              > although if 100 MB is a problem then this is most likely a relatively
              > old machine.[/color]

              Actually I think I was mistaken. When I locked up my machine it was
              because I specified a rediculously huge value by accedent.

              Although Linux does stuggle a little when stressed for memory. 2.2 would
              thrash. 2.4 has an elevator problem. It will be interesting to see how
              2.6 performs *when it reaches the stability of 2.2/2.4*.
              [color=blue]
              > If you call free (calloc (...)) then this probably happens quite
              > quickly, but is pointless anyway. Try calloc'ing 100 MB, then _use_ that
              > memory and see how long it takes.[/color]

              Ok, let's do this a little better. On my 1.8 GHz Pentium IV mobile
              w/ 256MB RAM the below program generates 250000 for malloc w/ memset
              and 230000 for calloc. So I guess calloc isn't that useful.

              #include <stdlib.h>
              #include <string.h>
              #include <stdio.h>
              #include <time.h>

              /* 100MB */
              #define N 10000

              int
              main(void)
              {
              char *buf;
              int i;
              clock_t t0 = clock();

              for (i = 0; i < N; i++) {

              buf = malloc(N);
              memset(buf, 0, N);

              if ((i % (N / 100)) == 0) {
              buf[i] = 'x';
              }
              }

              printf("t=%ld\n ", clock() - t0);

              return EXIT_SUCCESS;
              }
              int
              main0(void)
              {
              char *buf;
              int i;
              clock_t t0 = clock();

              for (i = 0; i < N; i++) {

              buf = calloc(1, N);

              if ((i % (N / 100)) == 0) {
              buf[i] = 'x';
              }
              }

              printf("t=%ld\n ", clock() - t0);

              return EXIT_SUCCESS;
              }

              Comment

              • Daniel Haude

                #8
                Re: Simple Memory Allocator Challenge

                On Fri, 24 Oct 2003 08:27:38 +0100,
                Christian Bau <christian.bau@ cbau.freeserve. co.uk> wrote
                in Msg. <christian.ba u-5E1D81.08273824 102003@slb-newsm1.svr.pol. co.uk>
                [color=blue]
                > linux seems to have this horrible property that it lets you allocate
                > memory that turns out to be unusable.[/color]

                It has been pointed out in this group before that this is a property most
                implementations share, and for good reasons too. I don't want to embark on
                a memory management discussion here because it's off-topic and I know
                virtually nothing about the mechanics of MM, I just wanted to mention that
                this is not a Linux-specific thing. I also forgot what the good reasons
                were.

                --Daniel

                --
                "With me is nothing wrong! And with you?" (from r.a.m.p)

                Comment

                • xarax

                  #9
                  Re: Simple Memory Allocator Challenge

                  "Michael B Allen" <mba2000@ioplex .com> wrote in message
                  news:pan.2003.1 0.24.04.29.37.2 3311.1434@iople x.com...[color=blue]
                  > On Fri, 24 Oct 2003 03:27:38 -0400, Christian Bau wrote:
                  >[color=green][color=darkred]
                  > >> > tears if it suddenly vanished. A simple memset() after an ordinary
                  > >> > malloc() would do just as well, even if it might be very slightly
                  > >> > less efficient.
                  > >>
                  > >> Actually I tested the difference between calloc and malloc w/ memset on
                  > >> Linux w/ glibc 2.2 and found that an increase in size had little or no
                  > >> effect on it's performance whereas malloc w/ memset took consideribly
                  > >> longer. With a large enough allocation (I beleive it was 100MB) calloc
                  > >> was virtually instantanious when malloc w/ memset locked up my machine
                  > >> for 10 minutes before hitting the power button. Obviously there's some
                  > >> VM tricks being used there.[/color][/color][/color]
                  /snip/

                  calloc() has an advantage over malloc()+memset (). On better-designed
                  implementations , the operating system doesn't actually assign
                  backing-store to virtual memory until the first reference. The
                  granularity is usually the native page size. For example, assume a
                  page size of 4096 bytes. If you allocate 100MB using calloc(), that's
                  25600 pages. calloc() and malloc() will return the pointer. The
                  performance penalty happens when memset() is used to touch all 25600
                  pages to set them to zero. In the calloc() case, explicit zeroing
                  is not needed, because the operating system will set each page to
                  zeroes on first reference. You can save time by not calling memset(),
                  which would touch each page, causing a page fault and subsequent
                  OS activity to set-up paging control blocks.

                  Some implementations may specify these semantics for malloc(), but
                  that makes the program non-portable. Relying on malloc() to yield
                  "first reference" page-aligned memory is a bad idea. Only calloc()
                  can guarantee that every byte will be zero by the time your program
                  needs to reference the byte.

                  If you run a benchmark with calloc()+memset () versus malloc()+memset (),
                  I would expect the timings to be very nearly identical with most of the
                  time being spent in memset(), and calloc() versus malloc() identical.
                  This, of course, is for huge page-aligned allocations of new virtual
                  memory (not memory recycled through free()).

                  Some implementations of free() may also take advantage of native
                  operating system API for resetting virtual memory to its "first
                  reference" state, so that calloc() knows that the recycled memory
                  that it is allocating will already be zeroes.

                  Bottom line: Use calloc() for zeroed memory, rather than malloc()+memset ().
                  Let calloc() decide how best to zero the memory. It will likely do a
                  better job than you can, and you have more important problems to solve.

                  --
                  ----------------------------------------------
                  Jeffrey D. Smith
                  Farsight Systems Corporation
                  24 BURLINGTON DRIVE
                  LONGMONT, CO 80501-6906
                  303-774-9381

                  z/Debug debugs your Systems/C programs running on IBM z/OS!


                  Comment

                  • Eric Sosman

                    #10
                    Re: Simple Memory Allocator Challenge

                    xarax wrote:[color=blue]
                    >
                    > calloc() has an advantage over malloc()+memset (). On better-designed
                    > implementations , the operating system doesn't actually assign
                    > backing-store to virtual memory until the first reference. [...]
                    > [Remainder of the usual argument snipped.][/color]

                    First, it is debatable whether such an implementation
                    is conforming (c.f. Question 7.14 in the FAQ). There's no
                    point in re-opening the debate for the umpteenth time, but
                    let's just note that reasonable people differ about calling
                    such implementations "better-designed."

                    Second, the "automatica lly zeroed by the O/S" advantage
                    obtains only on the first time the memory is allocated. If
                    it is then freed and re-calloc()ed the advantage vanishes.

                    Third, the example [snipped for brevity; sorry] of a
                    100MB calloc()ation is valid. But who needs a hundred meg
                    of zeroes? Remember, those zeroes are not necessarily valid
                    as any data type at all other than `unsigned char'. I'd
                    suggest that if your program needs 100MB of pre-zeroed
                    `unsigned char', the design is probably improvable.

                    Finally, the strategy of postponing the actual allocation
                    until the first reference doesn't so much eliminate cost as
                    shift it. If you eventually reference the memory you eventually
                    incur the cost of getting a page from somewhere, assigning or
                    reserving backing store, and so on. If you're not going to use
                    the memory, calloc() may well be faster than malloc() plus
                    memset(). But if you're not going to use the memory, the code
                    snippet

                    ;

                    is faster than either.

                    --
                    Eric.Sosman@sun .com

                    Comment

                    • xarax

                      #11
                      Re: Simple Memory Allocator Challenge

                      "Eric Sosman" <Eric.Sosman@su n.com> wrote in message
                      news:3F9950E1.5 BE71F43@sun.com ...[color=blue]
                      > xarax wrote:[color=green]
                      > >
                      > > calloc() has an advantage over malloc()+memset (). On better-designed
                      > > implementations , the operating system doesn't actually assign
                      > > backing-store to virtual memory until the first reference. [...]
                      > > [Remainder of the usual argument snipped.][/color]
                      >
                      > First, it is debatable whether such an implementation
                      > is conforming (c.f. Question 7.14 in the FAQ). There's no
                      > point in re-opening the debate for the umpteenth time, but
                      > let's just note that reasonable people differ about calling
                      > such implementations "better-designed."[/color]

                      I was describing an implementation detail, rather than
                      anything that could be considered conforming. My later
                      remarks were leaning towards "do it the right way rather
                      than what you're implementation prefers" for portability.
                      If you need zeroed memory, then calloc() is always better
                      (or at least it's never worse) than malloc()+memset ().
                      [color=blue]
                      > Second, the "automatica lly zeroed by the O/S" advantage
                      > obtains only on the first time the memory is allocated. If
                      > it is then freed and re-calloc()ed the advantage vanishes.[/color]

                      I also included remarks about free() that can recognize
                      when it can ask the OS to mark the memory "first reference"
                      again, thus discarding its contents from backing storage and
                      page I/O slots. A subsequent calloc() or malloc() that reuses
                      such storage will again see "first reference" behavior.
                      [color=blue]
                      > Third, the example [snipped for brevity; sorry] of a
                      > 100MB calloc()ation is valid. But who needs a hundred meg
                      > of zeroes? Remember, those zeroes are not necessarily valid
                      > as any data type at all other than `unsigned char'. I'd
                      > suggest that if your program needs 100MB of pre-zeroed
                      > `unsigned char', the design is probably improvable.[/color]

                      I only mention 100MB, because it was mentioned elsewhere. Such
                      allocations can and do happen in large scale applications (like
                      data base stuff). The mainframe world can see gigabyte allocations
                      quite often, so understanding the gotcha's of memory management
                      are crucial.
                      [color=blue]
                      > Finally, the strategy of postponing the actual allocation
                      > until the first reference doesn't so much eliminate cost as
                      > shift it. If you eventually reference the memory you eventually
                      > incur the cost of getting a page from somewhere, assigning or
                      > reserving backing store, and so on.[/color]

                      In rare circumstances, like when you know for sure that all of
                      the huge allocation will be touched very soon, there's probably
                      not much difference. However, smart operating systems can
                      recognize reference patterns and reasonably predict future
                      references and "pre-fault" the anticipated pages. For general
                      rule of thumb, it's usually a good idea to not peek/poke memory
                      until it's necessary.
                      [color=blue]
                      > If you're not going to use
                      > the memory, calloc() may well be faster than malloc() plus
                      > memset(). But if you're not going to use the memory, the code
                      > snippet
                      >
                      > ;
                      >
                      > is faster than either.[/color]

                      LOL.

                      It's more commonly a case of not knowing how much of it you
                      need, so you swag a gob of memory to reduce fragmentation
                      and off-load the burden to the OS. I am not saying this is
                      good design, because each case is unique. Sometimes, simple
                      incremental allocations with linking are better. Other times,
                      a swag for a huge contiguous chunk may be better. There are
                      times when you cannot predict exactly how much memory you
                      need, but can calculate a reasonable guess.

                      My bottom line still holds. If you happen to have a good
                      implementation of calloc()+free() , then you are just that
                      much better off compared to having a naive implementation.


                      --
                      ----------------------------------------------
                      Jeffrey D. Smith
                      Farsight Systems Corporation
                      24 BURLINGTON DRIVE
                      LONGMONT, CO 80501-6906

                      z/Debug debugs your Systems/C programs running on IBM z/OS!


                      Comment

                      • James Antill

                        #12
                        Re: Simple Memory Allocator Challenge

                        On Fri, 24 Oct 2003 08:27:38 +0100, Christian Bau wrote:
                        [color=blue]
                        > linux seems to have this horrible property that it lets you allocate
                        > memory that turns out to be unusable. Seems to have happened here,[/color]

                        s/horrible property/horrible default property, IMO,/

                        If you want to turn memory overcommit off, that's fine. Just do...

                        echo 3 > /proc/sys/vm/overcommit_memo ry

                        ....or use sysctl.conf etc.
                        [color=blue]
                        > If you call free (calloc (...)) then this probably happens quite
                        > quickly, but is pointless anyway. Try calloc'ing 100 MB, then _use_ that
                        > memory and see how long it takes.[/color]

                        Although even with overcommit checks, things are not paged into the
                        application until needed (it's just guaranteed that those pages can be
                        paged in).

                        --
                        James Antill -- james@and.org
                        Need an efficient and powerful string library for C?


                        Comment

                        • Eric Sosman

                          #13
                          Re: Simple Memory Allocator Challenge

                          xarax wrote:[color=blue]
                          >
                          > "Eric Sosman" <Eric.Sosman@su n.com> wrote...
                          >[color=green]
                          > > Third, the example [snipped for brevity; sorry] of a
                          > > 100MB calloc()ation is valid. But who needs a hundred meg
                          > > of zeroes? Remember, those zeroes are not necessarily valid
                          > > as any data type at all other than `unsigned char'. I'd
                          > > suggest that if your program needs 100MB of pre-zeroed
                          > > `unsigned char', the design is probably improvable.[/color]
                          >
                          > I only mention 100MB, because it was mentioned elsewhere. Such
                          > allocations can and do happen in large scale applications (like
                          > data base stuff). The mainframe world can see gigabyte allocations
                          > quite often, so understanding the gotcha's of memory management
                          > are crucial.[/color]

                          I do not deny the need for large allocations, but the
                          need for large zero-filled allocations. A hash table is
                          the only example that comes to (my) mind where zero-filling
                          is potentially useful, but even there the benefit is dubious:

                          - As soon as you start populating the table you'll be
                          touching most of the slots, and in an essentially
                          "random" pattern (assuming your hash function is a
                          good one). Most of the page-faulting saved by a
                          zero-on-reference implementation is going to occur
                          almost immediately afterwards anyhow.

                          - It's still a non-portable technique, because the hash
                          table elements are probably something other than plain
                          `unsigned char'. (Of course, some platform-dependency
                          may be acceptable; after all, 100MB allocations are
                          already ntoo big to be portable!)

                          In any event, the OP's "*simplest* memory allocator possible"
                          is unable to take advantage of any such trickery. In fact, in
                          his intended use the "arena" from which the memory is taken will
                          be an `auto' array whose prior content is unreliable! So whatever
                          our opinions on the merits of calloc() -- and I guess we'll have
                          to agree to disagree -- there seems to be not much reason other
                          than "work-alike-ness" for him to offer a zeroing allocator.

                          --
                          Eric.Sosman@sun .com

                          Comment

                          • Michael B Allen

                            #14
                            Re: Simple Memory Allocator Challenge

                            On Thu, 23 Oct 2003 04:23:17 -0400, Michael B Allen wrote:
                            <snip all code>

                            Natrually there were a few bugs in this code. Feel free to contact me if
                            you acutally care to see the update. Or google libmba which is where it
                            will ultimately reside.

                            Mike

                            Comment

                            Working...