Skip to content

A header file in C for allocating contiguous multi-dimensional arrays

License

Notifications You must be signed in to change notification settings

leomccormack/md_malloc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

md_malloc

A header file comprising functions for contiguously allocating multi-dimensional arrays. The code is written in C and is also MSVC compliant.

Motivation

The main reasons for consolidating these contiguous multi-dimensional memory allocation functions into one file are threefold.

The first is to avoid (and hopefully discourage) the wide-spread adoption of non-contiguous memory allocation:

/* For example, commonly, allocating an A x B, 2-D "array" is written as: */
float** array2D = malloc(A*sizeof(float*));
for(int i=0; i<A; i++)
    array2D[i] = malloc(B*sizeof(float));
    
/* This 2-D array may indeed be easily indexed (i,j) array2D[i][j]. However, since it 
 * employs multiple malloc calls, the second dimension vectors are not guaranteed to be
 * adjacent to one-another in memory i.e. the memory is not necessarily "contiguous". 
 * Therefore, calls to memset/memcpy or other functions which rely on contiguous memory
 * allocation may result in crashes or behave unpredictably. Such allocations are
 * also likely to be accessed much slower, when considering how CPUs cache nearby memory */
 
memset(&array2D[0][0], 0, A*B*sizeof(float)); /* could be fine, but probably not */

/* freeing this 2-D "array" is also slightly cumbersome: */
for(int i=0; i<A; i++)
    free(array2D[i]);
free(array2D);

Second, is to still retain easy array indexing:

/* One may allocate a contiguous A x B, 2-D "array" with as single malloc call as: */
float* array2D = malloc(A*B*sizeof(float));

/* However, these are perhaps also not ideal, as they must be indexed (i,j) as 
 * array2D[i*B+j], which gets particularly messy for high-dimensional arrays; 
 * e.g. (i,j,k,l,p), array5D[i*B*C*D*E + j*C*D*E + k*D*E + l*E +p] */

And thirdly, to retain easy passing of N-D arrays to other functions:

/* Since C99, multidimensional arrays may indeed be declared as */
float (*array2D)[B] = malloc(sizeof(float[A][B]));

/* which can be indexed "array2D[i][j]" and freed "free(array2D)" easily. However, 
 * these cannot be easily and flexibly passed to generalised functions due to their 
 * variable type. */
 
/* Furthermore, for cross-platform project development, supporting the ancient MSVC 
 * compiler is (unfortunately) a common requirement and limitation. 
 * Since Microsoft's C-compiler is still based on C89/C90, this C99-style may not be 
 * considered as a viable option, regardless. */

Getting Started

To include md_malloc in your project, simply add the following:

#define MD_MALLOC_ENABLE
#include "md_malloc.h"

Arrays may be allocated, resized, and freed as:

/* To allocate an A x B, 2-D "array" */
float** array2D = (float**)malloc2d(A, B, sizeof(float));

/* To resize: */
array2D = (float**)realloc2d(array2D, C, D, sizeof(float));

/* Note that "array2D" is: 1) contiguous, 2) easily indexable, and 3) compiles under MSVC: */

memset(*array2D, 0, C*D*sizeof(float));           /* this is OK */
memset(FLATTEN2D(array2D), 0, C*D*sizeof(float)); /* or using the included macro... */
array2D[i][j] = 666.0f; /* as is this */

/* And to free: */
free(array2D);

Testing

This project also includes a test/test.c file, which performs checks that the arrays are truely contiguously allocated by using memcpy and CBLAS calls on md_malloc allocated arrays and subsequently comparing their results to that of their static memory counterparts. The test file also compares the time taken to allocate arrays using md_array, mangled arrays (array2d[i*dim2+j]), and C-99 style arrays.

TLDR: md_malloc provides truely contiguous memory allocation. It also has roughly the same allocation speed performance for 2-D arrays compared to C99 and mangled arrays. Whereas, 3-D arrays begin to show a slow-down. Here is the test output when allocating, indexing, populating, and freeing a 290 x 300 2-D array 30 000 times, and a 290 x 300 x 295 3-D array 100 times:

********** Malloc Speed Test - 2D DATA **********
- Mangled array time taken 13 seconds 803 milliseconds
- md_malloc time taken 13 seconds 684 milliseconds
- C99-style malloc time taken 13 seconds 603 milliseconds
md_malloc was 0 seconds 119 milliseconds FASTER than Mangled array
md_malloc was 0 seconds 81 milliseconds SLOWER than C99-style array

********** Malloc Speed Test - 3D DATA **********
- Mangled array time taken 13 seconds 478 milliseconds
- md_malloc time taken 14 seconds 578 milliseconds
- C99-style malloc time taken 13 seconds 426 milliseconds
md_malloc was 1 seconds 100 milliseconds SLOWER than Mangled array
md_malloc was 1 seconds 152 milliseconds SLOWER than C99-style array

Program ended with exit code: 0

(Using a 2,9 GHz Intel Core i7 Skylake 6920H CPU, Xcode 10.2 - Apple Clang compiler -Os)

References

The creation of this mini-project was inspired by discussions on stackoverflow regarding the topic, which can be found here:

Contact

Comments, bug reports, and suggested improvements are very much welcomed via GitHub "issues" or via email: leo.mccormack(at)aalto.fi

License

The code is distributed under the MIT license.

About

A header file in C for allocating contiguous multi-dimensional arrays

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages