OVH Cloud OVH Cloud

valarray et std::allocator

8 réponses
Avatar
julien pommier
Bonjour =E0 tous,

Je cherche une classe qui me permettrait de g=E8rer efficacement de petits
vecteurs (petits mais de taille variable, inf=E9rieure =E0 8, g=E9n=E9ralem=
ent
=E9gale =E0 2 ou 3, et nombreux), principalement de "double". Pour l'instant
ce sont des std::vector<double>, mais le co=FBt en terme de m=E9moire est un
peu =E9lev=E9: dans le std::vector il y a 3 pointeurs, qui prennent 12 octe=
ts
sur une archi 32 bits classique, et 24 sur une machine 64 bits. Donc sur
mon pc, un std::vector<double> v(2) consomme 28 octets.

J'ai jet=E9 un oeil aux valarray<double>, qui n'ont que deux membres dans la
structure (pointeur+taille), donc =E7a permet de gagner 4 ou 8 octets par
rapport aux std::vector sur la taille de l'objet. Mais par contre ils
utilisent le new[] pour faire les allocations, qui rajoute
syst=E9matiquement 8 octets =E0 la taille du bloc allou=E9 (au moins ici su=
r une
debian/g++-3.3.2), du coup (sur mon pc), un std::valarray<double> v(2)
consomme 32 octets.

Et j'en arrive donc =E0 ma question: pour quelle raison le valarray
n'utilise-t-il pas les allocateurs (auxquels je ne connais rien donc la
r=E9ponse est peut-etre triviale) ? =E0 moins que le std::allocator ait un
surco=FBt m=E9moire =E0 peu pr=E8s =E9quivalent =E0 celui de new (auquel ca=
s vous
pouvez mettre ce message =E0 la poubelle) ?

Une troisi=E8me possibilit=E9 que j'envisage, c'est de refaire une classe
"from scratch" qui utilise le std::allocator<double>, et une feinte un peu
d=E9gueu sur les pointeurs: puisque les adresses retourn=E9es par
std::allocator<double> seront align=E9s sur une adresse multiple de 8
(d'ailleurs j'aimerais bien savoir dans quelle mesure cette hypoth=E8se est
valable), =E7a laisse de la place pour stocker la taille du vecteur dans les
3 bits de poids faible du pointeur. Du coup le co=FBt d'un vecteur de taille
n <=3D 8 est simplement sizeof(double*) + n*sizeof(double), soit 20 octets
pour un vecteur de 2 double.

Pour l'instant j'h=E9site.. j'aurais bien utilis=E9 valarray parce qu'on pe=
ut
compter sur ses performances pour les calculs, mais en d=E9finitive c'est
celui qui consomme le plus de m=E9moire.


--
Julien

8 réponses

Avatar
Gabriel Dos Reis
julien pommier writes:

| Bonjour à tous,
|
| Je cherche une classe qui me permettrait de gèrer efficacement de petits
| vecteurs (petits mais de taille variable, inférieure à 8, généralement
| égale à 2 ou 3, et nombreux), principalement de "double". Pour l'instant
| ce sont des std::vector<double>, mais le coût en terme de mémoire est un
| peu élevé: dans le std::vector il y a 3 pointeurs, qui prennent 12 octets
| sur une archi 32 bits classique, et 24 sur une machine 64 bits. Donc sur
| mon pc, un std::vector<double> v(2) consomme 28 octets.

Ces petits comptes me surprendront toujours.

Quelles sont les besoins de ton programme ? Quelles sont les
alternatives ? Quels sont les avantages et les inconvénients ?

Tant que des réponses claires et nettes ne sont pas apportées à ces
questions, je trouverai les petits comptes ci-haut sans aucun intérêt.

| J'ai jeté un oeil aux valarray<double>, qui n'ont que deux membres dans la

C'est une mauvaise idée de regarder derrière la scène.

| structure (pointeur+taille), donc ça permet de gagner 4 ou 8 octets par
| rapport aux std::vector sur la taille de l'objet. Mais par contre ils
| utilisent le new[] pour faire les allocations, qui rajoute
| systématiquement 8 octets à la taille du bloc alloué (au moins ici sur une
| debian/g++-3.3.2), du coup (sur mon pc), un std::valarray<double> v(2)
| consomme 32 octets.

