OVH Cloud OVH Cloud

processeur double coeur

28 réponses
Avatar
J-F Portala
Bonjour,
je ne sais pas si c'est le bon groupe, mais
je ne voyais pas où poster ma question.

Je développe en C++ (avec VC++6.0 sous XP) des applications où il y a
pas mal de calculs.
Dernierement j 'ai fait des benchs sur une application et j'ai vu que le
processeur etait utilisé à 50% alors
que je m'attendais à 100% d'occupation

La seule explication pour moi est que le PC etant un double coeur, les
calculs n'étaient faits que sur un des deux.

Mon application va aussi vite sur un PC récent que sur un P4 2,4GHz d'il y a
3 ans.

Comment fait on pour gérer ces nouveaux processeurs?
Est ce qu'il faut affecter des tâches à chaque processeur, (et quand le pc
en aura 4?)...
Toutes les applications écrites pour un seul processeur vont donc tourner à
50% sur les nouveaux PC et à 25% sur les futurs quadro processeurs?

Cela me parait un sacré retour en arrière en terme de performances.

Peut être que je suis à côté de la plaque, n'hésitez pas à m'en faire part.

Merci

Jeff

10 réponses

1 2 3
Avatar
Fabien LE LEZ
Avatar
Ivan Dutka-Malen
Eric Pruneau a écrit :
"J-F Portala" a écrit dans le message de news:
4851193d$0$19724$
Merci de vos reponses.

Tu évoquais le fait qu'un compilateur récent effectuerait la tâche tout
seul.
Cela n'a pas l'air d'être simple à vérifier.
J'ai passé un peu de temps sur le net pour avoir des informations sur les
compilateurs et leur capacité
à gérer plusieurs processeurs, mais sans succès.
Est ce que faire du multithreading veut dire automatiquement que les
threads seront affectés aux différents processeurs.
Est ce que cela dépend du compilateur (je travaille avec VC++6.0 plus trés
jeune), ou de l'OS.

Merci
Jeff




Je travaille avec le compilateur d'intel et il y a effectivement une option
d'optimisation qui permet de paraleliser le code automatiquement. Pour mon
cas il y a une légère amélioration, mais il faut noter que j'utilise aussi
plusieurs thread, donc je ne m'attendais pas à des gains spectaculaires.

La meilleur options pour des performances optimales serait probablement
d'utiliser "Intel® Threading Building Blocks". Je n'ai jamais utilisé mais
comme ils disent dans leur publicité: "Thread like an expert whitout being
one!". Ce qui est bien avec cet outil c'est qu'il s'adapte automatiquement
au nombre de coeur présent sur la machine.

--------------
Eric Pruneau





Bonsoir,

Je pense que tu peux oublier tout rêve de compilateur parallélisant.
L'analyse d'un code pour en extraire un parallélisme efficace est
extrèmement complexe et ne fonctionne en pratique que sur des boucles
très simples avec des données (notamment des tableaux) que le
compilateur peut aisément décomposer pour faire naître du parallélisme.
Concrètement, cela signifie qu'en dehors de boucles du type SAXPY (S =
A*X + Y) avec un pas d'accès (stride) regulier (idéalement 1), où les
tableaux (S, A, X et Y) sont non recouvrants et vus comme tels par le
compilateur, le compilateur choisit la solution de prudence et laisse
tout séquentiel.

Le moindre pointeur dans ta boucle empêche le compilateur de savoir
quelle est la donnée qu'il manipule et si deux pointeurs sont utilisés,
il ne peut pas savoir s'ils ne pointent pas effectivement sur la même
donnée. Les références constantes améliorent un peu les choses. Très peu.

