OVH Cloud OVH Cloud

Threads et boost

22 réponses
Avatar
JKB
Bonjour à tous,

Je ne sais pas si je suis sur le bon forum, mais je n'en trouve pas
de plus adapté. Toute redirection sera donc acceptée ;-)

Ma question porte sur la libboost qui est une bibliothèque écrite en
C++. J'utilise des fonctions de la boost_graph dans un bout de code
écrit en C et Fortran (toutes versions). Le code en question tourne en
environnement POSIX sur machines parallèles et la parallélisation
est faite actuellement à grands coups de fork(). Le fork() n'est pas
pénalisant parce que chaque calcul prend quelques minutes.

Problème : l'occupation mémoire. Chaque processus consommant un peu
plus d'un gigaoctet et la machine en question ayant 32 processeurs, il faut
soit que je limite le nombre de processus, soit que j'augmente la taille
mémoire. Vu le prix de la mémoire de la bête, je préfère trouver une
solution médiane... D'où l'idée d'utiliser des threads (la majeure
partie de la mémoire est utilisée par des données qui ne changent pas
d'un processus à un autre).

Pour raisons de portabilité, j'utilise gcc/gfortran/g++ sur toutes
mes architectures cibles (essentiellement du Solaris et du Linux).

La configuration de boost me renvoie :

Boost version 103700
BOOST_USER_CONFIG =<boost/config/user.hpp>
BOOST_COMPILER_CONFIG ="boost/config/compiler/gcc.hpp"
BOOST_STDLIB_CONFIG ="boost/config/stdlib/libstdcpp3.hpp"
BOOST_PLATFORM_CONFIG ="boost/config/platform/linux.hpp"
BOOST_HAS_THREADS [no value]

Je n'arrive pas à trouver d'information pertinente (des infos, j'en
ai trouvé, mais pas pertinentes, ou alors, je n'ai pas tout compris, ce
qui est fort possible...) sur le paramètre BOOST_HAS_THREADS.
D'après ce que j'ai compris, cette valeur est donnée par le couple
architecture et compilateur.

Sachant que j'utilise depuis pas mal de temps les threads sur ces
mêmes architectures sans problème, je ne vois pas trop pourquoi cette
valeur n'est pas définie.

Question : les fonctions de boost/graph sont-elles thread safe
malgré cette variable ? Je n'utilise que astar_search. Autrement dit,
puis-je appeler sans vergogne astar_search depuis plusieurs threads (sur
le même graphe bien entendu) mais avec des sommets de départ et
d'arrivée différents ?

Merci de vos lumières,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.

10 réponses

1 2 3
Avatar
JKB
Le 02-12-2008, ? propos de
Re: Threads et boost,
Michel Decima ?crivait dans fr.comp.lang.c++ :
JKB a écrit :

Que deviens edges[] apres l'initialisation du graphe ? Si tu ne le vide
pas, alors tu as deux copie des proprietes des aretes, une dans edges[],
et une dans le graphe (en interne). Ca peut etre une piste pour
economiser de la memoire.



La fonction appelante (C) fait un sauvage :

munmap((void *) edges, edges_size * size(edge_astar_t));

C'est un unmap() et non un malloc() parce que sous Solaris, free()
renvoie la mémoire à l'application et non au système.



no comment ;)


Est-ce que tu peux donner la definition de ton typedef graph_t, et
l'invocation de l'algorithme ?



Le wrapper C++ que je modifie commence par :


<snip>
Le wrapper complet est disponible à
<http://www.bertrand-lecarpentier.net/~bertrand/astar_boost_wrapper.cpp>



Je suppose que tu reecrit la fonction boost_astar ? Rien de choquant
a premiere vue, a part l'affreux graph_t statique et la gestion de
l'initialisation.



C'est fait à la hussarde, mais il y a une raison. La routine de base
faisait l'initialisation à _chaque_ appel, et cette initialisation
bouffait 90% du temps passé dans la fonction (en plus, la version de
base récupérait ses données dans une base postgresql...).

Si tu decoupe cette fonction en 3 parties (init,
calcul, recuperation des resultats) ca pourrait peut-etre marcher.



Là, j'ai un peu de mal à comprendre pourquoi en coupant la partie
calcul de la partie récupération, cela fonctionnerait mieux. Je ne vois
déjà pas trop comment séparer la partie initialisation de la partie
calcul sans refaire à chaque fois cette étape grouiguesque
d'initilisation...

(et par curiosite, pourquoi astar plutot que dijkstra ?)



Parce que les graphes sont gros.



J'avais utilise dijkstra sur des graphes a plusieurs dizaines de
milliers de noeuds, ce que je trouvais gros, et ca allait suffisamment
vite (mais je crois qu'a l'epoque l'algo astar_search n'etait pas encore
dans Boost). Bref, les tiens doivent donc etre plus gros que ca ;)



6,3 millions de points ;-)

Cordialement,


JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
Michel Decima
JKB a écrit :
Le 02-12-2008, ? propos de
Re: Threads et boost,
Michel Decima ?crivait dans fr.comp.lang.c++ :
JKB a écrit :

Que deviens edges[] apres l'initialisation du graphe ? Si tu ne le vide
pas, alors tu as deux copie des proprietes des aretes, une dans edges[],
et une dans le graphe (en interne). Ca peut etre une piste pour
economiser de la memoire.


La fonction appelante (C) fait un sauvage :

munmap((void *) edges, edges_size * size(edge_astar_t));

C'est un unmap() et non un malloc() parce que sous Solaris, free()
renvoie la mémoire à l'application et non au système.


no comment ;)


Est-ce que tu peux donner la definition de ton typedef graph_t, et
l'invocation de l'algorithme ?


Le wrapper C++ que je modifie commence par :


<snip>
Le wrapper complet est disponible à
<http://www.bertrand-lecarpentier.net/~bertrand/astar_boost_wrapper.cpp>


Je suppose que tu reecrit la fonction boost_astar ? Rien de choquant
a premiere vue, a part l'affreux graph_t statique et la gestion de
l'initialisation.



C'est fait à la hussarde, mais il y a une raison. La routine de base
faisait l'initialisation à _chaque_ appel, et cette initialisation
bouffait 90% du temps passé dans la fonction (en plus, la version de
base récupérait ses données dans une base postgresql...).

Si tu decoupe cette fonction en 3 parties (init,
calcul, recuperation des resultats) ca pourrait peut-etre marcher.



Là, j'ai un peu de mal à comprendre pourquoi en coupant la partie
calcul de la partie récupération, cela fonctionnerait mieux. Je ne vois
déjà pas trop comment séparer la partie initialisation de la partie
calcul sans refaire à chaque fois cette étape grouiguesque
d'initilisation...



Parce que l'initialisation est faite en fonction de la valeur de la
variable statique 'initialization', et que cette variable est lue et
modifiee sans protection, donc depuis plusieurs threads ca risque d'etre
pas beau a voir.

Je suppose que si tu passe par ce wrapper, c'est que le code appelant
est en C ? Dans ce cas tu pourrais envisager une interface comme ceci :

// type abstrait qui pointe vers une structure contenant le graphe
// (definie dans le .cpp)
//
typedef void* boost_astar_graph ;

// allocation de la structure, et remplissage du graphe avec les
// donnees d'entree. Sera appellee une seule fois dans le thread
// principal.
//
boost_astar_graph* boost_astar_create( edge_astar_t*, etc... ) ;

// encapsule l'appel a l'algorithme et l'extraction des resultats,
// sans modifier la structure g. Devrait pouvoir etre appelee par
// plusieurs threads simultanement.
//
int boost_astar_search( boost_astar_graph* g, source, target,
result, ... ) ;

// liberation des ressources
void boost_astar_destroy( boost_astar_graph* g ) ;

(et par curiosite, pourquoi astar plutot que dijkstra ?)


Parce que les graphes sont gros.


J'avais utilise dijkstra sur des graphes a plusieurs dizaines de
milliers de noeuds, ce que je trouvais gros, et ca allait suffisamment
vite (mais je crois qu'a l'epoque l'algo astar_search n'etait pas encore
dans Boost). Bref, les tiens doivent donc etre plus gros que ca ;)



6,3 millions de points ;-)