Et tu crois que std::allocator<T> fait quoi ?

| Et j'en arrive donc à ma question: pour quelle raison le valarray
| n'utilise-t-il pas les allocateurs (auxquels je ne connais rien donc la
| réponse est peut-etre triviale) ? à moins que le std::allocator ait un
| surcoût mémoire à peu près équivalent à celui de new (auquel cas vous
| pouvez mettre ce message à la poubelle) ?

Je poserais plutôt la question dans l'autre sens : pourquoi
std::valarray<> utiliserait un std::allocator<> ?

| Une troisième possibilité que j'envisage, c'est de refaire une classe
| "from scratch" qui utilise le std::allocator<double>, et une feinte un peu
| dégueu sur les pointeurs: puisque les adresses retournées par
| std::allocator<double> seront alignés sur une adresse multiple de 8
| (d'ailleurs j'aimerais bien savoir dans quelle mesure cette hypothèse est
| valable),

Elle est *fortement* dépendante de ta machine, du compilateur et des
versions du compilateur. Qu'est-ce que tu gagnes ? Cela eb vaut-il la
peine ? Ne peux-tu pas envisager des alternatives élégantes ?

| ça laisse de la place pour stocker la taille du vecteur dans les
| 3 bits de poids faible du pointeur.

jusqu'au jour où tu as un allocateur traçant qui fait trucs dans ton
dos. Et là KABOUM! Quel est le gain ?
Si tu utilises des sortes de bitfields pour stocker la taille, il te
faut des séquences d'instructions pour extraire le vrai pointeur à
chaque fois que tu veux déréférencer un pointeur. Optimises-tu vraiment ?
Il te faut aussi connaître un type intégral compatible avecton
pointeur. Bref que des problèmes.

| Du coup le coût d'un vecteur de taille
| n <= 8 est simplement sizeof(double*) + n*sizeof(double), soit 20 octets
| pour un vecteur de 2 double.
|
| Pour l'instant j'hésite.. j'aurais bien utilisé valarray parce qu'on peut
| compter sur ses performances pour les calculs, mais en définitive c'est
| celui qui consomme le plus de mémoire.

Tu utiliseras valarray s'il convient aux besoins de ton
programme. Pareil pour vector. Donc la question est, pourquoi
utilises-tu vector (ou valarray) ? Il te faut une réponse claire.
Sinon, ces micro optimisations (qui en fait ne sont pas des
optimisations) ne te créeront que des problèmes.

-- Gaby
Avatar
julien pommier
On 22 Dec 2003 20:54:11 +0100
Gabriel Dos Reis wrote:

julien pommier writes:

| Bonjour à tous,
|
| Je cherche une classe qui me permettrait de gèrer efficacement de pet its
| vecteurs (petits mais de taille variable, inférieure à 8, génér alement
| égale à 2 ou 3, et nombreux), principalement de "double". Pour l'in stant
| ce sont des std::vector<double>, mais le coût en terme de mémoire e st un
| peu élevé: dans le std::vector il y a 3 pointeurs, qui prennent 12
octets| sur une archi 32 bits classique, et 24 sur une machine 64 bits.
Donc sur| mon pc, un std::vector<double> v(2) consomme 28 octets.

Ces petits comptes me surprendront toujours.

Quelles sont les besoins de ton programme ? Quelles sont les
alternatives ? Quels sont les avantages et les inconvénients ?

Tant que des réponses claires et nettes ne sont pas apportées à ces
questions, je trouverai les petits comptes ci-haut sans aucun intérêt .