L'autre problème qui se pose alors est le volume de calcul nécessaire
(grainsize) pour que le parallélisme puisse être efficace. Si ta boucle
est trop petite (=pas beaucoup d'instructions), tu seras peut-être
parallèle, mais au final tu ne gagneras rien voire tu perdas du temps à
créer tes threads et à les synchroniser.

En réalité, tu prends un peu le problème à l'envers. Tu as un belle
machine que tu exploites (ou que tu crois exploiter) de manière
insuffisante et cela est frustrant. Le parallélisme de ton application
sera d'autant meilleur que l'application aura été pensée dans ce sens à
l'origine. Cela signifie qu'au lieu de chercher à améliorer le
parallélisme d'un algorithme séquentiel, il est parfois plus productif
de le remplacer par un algorithme intrinsèquement parallèle.
Ensuite, sur un plan purement informatique, il faut que tes structures
de données soient aussi pensées parallèles.
Et question à 100 balles, as-tu pensé à faire un profiling de ton code
pour voir où tu consommais réellement du temps ? Si ton code tourne
'mal' (= avec une mauvaise utilisation des ressources) en séquentiel, il
faut commencer par là.
Et je ne te parle pas des différentes typologies de codes (CPU bound,
memory bound, etc.) qui font que ta stratégie de parallélisation sera
différente.

Bref tu mets le doigt dans un domaine où ceux qui y sont depuis quelques
années se demandent s'il ont fait le bon choix de carrière tellement les
écueils sont nombreux et (vraiment) velus. Mais bon, c'est stimulant
intellectuellement.

Si après cela tu persistes dans ton entreprise, je te recommande soit
d'utiliser soit OpenMP (je ne sais pas si ton compilo favori le
supporte) soit les TBB (= Intel Threading Building Blocks). Le résultat
en termes de performance seront équivelents, mais l'approche est différente.
OpenMP fonctionne depuis longtemps en FORTRAN et en C, depuis moins
longtemps en C++ (depuis la version 4.2 de gcc par exemple) mais induit
peu de modification de ton code car ce sont des pragmas à ajouter devant
tes boucles (pour faire simple). C'est efficace, ça marche, on aime ou
on n'aime pas...
Les TBB sont une petite révolution car ils utilisent tout ce que sait
faire le C++ pour exprimer des concepts de parallélisme de manière assez
naturelle. Les performances sont au rendez-vous (comme OpenMP) et cela
fonctionne très bien. Ce n'est pas difficile à mettre en oeuvre pour
quelqu'un qui sait utiliser la STL. Les principes sont les mêmes. Par
contre cela induit une restructuration de ton code car (par exemmple)
tous les corps de boucle sont déportés dans des functors et il y a un
functor par boucle ! Bref tu as du mal à reconnaître ton algo...

Les deux solutions gèrent un nombre quelconque de coeurs et de multipro
(SMP) donc pas de soucis pour l'avenir.

Voilà, si ça peut t'aider.
Ivan

PS : Faire du code parallèle c'est bien, mais as-tu un debuggeur
parallèle ? ;-)
Avatar
Sylvain SF
Ivan Dutka-Malen wrote on 18/06/2008 22:32:



[...]
En réalité, tu prends un peu le problème à l'envers. Tu as un belle
machine que tu exploites (ou que tu crois exploiter) de manière
insuffisante et cela est frustrant. Le parallélisme de ton application
sera d'autant meilleur que l'application aura été pensée dans ce sens à
l'origine. Cela signifie qu'au lieu de chercher à améliorer le
parallélisme d'un algorithme séquentiel, il est parfois plus productif
de le remplacer par un algorithme intrinsèquement parallèle.



ou encore penser les services utilisateurs de son application avec
ce multi-core; si l'appli a un tant soit peu d'interface (pour gérer
les paramètres des calculs à réaliser), on pourra par exemple gérer
(spontanément via 2 threads non liés) l'interface et les actions
utilisateurs d'un coté et le rendu (l'impact des modifications
faites via l'IHM sur les données présentées) d'un autre.

Sylvain.
Avatar
James Kanze
On Jun 18, 10:32 pm, Ivan Dutka-Malen wrote:
Eric Pruneau a écrit :
> "J-F Portala" a écrit dans le message de news:
> 4851193d$0$19724$



>> Tu évoquais le fait qu'un compilateur récent effectuerait
>> la tâche tout seul. Cela n'a pas l'air d'être simple à
>> vérifier. J'ai passé un peu de temps sur le net pour avoir
>> des informations sur les compilateurs et leur capacité à
>> gérer plusieurs processeurs, mais sans succès. Est ce que
>> faire du multithreading veut dire automatiquement que les
>> threads seront affectés aux différents processeurs. Est ce
>> que cela dépend du compilateur (je travaille avec VC++6.0
>> plus trés jeune), ou de l'OS.



> Je travaille avec le compilateur d'intel et il y a
> effectivement une option d'optimisation qui permet de
> paraleliser le code automatiquement. Pour mon cas il y a une
> légère amélioration, mais il faut noter que j'utilise aussi
> plusieurs thread, donc je ne m'attendais pas à des gains
> spectaculaires.



> La meilleur options pour des performances optimales serait
> probablement d'utiliser "Intel® Threading Building Blocks".
> Je n'ai jamais utilisé mais comme ils disent dans leur
> publicité: "Thread like an expert whitout being one!". Ce
> qui est bien avec cet outil c'est qu'il s'adapte
> automatiquement au nombre de coeur présent sur la machine.



Je pense que tu peux oublier tout rêve de compilateur
parallélisant. L'analyse d'un code pour en extraire un
parallélisme efficace est extrèmement complexe et ne
fonctionne en pratique que sur des boucles très simples avec
des données (notamment des tableaux) que le compilateur peut
aisément décomposer pour faire naître du parallélisme.
Concrètement, cela signifie qu'en dehors de boucles du type
SAXPY (S = A*X + Y) avec un pas d'accès (stride) regulier
(idéalement 1), où les tableaux (S, A, X et Y) sont non
recouvrants et vus comme tels par le compilateur, le
compilateur choisit la solution de prudence et laisse tout
séquentiel.



Dans la calcule numérique, il paraît que des boucles de ce genre
prédominent, ou au moins, c'est elles qui font prèsque toujours
le chemin critique pour la performance. Il y a eu pas mal de
recherche sur les possibilités de les paralleliser, et les
exigeances du compilateur pour le faire ont en partie
conditionné l'évolution de Fortran.

Le moindre pointeur dans ta boucle empêche le compilateur de
savoir quelle est la donnée qu'il manipule et si deux
pointeurs sont utilisés, il ne peut pas savoir s'ils ne
pointent pas effectivement sur la même donnée. Les références
constantes améliorent un peu les choses. Très peu.



Et voilà le problème dans le C et le C++. Le fait que les
tableaux deviennent très vite des pointeurs, et que le
compilateur ne peut paralleliser que s'il peut établir que les
tableaux (designés par des pointeurs) ne se recouvrent pas.

J'ai entendu vaguement parler des recherches à cet égard, il y a
un certain temps. Ce que j'ai entendu, c'est qu'il y avait bien
un compilateur expérimental qui faisait une optimisation
globale, en suivant la source des pointeurs, et qui savait que
des pointeurs qui sortaient de deux new distincts ne pouvaient
pas se recouvrir. Mais je n'en sais pas plus ; je l'ai entendu
en passant dans un couloir.

En principe, c'est possible. Dans la pratique, c'est bigrement
difficile.

L'autre problème qui se pose alors est le volume de calcul
nécessaire (grainsize) pour que le parallélisme puisse être
efficace. Si ta boucle est trop petite (=pas beaucoup
d'instructions), tu seras peut-être parallèle, mais au final
tu ne gagneras rien voire tu perdas du temps à créer tes
threads et à les synchroniser.



C'est vrai si tu veux travailler au niveau Posix (ou de l'API
Windows). Au niveau hardware, il existe en général des
possibilités d'une synchronisation mois chère.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Falk Tannhäuser
James Kanze wrote:
Et voilà le problème dans le C et le C++. Le fait que les
tableaux deviennent très vite des pointeurs, et que le
compilateur ne peut paralleliser que s'il peut établir que les
tableaux (designés par des pointeurs) ne se recouvrent pas.



En C99, le mot clé "restrict" est censé adresser ce problème -
l'utilisateur promet que les pointeurs ainsi qualifiés ne pointent pas
sur des zones mémoires qui se recouvrent, et si cela s'avère être faux,
alors tant pis pour lui.

En C++ ce problème a motivé l'introduction de std::valarray, mais je ne
sais pas combien de compilateurs existants savent réellement optimiser
(voir même paralléliser) les accès dans ce cas. Enfin je n'ai pas
l'impression qu'il y ait beaucoup de gens qui se servent de
std::valarray en pratique...