Effectivement, ca fait beaucoup (aujourd'hui sur fclc++, concours de
celui qui a le plus gros, vainqueur: JKB).
Plus serieusement, l'algo utilise au moins 3 proprietes de travail
(distance_map, color_map et rank_map) qui doivent etre allouees
localement soit par l'algo lui-meme, soit par le wrapper. Si elles
portent sur les noeuds, ca veut dire allocation/liberation de 3
tableaux de 6 million d'entree a chaque appel, par thread. Si c'est
des proprietes sur les aretes, ca sera pire.
Avatar
JKB
Le 02-12-2008, ? propos de
Re: Threads et boost,
Michel Decima ?crivait dans fr.comp.lang.c++ :
JKB a écrit :
Là, j'ai un peu de mal à comprendre pourquoi en coupant la partie
calcul de la partie récupération, cela fonctionnerait mieux. Je ne vois
déjà pas trop comment séparer la partie initialisation de la partie
calcul sans refaire à chaque fois cette étape grouiguesque
d'initilisation...



Parce que l'initialisation est faite en fonction de la valeur de la
variable statique 'initialization', et que cette variable est lue et
modifiee sans protection, donc depuis plusieurs threads ca risque d'etre
pas beau a voir.



D'accord. Je n'aurais pas ce problème vu l'utilisation que je fais
de la fonction (mais il est vrai qu'un mutex autour du truc, ça ne
nuirait pas beaucoup).

Je suppose que si tu passe par ce wrapper, c'est que le code appelant
est en C ? Dans ce cas tu pourrais envisager une interface comme ceci :



Gagné ;-)

// type abstrait qui pointe vers une structure contenant le graphe
// (definie dans le .cpp)
//
typedef void* boost_astar_graph ;

// allocation de la structure, et remplissage du graphe avec les
// donnees d'entree. Sera appellee une seule fois dans le thread
// principal.
//
boost_astar_graph* boost_astar_create( edge_astar_t*, etc... ) ;

// encapsule l'appel a l'algorithme et l'extraction des resultats,
// sans modifier la structure g. Devrait pouvoir etre appelee par
// plusieurs threads simultanement.
//
int boost_astar_search( boost_astar_graph* g, source, target,
result, ... ) ;

// liberation des ressources
void boost_astar_destroy( boost_astar_graph* g ) ;



Bon, je vais faire des tests, je ne savais pas ce que j'allais faire
dans les prochains jours ;-) Je reviendrai vous donner les résultats
d'ici quelques temps.

6,3 millions de points ;-)



Effectivement, ca fait beaucoup (aujourd'hui sur fclc++, concours de
celui qui a le plus gros, vainqueur: JKB).



Franchement ? Il s'agit de la cartographie française (uniquement les
routes empruntables par un véhicule à moteur).

Plus serieusement, l'algo utilise au moins 3 proprietes de travail
(distance_map, color_map et rank_map) qui doivent etre allouees
localement soit par l'algo lui-meme, soit par le wrapper. Si elles
portent sur les noeuds, ca veut dire allocation/liberation de 3
tableaux de 6 million d'entree a chaque appel, par thread. Si c'est
des proprietes sur les aretes, ca sera pire.



Je me souviens avoir essayé l'algorithme de base et l'avoir rejeté
pour une bonne raison, mais je ne me souviens plus de cette raison :-(

Merci pour tous ces tuyaux,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
Michel Decima
JKB a écrit :

6,3 millions de points ;-)


Effectivement, ca fait beaucoup (aujourd'hui sur fclc++, concours de
celui qui a le plus gros, vainqueur: JKB).



Franchement ? Il s'agit de la cartographie française (uniquement les
routes empruntables par un véhicule à moteur).



Moi je jouais avec les interconnexions BGP de systemes autonomes (AS).
Donc un max théorique de 65535 points, et pas beaucoup d'aretes. Bref,
un monde vraiment beaucoup plus petit...
Avatar
JKB
Le 02-12-2008, ? propos de
Re: Threads et boost,
Michel Decima ?crivait dans fr.comp.lang.c++ :
JKB a écrit :

6,3 millions de points ;-)


Effectivement, ca fait beaucoup (aujourd'hui sur fclc++, concours de
celui qui a le plus gros, vainqueur: JKB).



Franchement ? Il s'agit de la cartographie française (uniquement les
routes empruntables par un véhicule à moteur).



Moi je jouais avec les interconnexions BGP de systemes autonomes (AS).
Donc un max théorique de 65535 points, et pas beaucoup d'aretes. Bref,
un monde vraiment beaucoup plus petit...



Par dessus, j'ai une couche d'optimisation dans un espace pas joli
du tout, avec une fonction de coût ni continue ni dérivable même par
morceau. Problème NP-complet fabuleux (et noeud au cerveau garanti ;-)).

Cordialement,

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
Michel Decima
JKB a écrit :
Le 02-12-2008, ? propos de
Re: Threads et boost,
Michel Decima ?crivait dans fr.comp.lang.c++ :
JKB a écrit :

6,3 millions de points ;-)


Effectivement, ca fait beaucoup (aujourd'hui sur fclc++, concours de
celui qui a le plus gros, vainqueur: JKB).


Franchement ? Il s'agit de la cartographie française (uniquement les
routes empruntables par un véhicule à moteur).


Moi je jouais avec les interconnexions BGP de systemes autonomes (AS).
Donc un max théorique de 65535 points, et pas beaucoup d'aretes. Bref,
un monde vraiment beaucoup plus petit...



Par dessus, j'ai une couche d'optimisation dans un espace pas joli
du tout, avec une fonction de coût ni continue ni dérivable même par
morceau. Problème NP-complet fabuleux (et noeud au cerveau garanti ;-)).



Effectivement, ca fait envie...
Avatar
JKB
Le 03-12-2008, ? propos de
Re: Threads et boost,
Michael DOUBEZ ?crivait dans fr.comp.lang.c++ :
Michel Decima a écrit :
JKB a écrit :
Le 02-12-2008, ? propos de
Re: Threads et boost,
Michel Decima ?crivait dans fr.comp.lang.c++ :
JKB a écrit :

6,3 millions de points ;-)


Effectivement, ca fait beaucoup (aujourd'hui sur fclc++, concours
de celui qui a le plus gros, vainqueur: JKB).


Franchement ? Il s'agit de la cartographie française (uniquement
les routes empruntables par un véhicule à moteur).


Moi je jouais avec les interconnexions BGP de systemes autonomes
(AS). Donc un max théorique de 65535 points, et pas beaucoup
d'aretes. Bref, un monde vraiment beaucoup plus petit...



Par dessus, j'ai une couche d'optimisation dans un espace pas joli
du tout, avec une fonction de coût ni continue ni dérivable même par
morceau. Problème NP-complet fabuleux (et noeud au cerveau garanti ;-)).



Effectivement, ca fait envie...



Je ne connais pas ce domaine mais je suis surpris qu'il n'y ait pas
d'approche divide&conquer pour éviter de tout charger en mémoire.



Disons que le problème au départ consiste à avoir une approche
globale (et non sectorisée) et que pour une question d'architecture
matérielle, il vaut mieux tout avoir en mémoire.

Moi ça me fait pas envie, je suis content de pas être à ta place :p



Merci de votre soutien ;-)

JKB

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
Avatar
Michael DOUBEZ
Michel Decima a écrit :
JKB a écrit :
Le 02-12-2008, ? propos de
Re: Threads et boost,
Michel Decima ?crivait dans fr.comp.lang.c++ :
JKB a écrit :

6,3 millions de points ;-)


Effectivement, ca fait beaucoup (aujourd'hui sur fclc++, concours
de celui qui a le plus gros, vainqueur: JKB).


Franchement ? Il s'agit de la cartographie française (uniquement
les routes empruntables par un véhicule à moteur).


Moi je jouais avec les interconnexions BGP de systemes autonomes
(AS). Donc un max théorique de 65535 points, et pas beaucoup
d'aretes. Bref, un monde vraiment beaucoup plus petit...



Par dessus, j'ai une couche d'optimisation dans un espace pas joli
du tout, avec une fonction de coût ni continue ni dérivable même par
morceau. Problème NP-complet fabuleux (et noeud au cerveau garanti ;-)).



Effectivement, ca fait envie...



Je ne connais pas ce domaine mais je suis surpris qu'il n'y ait pas
d'approche divide&conquer pour éviter de tout charger en mémoire.

Moi ça me fait pas envie, je suis content de pas être à ta place :p

--
Michael
Avatar
Michel Decima
JKB a écrit :
Le 03-12-2008, ? propos de
Re: Threads et boost,
Michael DOUBEZ ?crivait dans fr.comp.lang.c++ :



Je ne connais pas ce domaine mais je suis surpris qu'il n'y ait pas
d'approche divide&conquer pour éviter de tout charger en mémoire.



Disons que le problème au départ consiste à avoir une approche
globale (et non sectorisée) et que pour une question d'architecture
matérielle, il vaut mieux tout avoir en mémoire.



Est-ce que tu as regardé les classe subgraph et filtered_graph ? Ca te
permet de creer des "vues" sur un graphe existant, sans copier le
contenu (donc tu n'as qu'une seule representation physique du graphe,
et en particulier des proprietes internes).

Ca peut permettre de construire le sous-graphe des départementales sur
la zone de depart et d'arrivee, et le sous-graphe des autoroutes sur
l'ensemble des territoires, par exemple.

Mais bon, tout depend de l'approche choisie, c'est pas forcement
utilisable. Et dans mon souvenir, il y a des pieges subtils sur
l'acces aux proprietes, parce que les edge_descriptor du sous-graphe
ne sont plus des entiers consecutifs (ou qqch comme ca).
Avatar
JKB
Le 03-12-2008, ? propos de
Re: Threads et boost,
Michel Decima ?crivait dans fr.comp.lang.c++ :
JKB a écrit :
Le 03-12-2008, ? propos de
Re: Threads et boost,
Michael DOUBEZ ?crivait dans fr.comp.lang.c++ :



Je ne connais pas ce domaine mais je suis surpris qu'il n'y ait pas
d'approche divide&conquer pour éviter de tout charger en mémoire.



Disons que le problème au départ consiste à avoir une approche
globale (et non sectorisée) et que pour une question d'architecture
matérielle, il vaut mieux tout avoir en mémoire.



Est-ce que tu as regardé les classe subgraph et filtered_graph ? Ca te
permet de creer des "vues" sur un graphe existant, sans copier le
contenu (donc tu n'as qu'une seule representation physique du graphe,
et en particulier des proprietes internes).

Ca peut permettre de construire le sous-graphe des départementales sur
la zone de depart et d'arrivee, et le sous-graphe des autoroutes sur
l'ensemble des territoires, par exemple.

Mais bon, tout depend de l'approche choisie, c'est pas forcement
utilisable. Et dans mon souvenir, il y a des pieges subtils sur
l'acces aux proprietes, parce que les edge_descriptor du sous-graphe
ne sont plus des entiers consecutifs (ou qqch comme ca).



Merci de vous intéresser au problème, mais je n'ai pas besoin de ce
genre de fonctions. Je ne m'intéresse qu'aux voies carrossables quelles
que soient ces voies. Le but est d'aller le plus vite possible d'un
point à un autre sur l'ensemble d'un pays. Comme ce calcul d'itinéraire
n'est qu'un pouillème du calcul final, je m'attache déjà à optimiser le
reste du calcul avant de rentrer dans de telles considérations.

Au passage, j'ai essayé de ressortir le graphe de la fonction
astar_search sans succès (g++ me renvoie une erreur au niveau de l'appel
de la fonction C++ depuis mes routines C). J'ai donc gardé mon graphe
statique en collant au niveau de l'appel, dans ma routine C, un mutex
pour éviter des problèmes d'initialisations concurrentes. C'est un peu
goret-codé, mais je me pencherai sur le problème lorsque j'aurai un peu
plus de temps devant moi...

Cordialement,

JKB, qui retourne à ses problèmes de threads

--
Le cerveau, c'est un véritable scandale écologique. Il représente 2% de notre
masse corporelle, mais disperse à lui seul 25% de l'énergie que nous
consommons tous les jours.
1 2 3