C'est pour un code elements finis, qui gère des maillages, donc des
collections de points (d'où les petites tailles). Ces points peuvent êt re
très nombreux, on les ballade pas mal, on les additionne de temps en
temps... Sur des maillages de taille assez importante j'ai été étonn é de la
place prise en mémoire par la structure définissant le maillage, du coup
j'ai regardé combien prend un std::vector.
Ce qui m'ennuie (un peu), ce n'est pas seulement les 28 octets, mais aussi
que quand je parcoure un std::vector<point>, chaque élement du vecteur
occupe 12 octets, et sur ces douze, il y en a toute une partie qui ne
m'interesse pas (en gros le pointeur qui permet de redimensionner les
vecteurs), celle là elle vient polluer le cache et tout ça.
A vrai dire même le membre qui contient la dimension d'un point ne
m'interesse pas puisqu'on peut supposer que tous les points de la collection
font la même taille, mais là je n'ai pas trop d'idée pour gèrer
intelligement (et simplement) une collection de points, en pouvant
extraire/ajouter des points, sans dupliquer leur taille.

Donc voilà, partant de ce constat, et aillant un peu de temps à perdre, je
me suis dit que j'allais essayer de changer nos points basés sur
std::vector<> par autre chose qui serait un peu plus orienté "petits
vecteurs"


| J'ai jeté un oeil aux valarray<double>, qui n'ont que deux membres da ns
la

C'est une mauvaise idée de regarder derrière la scène.
Ça reste instructif, même si les double underscore partout demandent un

petit effort d'accomodation. J'ai même croisé votre nom ;-)

| structure (pointeur+taille), donc ça permet de gagner 4 ou 8 octets p ar
| rapport aux std::vector sur la taille de l'objet. Mais par contre ils
| utilisent le new[] pour faire les allocations, qui rajoute
| systématiquement 8 octets à la taille du bloc alloué (au moins ic i sur
une| debian/g++-3.3.2), du coup (sur mon pc), un std::valarray<double>
v(2)| consomme 32 octets.

Et tu crois que std::allocator<T> fait quoi ?

| Et j'en arrive donc à ma question: pour quelle raison le valarray
| n'utilise-t-il pas les allocateurs (auxquels je ne connais rien donc la
| réponse est peut-etre triviale) ? à moins que le std::allocator ait un
| surcoût mémoire à peu près équivalent à celui de new (auque l cas vous
| pouvez mettre ce message à la poubelle) ?

Je poserais plutôt la question dans l'autre sens : pourquoi
std::valarray<> utiliserait un std::allocator<> ?
Je la prends et je la retourne: pourquoi pas ? honnetement je ne connais

rien aux allocateurs qui m'ont l'air assez controversé, donc quelque soit le
sens de la question je n'ai aucune idée de la réponse ;)


| Une troisième possibilité que j'envisage, c'est de refaire une clas se
| "from scratch" qui utilise le std::allocator<double>, et une feinte un
peu| dégueu sur les pointeurs: puisque les adresses retournées par
| std::allocator<double> seront alignés sur une adresse multiple de 8
| (d'ailleurs j'aimerais bien savoir dans quelle mesure cette hypothèse
est| valable),

Elle est *fortement* dépendante de ta machine, du compilateur et des
versions du compilateur. Qu'est-ce que tu gagnes ? Cela eb vaut-il la
peine ? Ne peux-tu pas envisager des alternatives élégantes ?

| ça laisse de la place pour stocker la taille du vecteur dans les
| 3 bits de poids faible du pointeur.

jusqu'au jour où tu as un allocateur traçant qui fait trucs dans ton
dos. Et là KABOUM! Quel est le gain ?
Si tu utilises des sortes de bitfields pour stocker la taille, il te
faut des séquences d'instructions pour extraire le vrai pointeur à
chaque fois que tu veux déréférencer un pointeur. Optimises-tu vrai ment ?
Il te faut aussi connaître un type intégral compatible avecton
pointeur. Bref que des problèmes.


Pour la dépendance sur la machine/etc, je suis bien d'accord, même si
j'imagine que sur les archi actuelles et futures ça doit assez bien passer
(je n'envisage pas de compiler le code sur gameboy). Ça peut faire partie
des choses qui sont detectées au ./configure, avec un "fallback" sur
valarray.

L'allocateur traçant je ne vois pas du tout ce que c'est, j'imagine que ce
genre de gruikerie sur les pointeurs est aussi capable d'embêter les garb age
collector.

Pour le gain, j'ai benché rapidement le truc (je remplis plusieurs fois un
gros std::vector<points>, je fais des sommes entre les elements).
L'espacement est la distance entre deux "new double[2]" consécutifs, donc
optimalement c'est 16 bytes.