Falk
Avatar
Ivan Dutka-Malen
James Kanze a écrit :
On Jun 18, 10:32 pm, Ivan Dutka-Malen wrote:
Je pense que tu peux oublier tout rêve de compilateur
parallélisant. L'analyse d'un code pour en extraire un
parallélisme efficace est extrèmement complexe et ne
fonctionne en pratique que sur des boucles très simples avec
des données (notamment des tableaux) que le compilateur peut
aisément décomposer pour faire naître du parallélisme.
Concrètement, cela signifie qu'en dehors de boucles du type
SAXPY (S = A*X + Y) avec un pas d'accès (stride) regulier
(idéalement 1), où les tableaux (S, A, X et Y) sont non
recouvrants et vus comme tels par le compilateur, le
compilateur choisit la solution de prudence et laisse tout
séquentiel.



Dans la calcule numérique, il paraît que des boucles de ce genre
prédominent, ou au moins, c'est elles qui font prèsque toujours
le chemin critique pour la performance. Il y a eu pas mal de
recherche sur les possibilités de les paralleliser, et les
exigeances du compilateur pour le faire ont en partie
conditionné l'évolution de Fortran.



Oui et non. Cela dépend du type de calcul numérique que tu réalises.
Si ton calcul peut se réduire à la résolution d'un système linéaire Ax=b
(A matrice, x et b vecteurs) alors tu es dans le cas le plus fréquemment
traité. Il existe des tas de solveurs différents, chacun adapté à un cas
de figure (matrice pleine, creuse, bande, hiérarchique, mal
conditionnée, etc.) et la littérature est abondante. Je dirai que c'est
le cas facile et le plus souvent pour ne pas réinventer l'eau chaude on
utilise des bibliothèques toutes faites (Linpack, Lapack, Scalapack,
etc.). Tu rencontres par exemple ce genre de résolution dans les codes
éléments finis (mécanique des structures, des fluides, électromagnétisme
et j'en passe).
Mais il existe aussi des tas de codes qui ne rentrent pas dans ce moule.
Juste pour l'exemple, je travaille sur un code qui fait du calcul
probabiliste et il n'y a (presque) rien qui ressemble à un Ax=b dedans.
Par contre pour ce qui est des nombres aléatoires, ça y va...
Tu as aussi les codes qui font du gather/scatter, c'est-à-dire de
l'indirection d'indice, en particulier les codes éléments finis qui font
de l'Ax=B ! Ex:
// A,B et I sont des tableaux, N la taille de ce petit monde-là
for( size_t i=0; i<N; ++i) A[ I[i] ] = B[ i ];
Tu ne peux pas savoir si I[i] != I[j] (i!=j), donc tu ne peux pas
paralléliser car tu auras un conflit de données sur A.

Il se trouve aussi que les codes scientifiques ont généralement une
longue histoire. Beaucoup ont plusieurs années, voire plusieurs
décennies, au compteur. Ce qui fait qu'il ont été créés au moment où les
machines vectorielles (Cray, Nec, Fujitsu, etc.) avaient leur heure de
gloire. C'était aussi la période où tous les algorithmes qui tentaient
le pari du calcul intensif ont connu leurs meilleures implémentations.
On a donc produit des codes adaptés à des machines qui voyaient tout à
travers la notion de vecteur. Or le coût de réécriture d'un tel code est
souvent prohibitif et on ne le fait pas. Le mode de pensée a aussi été
fortement conditionné et les numériciens ont pris l'habitude de tout
vectoriser.

Le moindre pointeur dans ta boucle empêche le compilateur de
savoir quelle est la donnée qu'il manipule et si deux
pointeurs sont utilisés, il ne peut pas savoir s'ils ne
pointent pas effectivement sur la même donnée. Les références
constantes améliorent un peu les choses. Très peu.



Et voilà le problème dans le C et le C++. Le fait que les
tableaux deviennent très vite des pointeurs, et que le
compilateur ne peut paralleliser que s'il peut établir que les
tableaux (designés par des pointeurs) ne se recouvrent pas.



Mais le FORTRAN n'est pas mieux loti avec son instruction EQUIVALENCE
qui bousille l'antialiasing des données...

J'ai entendu vaguement parler des recherches à cet égard, il y a
un certain temps. Ce que j'ai entendu, c'est qu'il y avait bien
un compilateur expérimental qui faisait une optimisation
globale, en suivant la source des pointeurs, et qui savait que
des pointeurs qui sortaient de deux new distincts ne pouvaient
pas se recouvrir. Mais je n'en sais pas plus ; je l'ai entendu
en passant dans un couloir.



A condition que les new aient lieu pas trop loin de la boucle à
optimiser. Mais les codes numériques allouent généralement les données
en bloc au début, à la lecture des fichiers d'entrée, ce qui fait qu'au
bout de quelques fonctions, le compilateur est largué ne serait-ce que
par la pile d'appel qui devient gigantesque à traiter. Si en plus tu
fais de la compilation en unités séparées (= une fonction par fichier),
le compilo ne peut plus suivre le lien et l'analyse interprocédurale ne
pourra se faire qu'au link au mieux.

Sans compter aussi que les personnes qui ont écrit les codes un peu
anciens étaient souvent obsédés par la mémoire, et qu'ils réutilisaient
souvent les structures de données pour y mettre tout et n'importe quoi.
Si je te faisais le catalogue de ce que j'ai vu, tu aurais de la lecture
(amusante) pour le reste de la soirée. Si un humain n'arrive pas à
suivre ce qu'on met dans un tableau, je ne te parle pas du pauvre
compilo qui ne sait même pas ce qu'il manipule...

En principe, c'est possible. Dans la pratique, c'est bigrement
difficile.



Comme tu dis...

L'autre problème qui se pose alors est le volume de calcul
nécessaire (grainsize) pour que le parallélisme puisse être
efficace. Si ta boucle est trop petite (=pas beaucoup
d'instructions), tu seras peut-être parallèle, mais au final
tu ne gagneras rien voire tu perdas du temps à créer tes
threads et à les synchroniser.



C'est vrai si tu veux travailler au niveau Posix (ou de l'API
Windows). Au niveau hardware, il existe en général des
possibilités d'une synchronisation mois chère.



Ceci est vrai avec tous les threads quelle que soit l'implémentation. Le
problème est inhérent au parallélisme et à la gestion de la dépendance
entre les données (=je dois attendre que la données D soit produite
avant de pouvoir l'utiliser).
Mais je ne vois pas ce que tu veux dire avec le niveau hardware.

Ivan
Avatar
Fabien LE LEZ
On Thu, 19 Jun 2008 00:53:47 -0700 (PDT), James Kanze
:

Et voilà le problème dans le C et le C++. Le fait que les
tableaux deviennent très vite des pointeurs, et que le
compilateur ne peut paralleliser que s'il peut établir que les
tableaux (designés par des pointeurs) ne se recouvrent pas.



N'est-il pas envisageable de remplacer vector<> et compagnie par une
classe qui aurait les bonnes propriétés ? Ça peut être une classe
propriétaire, adaptée à un compilo, et que le compilo reconnaît.
Avatar
James Kanze
On Jun 19, 3:55 pm, Fabien LE LEZ wrote:
On Thu, 19 Jun 2008 00:53:47 -0700 (PDT), James Kanze
:



>Et voilà le problème dans le C et le C++. Le fait que les
>tableaux deviennent très vite des pointeurs, et que le
>compilateur ne peut paralleliser que s'il peut établir que les
>tableaux (designés par des pointeurs) ne se recouvrent pas.



N'est-il pas envisageable de remplacer vector<> et compagnie
par une classe qui aurait les bonnes propriétés ? Ça peut être
une classe propriétaire, adaptée à un compilo, et que le
compilo reconnaît.



vector<> lui-même a déjà une bonne partie des bonnes propriétés.
Si ta fonction prend en paramètres des références à des
vecteurs, tu sais d'office qu'ou bien, ce sont les mêmes
vecteurs, ou bien, ils ne se recouvrent pas. Reste à integrer
cette information dans le compilateur et... ne pas se servir des
itérateurs:-).

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Avatar
Fabien LE LEZ
On Thu, 19 Jun 2008 07:45:30 -0700 (PDT), James Kanze
:

... ne pas se servir des
itérateurs:-).



