2

I am building a simple particle system and want to use a single array buffer of structs to manage my particles. That said, I can't find a C function that allows me to malloc() and free() from an arbitrary buffer. Here is some pseudocode to show my intent:

Particle* particles = (Particle*) malloc( sizeof(Particle) * numParticles );
Particle* firstParticle = <buffer_alloc>( particles );
initialize_particle( firstParticle );
// ... Some more stuff
if (firstParticle->life < 0)
    <buffer_free>( firstParticle );

// @ program's end
free(particles);

Where <buffer_alloc> and <buffer_free> are functions that allocate and free memory chunks from arbitrary pointers (possibly with additional metadata such as buffer length, etc.). Do such functions exist and/or is there a better way to do this? Thank you!

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
Grimless
  • 1,268
  • 1
  • 15
  • 30
  • Could you add the definiton for (struct?) Particle? Does it contain pointers? – wildplasser Jun 24 '12 at 21:05
  • 1
    +1. As far as I know, C doesn't do what you want. C++ does it, using the *placement new* syntax, which is fun. However, nothing prevents you from allocating an array of Particles (whether on the stack or on the heap) and then doling out storage for one Particle at a time as needed. – thb Jun 24 '12 at 21:05
  • @thb: I don't think this is a placement-new scenario. The OP wants automatic memory management, but isolated to a defined memory region. – Oliver Charlesworth Jun 24 '12 at 21:11
  • @thb Yeah, it's the doling out part that I don't want to have to deal with. Since I can't guarantee that dead/unused particles are at a given position in the array without sorting them at every frame, I have to add in a lot of functionality just for particle management. I've been using a linked list up until now but sorting, insertion, and deletion are not friendly on processing nor understanding. – Grimless Jun 24 '12 at 21:16
  • If the number of particles is bounded, you could allocate them in one sweep (maybe even statically) and initialise them at startup time (this wil need loops and code, just like malloc() would) You don't have to care about particles becoming unused, you could always reinitialise the array in yet another sweep. Which particles are currently active has to do with the program logic, it does not mean they should be allocated / deallocated. ( you could add a flag to them, or use an extra pointer array or bitmap to track the active particles. – wildplasser Jun 24 '12 at 21:22

5 Answers5

5

Yeah, you’d have to write your own. It’s so simple it’s really silly, but its performance will scream in comparison to simply using malloc() and free() all the time....

static const int maxParticles = 1000;

static Particle particleBuf[maxParticles]; // global static array

static Particle* headParticle;

void initParticleAllocator()
{
    Particle* p = particleBuf;
    Particle* pEnd = &particleBuf[maxParticles-1];
    // create a linked list of unallocated Particles
    while (p!=pEnd)
    {
        *((Particle**)p) = p+1;
        ++p;
    }
    *((Particle**)p) = NULL; // terminate the end of the list
    headParticle = particleBuf; // point 'head' at the 1st unalloc'ed one
}

Particle* ParticleAlloc()
{
    // grab the next unalloc'ed Particle from the list
    Particle* ret = headParticle;
    if (ret)
        headParticle = *(Particle**)ret;
    return ret; // will return NULL if no more available
}

void ParticleFree(Particle* p)
{
    // return p to the list of unalloc'ed Particles
    *((Particle**)p) = headParticle;
    headParticle = p;
}

You could modify the approach above to not start with any global static array at all, and use malloc() at first when the user calls ParticleAlloc(), but when Particles are returned, don't call free() but instead add the returned ones to the linked list of unalloc'ed particles. Then the next caller to ParticleAlloc() will get one off the list of free Particles rather than use malloc(). Any time there are no more on the free list, your ParticleAlloc() function could then fall back on malloc(). Or use a mix of the two strategies, which would really be the best of both worlds: If you know that your user will almost certainly be using at least 1000 Particles but occasionally might need more, you could start with a static array of 1000 and fall back on calling malloc() if you run out. If you do it that way, the malloc()'ed ones do not need special handling; just add them to your list of unalloc'ed Particles when they come back to ParticleFree(). You do NOT need to bother calling free() on them when your program exits; the OS will free the process'es entire memory space, so any leaked memory will clear up at that point.

I should mention that since you question was tagged "C" and not "C++", I answered it in the form of a C solution. In C++, the best way to implement this same thing would be to add "operator new" and "operator delete" methods to your Particle class. They would contain basically the same code as I showed above, but they override (not overload) the global 'new' operator and, for the Particle class only, define a specialized allocator that replaces global 'new'. The cool thing is that users of Particle objects don't even have to know that there's a special allocator; they simply use 'new' and 'delete' as normal and remain blissfully unaware that their Particle objects are coming from a special pre-allocated pool.