(compilé en -O3)
std::vector de 20000 std::vector<double>(2):
mon pc: une machine 64 bits (alpha, cxx):
size + 16 bytes size@ + 32 bytes
creation : 0.81 sec creation : 3.51 sec
operator + : 2.44 sec operator + : 10.6 sec
operator -=: 1.05 sec operator -=: 4.13 sec

std::vector de 20000 smallvec(2) (i.e. ceux avec la feinte sur les
pointeurs)
mon pc: une machine 64 bits (alpha, cxx):
size= 4 + 16 bytes size= 8 + 32 bytes
creation : 0.47 sec creation : 1.15 sec
operator + : 0.87 sec operator + : 2.58 sec
operator -=: 0.58 sec operator -=: 1.35 sec

std::vector de 20000 valarray<double>(2):
mon pc: une machine 64 bits (alpha, cxx):
size= 8 + 24 bytes size + 32 bytes
creation : 0.69 sec creation : 2.78 sec
operator + : 0.82 sec operator + : 5.8 sec
operator -=: 0.72 sec operator -=: 3.06 sec

Donc voilà, au moins sur un pc j'économise quand même pas mal de mé moire, et
même le cache doit dire merci. Sur la dec, on peut dire que c'est pas
glorieux pour son compilateur, puisque chaque point(2) consomme 32 octets,
ça ne m'étonne pas que notre code rame comme une bete là-dessus.

| Du coup le coût d'un vecteur de taille
| n <= 8 est simplement sizeof(double*) + n*sizeof(double), soit 20 oct ets
| pour un vecteur de 2 double.
|
| Pour l'instant j'hésite.. j'aurais bien utilisé valarray parce qu'on
peut| compter sur ses performances pour les calculs, mais en définitive
c'est| celui qui consomme le plus de mémoire.

Tu utiliseras valarray s'il convient aux besoins de ton
programme. Pareil pour vector. Donc la question est, pourquoi
utilises-tu vector (ou valarray) ? Il te faut une réponse claire.
Sinon, ces micro optimisations (qui en fait ne sont pas des
optimisations) ne te créeront que des problèmes.


Pour l'instant c'est vector, pour des raisons historiques. Je pense que le
passage à valarray pour tous les vecteurs "gros" se fera de toute façon.
Mais pour les petits, au vu du bench sur la dec que je n'avais pas encore
fait tourner, je vais peut-etre me laisser tenter par le côté obscur

--
Julien

Avatar
Gabriel Dos Reis
julien pommier writes:

| On 22 Dec 2003 20:54:11 +0100
| Gabriel Dos Reis wrote:
|
| > julien pommier writes:
| >
| > | Bonjour à tous,
| > |
| > | Je cherche une classe qui me permettrait de gèrer efficacement de petits
| > | vecteurs (petits mais de taille variable, inférieure à 8, généralement
| > | égale à 2 ou 3, et nombreux), principalement de "double". Pour l'instant
| > | ce sont des std::vector<double>, mais le coût en terme de mémoire est un
| > | peu élevé: dans le std::vector il y a 3 pointeurs, qui prennent 12
| > octets| sur une archi 32 bits classique, et 24 sur une machine 64 bits.
| > Donc sur| mon pc, un std::vector<double> v(2) consomme 28 octets.
| >
| > Ces petits comptes me surprendront toujours.
| >
| > Quelles sont les besoins de ton programme ? Quelles sont les
| > alternatives ? Quels sont les avantages et les inconvénients ?
| >
| > Tant que des réponses claires et nettes ne sont pas apportées à ces
| > questions, je trouverai les petits comptes ci-haut sans aucun intérêt.
|
| C'est pour un code elements finis, qui gère des maillages, donc des
| collections de points (d'où les petites tailles). Ces points peuvent être
| très nombreux, on les ballade pas mal, on les additionne de temps en
| temps... Sur des maillages de taille assez importante j'ai été étonné de la
| place prise en mémoire par la structure définissant le maillage, du coup
| j'ai regardé combien prend un std::vector.

OK. Ce contexte étant mis, je comprends ce que tu veux dire (au
moins, je crois comprendre).

Donc reprenons -- je suis conscient qu'il est un peu tard à Paris.
D'après ce que tu dit plus bas, ton maillage est homogène, i.e. tous
les points ont la même dimension. Dans ce cas, je ne crois pas que
l'utilisation de vector<> s'impose.

| Ce qui m'ennuie (un peu), ce n'est pas seulement les 28 octets, mais aussi
| que quand je parcoure un std::vector<point>, chaque élement du vecteur
| occupe 12 octets, et sur ces douze, il y en a toute une partie qui ne
| m'interesse pas (en gros le pointeur qui permet de redimensionner les
| vecteurs), celle là elle vient polluer le cache et tout ça.
| A vrai dire même le membre qui contient la dimension d'un point ne
| m'interesse pas puisqu'on peut supposer que tous les points de la collection
| font la même taille, mais là je n'ai pas trop d'idée pour gèrer
| intelligement (et simplement) une collection de points, en pouvant
| extraire/ajouter des points, sans dupliquer leur taille.

est-ce que cette taille est connue à la compilation (je présume que
non, mais je pose quand même la question). Sinon, la solution
classique, consiste à allouer un gros bout de mémoire et à voir cette
mémoire comme une suite de petits points :
(1) pas de duplication de taille
(2) données consécutives
(3) pas d'allocation/deallocation intempestives

Tu implémentes cette vue par une classe wrapper autour du double* brute.

| J'ai même croisé votre nom ;-)

raison de plus -- je ne peux plus dire, ne faites pas ce que je fais ;-p

[...]

| > Je poserais plutôt la question dans l'autre sens : pourquoi
| > std::valarray<> utiliserait un std::allocator<> ?
| Je la prends et je la retourne: pourquoi pas ? honnetement je ne connais
| rien aux allocateurs qui m'ont l'air assez controversé, donc quelque soit le
| sens de la question je n'ai aucune idée de la réponse ;)

Hmm, valarray, c'est pour les tableaux de taille moyenne et plus.
C'est plus efficace quand on fait les operations sur place -- ou alors
il faut utiliser les expressions templates (comme tu as dû le voir
dans le code source). Les classes std::slice et std::slice_array
sont un exemple de l'implémentation de vue dont je parlais -- dans ton
cas, où on a encore plus d'information, on peut faire mieux.

[...]

| L'allocateur traçant je ne vois pas du tout ce que c'est, j'imagine que ce
| genre de gruikerie sur les pointeurs est aussi capable d'embêter les garbage
| collector.

Yep.

[...]

| Pour l'instant c'est vector, pour des raisons historiques. Je pense que le
| passage à valarray pour tous les vecteurs "gros" se fera de toute façon.
| Mais pour les petits, au vu du bench sur la dec que je n'avais pas encore
| fait tourner, je vais peut-etre me laisser tenter par le côté obscur

As-tu vraiment besoin de retourner par valeur, ou peux-tu calculer sur
place? Parce que si tu peux calculer sur place, en utilisant les
expressions templates, alors tu veux allouer une fois pour toute et
utiliser des classes qui ont la taille d'un pointeur.

-- Gaby
Avatar
julien pommier
On 22 Dec 2003 23:14:05 +0100
Gabriel Dos Reis wrote:

julien pommier writes:
| Ce qui m'ennuie (un peu), ce n'est pas seulement les 28 octets, mais
aussi| que quand je parcoure un std::vector<point>, chaque élement du
vecteur| occupe 12 octets, et sur ces douze, il y en a toute une partie
qui ne| m'interesse pas (en gros le pointeur qui permet de redimensionner
les| vecteurs), celle là elle vient polluer le cache et tout ça.
| A vrai dire même le membre qui contient la dimension d'un point ne
| m'interesse pas puisqu'on peut supposer que tous les points de la
collection| font la même taille, mais là je n'ai pas trop d'idée po ur
gèrer| intelligement (et simplement) une collection de points, en pouva nt
| extraire/ajouter des points, sans dupliquer leur taille.

est-ce que cette taille est connue à la compilation (je présume que
non, mais je pose quand même la question). Sinon, la solution
classique, consiste à allouer un gros bout de mémoire et à voir cet te
mémoire comme une suite de petits points :
(1) pas de duplication de taille
(2) données consécutives
(3) pas d'allocation/deallocation intempestives

Tu implémentes cette vue par une classe wrapper autour du double* brute.


Effectivement la taille n'est pas connue à la compilation, la seule chose
que l'on sache, c'est que la probabilité que l'utilisateur fasse des
elements finis en dimension 5 ou plus est très proche de zéro. Pour
l'allocation par gros bloc, effectivement ça serait idéal. Mais ça va
demander pas mal de boulot pour tout changer. L'idéal serait ne n'avoir in
fine qu'une seule classe pour décrire les points, et pas une classe pour les
references à un point dans un paquet, et une classe pour les points tout
seuls (histoire de limiter un peu les templates, ça prend déjà des pl ombes à
compiler). Faut que je reflechisse, mais ça serait mieux à tout point d e vue
que la feinte sur les pointeurs.

Hmm, valarray, c'est pour les tableaux de taille moyenne et plus.

C'est plus efficace quand on fait les operations sur place -- ou alors
il faut utiliser les expressions templates (comme tu as dû le voir
dans le code source). Les classes std::slice et std::slice_array
sont un exemple de l'implémentation de vue dont je parlais -- dans ton
cas, où on a encore plus d'information, on peut faire mieux.


J'ai le droit de regarder alors ? ;)

[...]

| Pour l'instant c'est vector, pour des raisons historiques. Je pense que
le| passage à valarray pour tous les vecteurs "gros" se fera de toute
façon.| Mais pour les petits, au vu du bench sur la dec que je n'avais pas
encore| fait tourner, je vais peut-etre me laisser tenter par le côté
obscur

As-tu vraiment besoin de retourner par valeur, ou peux-tu calculer sur
place? Parce que si tu peux calculer sur place, en utilisant les
expressions templates, alors tu veux allouer une fois pour toute et
utiliser des classes qui ont la taille d'un pointeur.


Sur les gros vecteurs on n'a pas besoin de choses très pointues. Et à v rai
dire ça serait ennuyeux de reposer sur ton implémentation des expressio ns
templates dans la libstdc++ si les autres implémentations de valarray ne
l'ont pas. Donc pour l'instant on se limite à des X+=V, mais c'est pas
gênant.

Au fait, pourquoi pas de std::allocator dans valarray ? tu dis que valarray
c'est pour les vecteurs de taille moyenne et plus, mais le std::allocator
est visiblement efficace sur de petits blocs et fait appel à new sur les
blocs de plus de 128 octets, donc ça marcherait aussi, non ? Je précise que
c'est juste pour ma culture, je ne veux pas lancer de flame sur le sujet ;)


--
Julien

Avatar
julien pommier
On 22 Dec 2003 23:14:05 +0100
Gabriel Dos Reis wrote:

julien pommier writes:
| Ce qui m'ennuie (un peu), ce n'est pas seulement les 28 octets, mais
aussi| que quand je parcoure un std::vector<point>, chaque élement du
vecteur| occupe 12 octets, et sur ces douze, il y en a toute une partie
qui ne| m'interesse pas (en gros le pointeur qui permet de redimensionner
les| vecteurs), celle là elle vient polluer le cache et tout ça.
| A vrai dire même le membre qui contient la dimension d'un point ne
| m'interesse pas puisqu'on peut supposer que tous les points de la
collection| font la même taille, mais là je n'ai pas trop d'idée po ur
gèrer| intelligement (et simplement) une collection de points, en pouva nt
| extraire/ajouter des points, sans dupliquer leur taille.

est-ce que cette taille est connue à la compilation (je présume que
non, mais je pose quand même la question). Sinon, la solution
classique, consiste à allouer un gros bout de mémoire et à voir cet te
mémoire comme une suite de petits points :
(1) pas de duplication de taille
(2) données consécutives
(3) pas d'allocation/deallocation intempestives

Tu implémentes cette vue par une classe wrapper autour du double* brute.


