11
\$\begingroup\$

I recently have been very interested in custom allocators, so I decided to make the very basic (this should be faster than malloc) bump allocator. Here is my code in C:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/mman.h>
#include <unistd.h>

#define KB(size) ((size_t) size * 1024)
#define MB(size) (KB(size) * 1024)
#define GB(size) (MB(size) * 1024)
#define HEAP_SIZE GB(1)

typedef intptr_t word_t;
void* free_ptr = NULL;
void* start_ptr;
word_t end_ptr;

void init() {
    free_ptr = mmap(NULL, HEAP_SIZE, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (free_ptr == MAP_FAILED) {
        printf("unable to map memory\n");
        abort();
    }
    start_ptr = free_ptr;
    end_ptr = (word_t) start_ptr + HEAP_SIZE;
}

void* bump_alloc(size_t size) {
    void* new_ptr = free_ptr;
    free_ptr = (char*) free_ptr + size;
    return new_ptr;
}

void free_all_mem() {
    munmap(start_ptr, HEAP_SIZE);
}

int main() {
    init();
    int* x = (int*) bump_alloc(sizeof(int));
    assert(x != NULL);
    *x = 10000;
    printf("x: %d\n", *x);
    free_all_mem();
}

This is my first custom allocator so could I get some tips on optimization, etc.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ GB(10*1024) etc will overflow. Don't use function-like macros for these, use absolute numeric constants. \$\endgroup\$
    – Lundin
    Commented Jun 2, 2020 at 8:58
  • 3
    \$\begingroup\$ Don't cast malloc returns (applies to your malloc as well). \$\endgroup\$
    – DarkDust
    Commented Jun 2, 2020 at 10:59
  • 1
    \$\begingroup\$ @Lundin Are you concerned GB(10*1024) overflows 32-bit math? Overwise (size_t)10* 1024 * 1024 *1024 looks OK. \$\endgroup\$
    – chux
    Commented Jun 2, 2020 at 14:29
  • 1
    \$\begingroup\$ If you are concerned with efficiency, shouldn't bump allocators always allocate memory downwards instead of upwards? \$\endgroup\$
    – Glen Yates
    Commented Jun 2, 2020 at 19:47
  • \$\begingroup\$ @GlenYates -- Ah, yes-- I seem to have forgot that! Good catch! ty! \$\endgroup\$
    – xilpex
    Commented Jun 2, 2020 at 19:55

2 Answers 2

10
\$\begingroup\$

could I get some tips on optimization, etc.

Alignment loss

free_ptr = (char*) free_ptr + size; simply increases the next available allocation to so many bytes later. This differs from malloc() whose allocations meets all possible system alignment needs.

Either document that bump_alloc() does not provide aligned allocations or change code to do so.

Error messages

I'd expect the error message to go out stderr - yet your call.

// printf("unable to map memory\n");
fprintf(stderr, "Unable to map memory\n");

Missing include

intptr_t is define in <stdint.h>. Best to include that rather than rely of a hidden inclusion.

Good type math

The below avoids int overflow.

#define KB(size) ((size_t) size * 1024)

Better code would () each macro parameter.

#define KB(size) ((size_t) (size) * 1024)

Yet I'd recommend rather than type-casting, which may narrow the math, allow gentle widening. The below multiplication will occur with the wider of size_t and the type of size.

#define KB(size) ((size_t) 1024 * (size))

Unneeded cast, simplify

Casting not needed going from void * to an object pointer. Size to the de-referenced type. Easier to code right, review and maintain.

// int* x = (int*) bump_alloc(sizeof(int));
int* x = bump_alloc(sizeof *x);
\$\endgroup\$
7
\$\begingroup\$

Unit pedantry

Technically those are KiB and MiB since they're multiples of 1024 and not 1000.

Empty arguments

This was a difficult lesson for me to drill into my head, but in C empty arguments() and (void) arguments are not the same thing, particularly for function declarations. Technically for definitions they are, but I don't like relying on inconsistent rules and recommend that (void) be used uniformly - even though you only have definitions without declarations. (void) is safer and more explicit. () is closer to meaning "an unspecified number of arguments".

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Except that nobody (to within rounding error) uses KiB and MiB. :-) The definitions are clear visible; I don't think this is a big point of confusion. In fact, as Lundin suggested in the comments, it would be even clearer to eschew the use of the macros and just multiply inline. Agreed completely with your second point, although an empty parameter list isn't just "closer" to meaning that. It does mean exactly that! \$\endgroup\$
    – Cody Gray
    Commented Jun 2, 2020 at 20:10
  • 1
    \$\begingroup\$ @CodyGray if Jonathan Leffler uses it, everybody must use it. Are you better than him? No way. No possibility. \$\endgroup\$ Commented Jun 3, 2020 at 14:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.