phonetagger
  • 7,701
  • 3
  • 31
  • 55
  • Your code works, but could you please explain how is your linked list stored? How to translate this? `*((Particle**)p) = p+1;` – Ulterior Jun 25 '12 at 01:01
  • @Ulterior - Cool... I didn't actually test it, so I'm glad it works. Are you the same as Grimless? They key to the strategy is that when Particle objects aren't in use (i.e. they're on the unallocated linked list), they function as an intrinsic 'next' pointer for the list. To do so, I cast them as Particle pointers (Particle*). To write the address of a Particle object into one, I cast pointer p as a pointer-to-a-Particle*, or Particle**, then dereference it to assign a Particle* to it. In that particular case, I'm writing the address of the next Particle into it, forming the linked list. – phonetagger Jun 25 '12 at 01:31
  • This design works as long as `sizeof(Particle) >= sizeof(Particle *)`. If Particles are smaller than pointers, it fails. – Jonathan Leffler Jun 25 '12 at 02:14
  • 1
    Somewhere else on the page, the OP said that Particles are 24 bytes long, so I knew it would work even on 64-bit systems, but thanks for bringing this up. Pretty much any nontrivial object will be >= sizeof(void*) on 32-bit systems, and only trivial types will be < sizeof(void*) on 64-bit systems. If that does become an issue, you could artificially inflate Particle with a dummy int in order to make the scheme work, and even with the wasted memory the statically-allocated buffer scheme will consume less memory than pure malloc()-based schemes due to the overhead involved in each allocation. – phonetagger Jun 25 '12 at 03:12
  • ...overhead involved in malloc() allocations, that is. – phonetagger Jun 25 '12 at 03:14
  • To avoid aliasing problems this might work better as an array of unions of Particle and Particle* – Zan Lynx Feb 10 '17 at 16:06
  • @ZanLynx - Your suggestion of using a union may result in a cleaner-looking implementation, regardless of the aliasing issue. I’m not real strong on pointer aliasing issues, so forgive me if I’m wrong, but I think aliasing ***problems*** (bugs) occur when the compiler assumes two pointers can’t point to the same thing yet they do, and therefore optimizes accesses in such a way that the effects of a write via one pointer are not seen by a read via the other pointer, when correct program operation requires that the read must see the effects of the write. ... – phonetagger Feb 10 '17 at 22:32
  • ... I don’t **think** that’s an issue here; during initialization the code only writes to the array, never reading from it. When allocating a particle, it only reads from the array, never writing to it. And when freeing a particle, it only writes to the array, never reading from it. So I think the casting is safe from aliasing issues. Please correct me if I’m wrong. – phonetagger Feb 10 '17 at 22:32
1

Oh, sorry. This question is C only I see. Not C++. Well, if it was C++ the following would help you out.

Look at Boost's pool allocation library.

It sounds to me that each of your allocations is the same size? The size of a particle, correct? If so the pool allocation functions from Boost will work really well and you don't have to write your own.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
0

You would have to write your own, or find someone who has already written them and reuse what they wrote. There isn't a standard C library to manage that scenario, AFAIK.

You'd probably need 4 functions for your 'buffer allocation' code:

typedef struct ba_handle ba_handle;

ba_handle *ba_create(size_t element_size, size_t initial_space);
void  ba_destroy(ba_handle *ba);

void *ba_alloc(ba_handle *ba);
void  ba_free(ba_handle *ba, void *space);

The create function would do the initial allocation of space, and arrange to parcel out the information in units of the element_size. The returned handle allows you to have separate buffer allocations for different types (or even for the same type several times). The destroy function forcibly releases all the space associated with the handle.

The allocate function provides you with a new unit of space for use. The free function releases that for reuse.

Behind the scenes, the code keeps track of which units are in use (a bit map, perhaps) and might allocate extra space as needed, or might deny space when the initial allocation is used up. You could arrange for it to fail more or less dramatically when it runs out of space (so the allocator never returns a null pointer). Clearly, the free function can validate that the pointer it is given was one it supplied by the buffer allocator handle that is currently in use. This allows it to detect some errors that regular free() does not normally detect (though the GNU C library version of malloc() et al does seem to do some sanity checking that others do not necessarily do).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Agreed. Worth noting you're exactly asking for a C-equivalent to C++ new [] and delete []. – djechlin Jun 24 '12 at 21:05
  • Edited answer is much better. I was afraid that this would be the case. I will have to brush up on my heap allocation techniques to implement this properly. Will likely make OSS to help solve this exact problem. Thanks! – Grimless Jun 24 '12 at 21:23
0

Maybe try something like this instead...

Particle * particles[numParticles];
particles[0] = malloc(sizeof(Particle));
initialize_particle( particle[0] );

// ... Some more stuff
if (particle[0]->life < 0)
    free( particle[0] );

// @ program's end
// don't free(particles);
gpian
  • 245
  • 4
  • 15
  • This is the exact situation I am trying to avoid: mallocing on a per-particle basis. Malloc can become very slow when memory starts running out and I lose out on 24 bytes for every malloc, wasting 24K for a 1,000 particle system. Generally, I am trying to efficiently size my buffer to reuse the particles without hitting malloc again. – Grimless Jun 24 '12 at 21:20
  • Changed particles to a Particle * array, but I guess its a moot point. – gpian Jun 24 '12 at 21:42
-1

I am building a simple particle system and want to use a single array buffer of structs to manage my particles.

I think you answered it:

static Particle myParticleArray[numParticles];

Gets allocated at the start of the program and deallocated at the end, simple. Or do like your pseudocode and malloc the array all at once. You might ask yourself why allocate a single particle, why not allocate the whole system? Write your API functions to take a pointer to a particle array and an index.

Josh Petitt
  • 9,371
  • 12
  • 56
  • 104
  • 1
    This does not solve my problem. I need to be able to find the best available position within that particle array to spawn the next emitted particle. Additionally, if I turn off particles or desire to have more particles on-screen, that array cannot be resized nor deallocated. This is markedly counter to my goals and does not take my time nor effort of solving this problem thoroughly before asking into account. – Grimless Jun 24 '12 at 21:51
  • Hi Grimless, since you said 'C' 'simple' and 'single array', I just wanted to present an alternative that didn't require an extra library or a fancy malloc/free. It sounds like (from the other comments and AFAIK) you'll have to write your own malloc/free or find a lib outside of the standard. From your description (large number of particles), it sounded like you possibly wanted to manipulate collections of particles, rather than individual particles. I assumed you were writing the particle interface, so you could write it like: ParticleInit(Particles* particles, unsigned index); – Josh Petitt Jun 25 '12 at 00:49
  • No intent to sound thoughtless. – Josh Petitt Jun 25 '12 at 00:50