Effectivement la taille n'est pas connue à la compilation, la seule chose
que l'on sache, c'est que la probabilité que l'utilisateur fasse des
elements finis en dimension 5 ou plus est très proche de zéro. Pour
l'allocation par gros bloc, effectivement ça serait idéal. Mais ça va
demander pas mal de boulot pour tout changer. L'idéal serait ne n'avoir in
fine qu'une seule classe pour décrire les points, et pas une classe pour les
references à un point dans un paquet, et une classe pour les points tout
seuls (histoire de limiter un peu les templates, ça prend déjà des pl ombes à
compiler). Faut que je reflechisse, mais ça serait mieux à tout point d e vue
que la feinte sur les pointeurs.

Hmm, valarray, c'est pour les tableaux de taille moyenne et plus.

C'est plus efficace quand on fait les operations sur place -- ou alors
il faut utiliser les expressions templates (comme tu as dû le voir
dans le code source). Les classes std::slice et std::slice_array
sont un exemple de l'implémentation de vue dont je parlais -- dans ton
cas, où on a encore plus d'information, on peut faire mieux.


J'ai le droit de regarder alors ? ;)

[...]

| Pour l'instant c'est vector, pour des raisons historiques. Je pense que
le| passage à valarray pour tous les vecteurs "gros" se fera de toute
façon.| Mais pour les petits, au vu du bench sur la dec que je n'avais pas
encore| fait tourner, je vais peut-etre me laisser tenter par le côté
obscur

As-tu vraiment besoin de retourner par valeur, ou peux-tu calculer sur
place? Parce que si tu peux calculer sur place, en utilisant les
expressions templates, alors tu veux allouer une fois pour toute et
utiliser des classes qui ont la taille d'un pointeur.


Sur les gros vecteurs on n'a pas besoin de choses très pointues. Et à v rai
dire ça serait ennuyeux de reposer sur ton implémentation des expressio ns
templates dans la libstdc++ si les autres implémentations de valarray ne
l'ont pas. Donc pour l'instant on se limite à des X+=V, mais c'est pas
gênant.

Au fait, pourquoi pas de std::allocator dans valarray ? tu dis que valarray
c'est pour les vecteurs de taille moyenne et plus, mais le std::allocator
est visiblement efficace sur de petits blocs et fait appel à new sur les
blocs de plus de 128 octets, donc ça marcherait aussi, non ? Je précise que
c'est juste pour ma culture, je ne veux pas lancer de flame sur le sujet ;)


--
Julien

Avatar
julien pommier
On 22 Dec 2003 23:14:05 +0100
Gabriel Dos Reis wrote:

julien pommier writes:
| Ce qui m'ennuie (un peu), ce n'est pas seulement les 28 octets, mais
aussi| que quand je parcoure un std::vector<point>, chaque élement du
vecteur| occupe 12 octets, et sur ces douze, il y en a toute une partie
qui ne| m'interesse pas (en gros le pointeur qui permet de redimensionner
les| vecteurs), celle là elle vient polluer le cache et tout ça.
| A vrai dire même le membre qui contient la dimension d'un point ne
| m'interesse pas puisqu'on peut supposer que tous les points de la
collection| font la même taille, mais là je n'ai pas trop d'idée po ur
gèrer| intelligement (et simplement) une collection de points, en pouva nt
| extraire/ajouter des points, sans dupliquer leur taille.

est-ce que cette taille est connue à la compilation (je présume que
non, mais je pose quand même la question). Sinon, la solution
classique, consiste à allouer un gros bout de mémoire et à voir cet te
mémoire comme une suite de petits points :
(1) pas de duplication de taille
(2) données consécutives
(3) pas d'allocation/deallocation intempestives

Tu implémentes cette vue par une classe wrapper autour du double* brute.


Effectivement la taille n'est pas connue à la compilation, la seule chose
que l'on sache, c'est que la probabilité que l'utilisateur fasse des
elements finis en dimension 5 ou plus est très proche de zéro. Pour
l'allocation par gros bloc, effectivement ça serait idéal. Mais ça va
demander pas mal de boulot pour tout changer. L'idéal serait ne n'avoir in
fine qu'une seule classe pour décrire les points, et pas une classe pour les
references à un point dans un paquet, et une classe pour les points tout
seuls (histoire de limiter un peu les templates, ça prend déjà des pl ombes à
compiler). Faut que je reflechisse, mais ça serait mieux à tout point d e vue
que la feinte sur les pointeurs.

Hmm, valarray, c'est pour les tableaux de taille moyenne et plus.

C'est plus efficace quand on fait les operations sur place -- ou alors
il faut utiliser les expressions templates (comme tu as dû le voir
dans le code source). Les classes std::slice et std::slice_array
sont un exemple de l'implémentation de vue dont je parlais -- dans ton
cas, où on a encore plus d'information, on peut faire mieux.


J'ai le droit de regarder alors ? ;)

[...]

| Pour l'instant c'est vector, pour des raisons historiques. Je pense que
le| passage à valarray pour tous les vecteurs "gros" se fera de toute
façon.| Mais pour les petits, au vu du bench sur la dec que je n'avais pas
encore| fait tourner, je vais peut-etre me laisser tenter par le côté
obscur

As-tu vraiment besoin de retourner par valeur, ou peux-tu calculer sur
place? Parce que si tu peux calculer sur place, en utilisant les
expressions templates, alors tu veux allouer une fois pour toute et
utiliser des classes qui ont la taille d'un pointeur.


Sur les gros vecteurs on n'a pas besoin de choses très pointues. Et à v rai
dire ça serait ennuyeux de reposer sur ton implémentation des expressio ns
templates dans la libstdc++ si les autres implémentations de valarray ne
l'ont pas. Donc pour l'instant on se limite à des X+=V, mais c'est pas
gênant.

Au fait, pourquoi pas de std::allocator dans valarray ? tu dis que valarray
c'est pour les vecteurs de taille moyenne et plus, mais le std::allocator
est visiblement efficace sur de petits blocs et fait appel à new sur les
blocs de plus de 128 octets, donc ça marcherait aussi, non ? Je précise que
c'est juste pour ma culture, je ne veux pas lancer de flame sur le sujet ;)


--
Julien

Avatar
Gabriel Dos Reis
(Arnaud Debaene) writes:

| julien pommier wrote in message news:...
|
| > .
| >
| > C'est pour un code elements finis, qui g re des maillages, donc des
| > collections de points (d'o les petites tailles). Ces points peuvent t
| > re
| > tr s nombreux, on les ballade pas mal, on les additionne de temps en
| > temps... Sur des maillages de taille assez importante j'ai t tonn
| > de la
| > place prise en m moire par la structure d finissant le maillage, du coup
| > j'ai regard combien prend un std::vector.
| > Ce qui m'ennuie (un peu), ce n'est pas seulement les 28 octets, mais aussi
| > que quand je parcoure un std::vector<point>, chaque lement du vecteur
| > occupe 12 octets, et sur ces douze, il y en a toute une partie qui ne
| > m'interesse pas (en gros le pointeur qui permet de redimensionner les
| > vecteurs), celle l elle vient polluer le cache et tout a.
| > A vrai dire m me le membre qui contient la dimension d'un point ne
| > m'interesse pas puisqu'on peut supposer que tous les points de la collection
| > font la m me taille, mais l je n'ai pas trop d'id e pour g rer
| > intelligement (et simplement) une collection de points, en pouvant
| > extraire/ajouter des points, sans dupliquer leur taille.
|
| Donc tous les points d'une même collection font la même taille? Dans
| ce cas est-ce que tu ne pourrait pas représenter un point avec une
| structure de taille fixe, par exemple un boost::array ou quelque chose
| du même goût?

Un boost::array demande une taille fixe à la compilation. Ce n'est pas
le cas de Julien.

Mais globalement, on peut enroler un pointeur dans une classe -- c'est
plus facile lorsque les calculs sont faits « sur place ».

-- Gaby
Avatar
drkm
Gabriel Dos Reis writes:

En général, j'utilise quand même les classes imbriquées dans les
templates. J'exprime les contraintes différemment


As-tu des règles précises sur le sujet (l'expression de ces
contraintes) ? Que ce soit pour les classes « imbriquantes » ou les
paramètres templates.

--drkm