Ou pondre un équivalent de vector<> avec des itérateurs adéquats.
Avatar
James Kanze
On Jun 19, 1:42 pm, Ivan Dutka-Malen wrote:
James Kanze a écrit :
> On Jun 18, 10:32 pm, Ivan Dutka-Malen wrote:



[...]
> Dans la calcule numérique, il paraît que des boucles de ce
> genre prédominent, ou au moins, c'est elles qui font prèsque
> toujours le chemin critique pour la performance. Il y a eu
> pas mal de recherche sur les possibilités de les
> paralleliser, et les exigeances du compilateur pour le faire
> ont en partie conditionné l'évolution de Fortran.



Oui et non. Cela dépend du type de calcul numérique que tu
réalises. Si ton calcul peut se réduire à la résolution d'un
système linéaire Ax=b (A matrice, x et b vecteurs) alors tu es
dans le cas le plus fréquemment traité. Il existe des tas de
solveurs différents, chacun adapté à un cas de figure (matrice
pleine, creuse, bande, hiérarchique, mal conditionnée, etc.)
et la littérature est abondante. Je dirai que c'est le cas
facile et le plus souvent pour ne pas réinventer l'eau chaude
on utilise des bibliothèques toutes faites (Linpack, Lapack,
Scalapack, etc.). Tu rencontres par exemple ce genre de
résolution dans les codes éléments finis (mécanique des
structures, des fluides, électromagnétisme et j'en passe).



Mais il existe aussi des tas de codes qui ne rentrent pas dans
ce moule. Juste pour l'exemple, je travaille sur un code qui
fait du calcul probabiliste et il n'y a (presque) rien qui
ressemble à un Ax=b dedans. Par contre pour ce qui est des
nombres aléatoires, ça y va... Tu as aussi les codes qui font
du gather/scatter, c'est-à-dire de l'indirection d'indice, en
particulier les codes éléments finis qui font de l'Ax=B ! Ex:
// A,B et I sont des tableaux, N la taille de ce petit monde-là
for( size_t i=0; i<N; ++i) A[ I[i] ] = B[ i ];
Tu ne peux pas savoir si I[i] != I[j] (i!=j), donc tu ne peux pas
paralléliser car tu auras un conflit de données sur A.



Effectivement, ce genre de calcul ne se prête pas du tout à la
parallelisation. Même manual.

Il se trouve aussi que les codes scientifiques ont
généralement une longue histoire. Beaucoup ont plusieurs
années, voire plusieurs décennies, au compteur. Ce qui fait
qu'il ont été créés au moment où les machines vectorielles
(Cray, Nec, Fujitsu, etc.) avaient leur heure de gloire.
C'était aussi la période où tous les algorithmes qui tentaient
le pari du calcul intensif ont connu leurs meilleures
implémentations. On a donc produit des codes adaptés à des
machines qui voyaient tout à travers la notion de vecteur. Or
le coût de réécriture d'un tel code est souvent prohibitif et
on ne le fait pas. Le mode de pensée a aussi été fortement
conditionné et les numériciens ont pris l'habitude de tout
vectoriser.



Mais c'est exactement ce genre de calcul vectorisé qui se prête
le mieux à la parallelisation. Les machines vectorielles,
précisement, travaillaient souvent en parallel, et beaucoup de
la recherche dans la parallelisation automatique s'est fait sur
elles, à l'époque. (Recherche du travail de Ken Kennedy, de Rice
University, si ça t'intéresse. J'ai lu plusieurs papiers de lui
sur le sujet fin des années 1980.)

>> Le moindre pointeur dans ta boucle empêche le compilateur de
>> savoir quelle est la donnée qu'il manipule et si deux
>> pointeurs sont utilisés, il ne peut pas savoir s'ils ne
>> pointent pas effectivement sur la même donnée. Les références
>> constantes améliorent un peu les choses. Très peu.



> Et voilà le problème dans le C et le C++. Le fait que les
> tableaux deviennent très vite des pointeurs, et que le
> compilateur ne peut paralleliser que s'il peut établir que les
> tableaux (designés par des pointeurs) ne se recouvrent pas.



Mais le FORTRAN n'est pas mieux loti avec son instruction
EQUIVALENCE qui bousille l'antialiasing des données...



Sauf qu'il a introduit des règles qui disent que si tu passes un
global à une fonction, et que cette fonction accède aussi au
global, c'est un comportement indéfini, et que si tu passes de
différents tableaux liés par une EQUIVALENCE à une fonction,
c'est un comportement indéfini. C'est à peu près comme s'il y
avait un « restrict » implicit sur tous les paramètres.

> J'ai entendu vaguement parler des recherches à cet égard, il
> y a un certain temps. Ce que j'ai entendu, c'est qu'il y
> avait bien un compilateur expérimental qui faisait une
> optimisation globale, en suivant la source des pointeurs, et
> qui savait que des pointeurs qui sortaient de deux new
> distincts ne pouvaient pas se recouvrir. Mais je n'en sais
> pas plus ; je l'ai entendu en passant dans un couloir.



A condition que les new aient lieu pas trop loin de la boucle à
optimiser.



Pas forcément. L'argument que j'ai entendu, justement, disait
qu'on n'avait pas besoin de quelque chose de particulier dans le
langage, parce que dans le cas de quelque chose comme
std::vector, le compilateur était capable de reconnaître que la
mémoire utilisée par chaque vecteur ne se recouvrait pas.

Mais les codes numériques allouent généralement les données en
bloc au début, à la lecture des fichiers d'entrée, ce qui fait
qu'au bout de quelques fonctions, le compilateur est largué ne
serait-ce que par la pile d'appel qui devient gigantesque à
traiter.



Le compilateur voit que la fonction prend une référence à un
std::vector (ou quelque chose de semblable) en paramètre, et il
voit que toute la mémoire gérée par la classe a été allouée dan s
la classe. Et donc est disjointe de la mémoire de toute autre
instance de la classe.

Si en plus tu fais de la compilation en unités séparées (= une
fonction par fichier), le compilo ne peut plus suivre le lien
et l'analyse interprocédurale ne pourra se faire qu'au link au
mieux.



Ce qui est le cas habituel de nos jours, quand tu démandes de
l'optimisation. (Au moins, c'est le cas avec les versions
récentes de VC++, et de Sun CC. Comme d'habitude, g++ est en
retard, sans doute en partie à cause de sa dépendence sur des
outils externes, comme des éditeurs de liens du système hôte, et
son exigeance de portabilité.)

Sans compter aussi que les personnes qui ont écrit les codes
un peu anciens étaient souvent obsédés par la mémoire, et
qu'ils réutilisaient souvent les structures de données pour y
mettre tout et n'importe quoi. Si je te faisais le catalogue
de ce que j'ai vu, tu aurais de la lecture (amusante) pour le
reste de la soirée. Si un humain n'arrive pas à suivre ce
qu'on met dans un tableau, je ne te parle pas du pauvre
compilo qui ne sait même pas ce qu'il manipule...



Ça, malheureusement, je le connais.

> En principe, c'est possible. Dans la pratique, c'est bigrement
> difficile.



Comme tu dis...



>> L'autre problème qui se pose alors est le volume de calcul
>> nécessaire (grainsize) pour que le parallélisme puisse être
>> efficace. Si ta boucle est trop petite (=pas beaucoup
>> d'instructions), tu seras peut-être parallèle, mais au final
>> tu ne gagneras rien voire tu perdas du temps à créer tes
>> threads et à les synchroniser.



> C'est vrai si tu veux travailler au niveau Posix (ou de l'API
> Windows). Au niveau hardware, il existe en général des
> possibilités d'une synchronisation mois chère.



Ceci est vrai avec tous les threads quelle que soit
l'implémentation. Le problème est inhérent au parallélisme et
à la gestion de la dépendance entre les données (=je dois
attendre que la données D soit produite avant de pouvoir
l'utiliser).



Mais je ne vois pas ce que tu veux dire avec le niveau hardware.



Qu'il y a en général des instructions machines qui assure la
synchronisation entre la mémoire et ta CPU (flush du pipeline
d'écriture, purge du pipeline de lecture), et qu'il existe des
algorithmes qui permettent à se resynchroniser à assez peu de
frais si la mémoire est synchronisée.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
1 2 3