[nanomsg] poll: new allocator for consecutive chunks

  • From: Ioannis Charalampidis <ioannis.charalampidis@xxxxxxx>
  • To: <nanomsg@xxxxxxxxxxxxx>
  • Date: Thu, 14 Apr 2016 15:03:39 +0200

Hi all!

I am going to add an additional feature to the chunks core and I wanted your feedback in order to chose what would benefit everyone. In my case I will need to allocate a series of consecutive chunks for optimization reasons (fewer memory registrations) and since this is not currently supported I have two solutions planned for implementation :

(1) Shall I introduce a high-level function: nn_chunk_alloc_many( size_t size, int type, int count, void*** chunks ) that allocates a number of consecutive chunks using the allocation type specified?

Example:

void * chunks[4];
nn_chunk_alloc_many( 1024, NN_ALLOC_PAGEALIGN, 4, &chunks );

// .. use them as chunks ..

// Free them
nn_chunk_free( chunks[0] );
nn_chunk_free( chunks[1] );
nn_chunk_free( chunks[2] );
nn_chunk_free( chunks[3] );

Pros:

 * Very simple and straightforward API from user's PoV

Cons:

 * This requires additional reference tracking and custom de-allocator
   functions in order to wait for all chunks to be free'd before the
   actual memory region is released, but that's easily managed.
 * In order to implement the memory registration I will need to know
   the buffer base address and overall size (that in case of memory
   alignment won't be equal to size * count), therefore introducing a
   kind of ugly optional 5h parameter ( struct nn_chunk_meta * meta )
   that will be used to track such information.
 * If more fine-grained control is required it's difficult to access
   the implementation internals without hacking it (btw, this is
   something that I have been fighting with until I decided to actually
   touch the chunk code myself).


(2) Or shall I introduce a lower-level function : nn_chunk_init( void * ptr, size_t ptr_size, nn_chunk_free_fn destructor, void * userptr, void ** chunk ) that initializes a chunk structure to a given buffer? In this case the user should allocate the consecutive buffer and then call this function to initialize parts of it as chunks.

Example:

size_t chunk_size = 1024 + nn_chunk_hdrsize();
void * memory = aligned_alloc( sysconf(_SC_PAGESIZE), chunk_size * 4 );

// Create chunks
void * chunks[4];
void * ptr = memory;
for (int i=0; i<4; i++) {
nn_chunk_init( ptr, chunk_size, &free_fn, NULL, &chunks[i] );
   ptr = ((uint8_t*)ptr) + chunk_size;
}

// .. use them ..

// Free chunks (calls the given free function, doesn't free anything)
nn_chunk_free( chunks[0] );
nn_chunk_free( chunks[1] );
nn_chunk_free( chunks[2] );
nn_chunk_free( chunks[3] );

// User needs to free memory eventually, or needs to
// implement the high-level logic mentioned before to
// free memory when last chunk is freed
free( memory );

Pros:

 * No need to implement the reference tracking, which keeps the chunk
   core cleaner
 * The custom free function can be used to track
   implementation-specific logic (ex. mark buffer as free for re-use)
 * No need to track the individual chunks when it's time for clean-up,
   just free the allocated memory.

Cons:

 * The user needs to do the memory management.
 * Very similar API to nn_chunk_alloc_ptr that might introduce
   confusion. The difference is that the latter just creates a const
   pointer-chunk to the user data, while the former assumes the data
   given is a chunk and initializes it as such (writes a chunk header
   and returns a pointer to the chunk data).

(3) Or shall I implement both solutions?

Looking forward to your comment/choice!

Cheers,
Ioannis

Other related posts: