Schéma d'allocation mémoire pour une matrice
Dans un autre fils, j'ai posé un code proposant un schéma d'allocation de mémoire pour un type abstrait de donnée Matrix qui a suscité le débat sur le plan du respect des contraintes d'alignement (Version 3 ci-dessous). Je crée ici une nouvelle discussion pour ne pas polluer le thread initial. Si la version 1 du code ci-dessous est celle généralement proposée, la version 2 est probablement plus facile à gérer par un débutant (moins d'allocation, gestion des erreurs allégée). Du point de vue de la performance, pour des grosse matrices, y a-t-il un avantage à allouer la mémoire pour chaque ligne de la matrice comme dans la version 1, par rapport à une allocation en masse comme dans la version 2 (p.ex. moins de swap)? Ou inversemment?
Les trois versions proposées sont:
Version 1: (nlines + 2) allocations de mémoire:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| struct Matrix
{
double **_data;
size_t dim[2];
};
#define MATRIX_INIT {NULL, {0}}
struct Matrix *m_matrix_new(size_t nlines, size_t ncols)
{
struct Matrix *self = NULL;
if (nlines > 0 && ncols > 0)
{
/* Hop, on change les habitudes et on alloue tout en un bloc */
self = malloc(sizeof *self);
if (self != NULL)
{
static const struct Matrix tmp = MATRIX_INIT;
/* Initialisation de tous les champs de la structure a une valeur
nulle */
*self = tmp;
self->dim[0] = nlines;
self->dim[1] = ncols;
self->_data = malloc(nlines * sizeof *self->_data);
if (self->_data != NULL)
{
size_t i;
size_t j;
int err = 0;
for (i = 0; i < nlines && err == 0; i++)
{
self->_data[i] = malloc(ncols *sizeof *self->_data[i]);
if (self->_data[i] == NULL)
{
/* Echec d'allocation: on fait le menage, on sort de
la boucle et on renvoit la valeur NULL */
do
{
i--;
free(self->_data[i]);
}
while (i > 0);
free(self->_data);
free(self), self = NULL;
err = 1;
}
}
if (err == 0)
{
/* Initialisation */
for (i = 0; i < nlines; i++)
{
for (j = 0; j < ncols; j++)
{
self->_data[i][j] = 0.0;
}
}
}
}
else
{
/* Echec de l'allocation: on fait le menage et on renvoit la
valeur NULL */
free(self), self = NULL;
}
}
else
{
/* Echec de l'allocation de memoire pour self: rien a faire, on
renvoit la valeur NULL. */
}
}
return self;
}
struct Matrix * m_matrix_delete(struct Matrix *self)
{
if (self != NULL)
{
size_t i;
for (i = 0; i < self->dim[0]; i++)
{
free(self->_data[i]);
}
free(self->_data);
free(self), self = NULL;
}
return self;
} |
Version 2: 3 allocations de mémoire:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| struct Matrix
{
double **_data;
size_t dim[2];
};
#define MATRIX_INIT {NULL, {0}}
struct Matrix *m_matrix_new(size_t nlines, size_t ncols)
{
struct Matrix *self = NULL;
if (nlines > 0 && ncols > 0)
{
/* Hop, on change les habitudes et on alloue tout en un bloc */
self = malloc(sizeof *self);
if (self != NULL)
{
static const struct Matrix tmp = MATRIX_INIT;
/* Initialisation de tous les champs de la structure a une valeur
nulle */
*self = tmp;
self->dim[0] = nlines;
self->dim[1] = ncols;
self->_data = malloc(nlines * sizeof *self->_data);
if (self->_data != NULL)
{
self->_data[0] = malloc(nlines * ncols * sizeof *self->_data[0]);
if (self->_data[0] != NULL)
{
int i;
for (i = 1; i < nlines; i++)
{
self->_data[i] = self->_data[0] + i * ncols;
}
/* Initialization */
for (i = 0; i < nlines * ncols; i++)
{
self->_data[0][i] = 0.0;
}
}
else
{
/* Echec de l'allocation de memoire: on fait le menage et on
renvoit la valeur NULL */
free(self->_data);
free(self), self = NULL;
}
}
else
{
/* Echec de l'allocation: on fait le menage et on renvoit la
valeur NULL */
free(self), self = NULL;
}
}
else
{
/* Echec de l'allocation de memoire pour self: rien a faire, on
renvoit la valeur NULL. */
}
}
return self;
}
struct Matrix * m_matrix_delete(struct Matrix *self)
{
if (self != NULL)
{
free(self->_data[0]);
free(self->_data);
free(self), self = NULL;
}
return self;
} |
Version 3: 1 allocation de mémoire:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| struct Matrix
{
double **_data;
size_t dim[2];
};
#define MATRIX_INIT {NULL, {0}}
struct Matrix *m_matrix_new(size_t nlines, size_t ncols)
{
struct Matrix *self = NULL;
if (nlines > 0 && ncols > 0)
{
/* Hop, on change les habitudes et on alloue tout en un bloc */
self = malloc(sizeof *self + nlines * sizeof *self->_data +
nlines * ncols * sizeof **self->_data);
if (self != NULL)
{
size_t i, j;
static const struct Matrix tmp = MATRIX_INIT;
/* Initialisation de tous les champs de la structure a une valeur
nulle */
*self = tmp;
self->dim[0] = nlines;
self->dim[1] = ncols;
/* Mise en forme de l'objet pour que tout pointe ou il faut */
self->_data = (double **) (self + 1);
self->_data[0] = (double *) (self->_data + nlines);
for (i = (size_t) 1; i < nlines; i++)
{
self->_data[i] = self->_data[0] + i * ncols;
}
/* Initialisation des elements de la matrice */
for (i = (size_t) 0; i < nlines; i++)
{
for (j = (size_t) 0; j < ncols; j++)
{
self->_data[i][j] = 0.0;
}
}
}
}
return self;
}
void m_matrix_delete(struct Matrix *self)
{
if (self != NULL)
{
free(self);
}
} |
Avec mes meilleures salutations
Thierry