Commit 155192b0 authored by Martin Khannouz's avatar Martin Khannouz Committed by Berenger Bramas

Add function to compute execution time and a presentation in orgmode.

parent 2f0d1ef4
This diff is collapsed.
#+TITLE: Usage implicit de MPI dans ScalFmm
#+AUTHOR: Martin Khannouz
#+LANGUAGE: fr
#+STARTUP: inlineimages
#+OPTIONS: H:3 num:t toc:t \n:nil @:t ::t |:t ^:nil -:t f:t *:t <:t
#+OPTIONS: TeX:t LaTeX:t skip:nil d:nil todo:nil pri:nil tags:not-in-toc
#+EXPORT_SELECT_TAGS: export
#+EXPORT_EXCLUDE_TAGS: noexport
#+TAGS: noexport(n)
# #+BEGIN_SRC sh
# export SCALFMM_DIR=/home/mkhannou/scalfmm
# cd $SCALFMM_DIR
# git checkout mpi_implicit
# spack install scalfmm@src+mpi+starpu \^starpu@svn-trunk+mpi+fxt \^openmpi
# #+END_SRC
* Implémentation mpi implicite très naïve
Cette première version avait pour principal but de découvrir et à prendre en main les fonctions de StarPU MPI.
Les premières étant starpu_mpi_init et starpu_mpi_shutdown. Mais rapidement ont suivies les fonctions pour /tagger/ les /handles/ de StarPU et les ajouter à des nœuds MPI.
À cela c'est ajouté la transformation de tous les appels à starpu_insert_task ou starpu_task_submit par starpu_mpi_insert_task.
Par soucis de simplicité chaque nœud MPI possède l'intégralité de l'arbre, même si ce n'est pas une solution viable sur le long terme.
Pour vérifier que tout fonctionnait correctement, je me suis amusé à /mapper/ toutes les données sur un premier nœud MPI et toutes les tâches sur un second.
J'ai ensuite pu valider que l'arbre du premier nœud avait les bons résultats et que le second nœud n'avait que des erreurs.
* Implémentation moins naïve
Dans l'idée de créer une version 0 un brin potable qui puisse faire du calcul avec plus de deux nœuds MPI, j'ai créé une fonction de /mapping/ des données.
Elle consistait à partager chaque niveau entre tous les processus de la manière la plus équitable possible.
#+CAPTION: Division de chaque niveau entre chaque processus. Groupe de l'arbre de taille 4.
[[./naive_split.png]]
* Reproduction du mapping mpi explicite
Pour pouvoir effectuer des comparaisons il était nécessaire de reproduire le même /mapping/ de tâches la version MPI explicite.
Dans le cas de la version implicite telle qu'elle est actuellement implémentée, le /mapping/ des données infère le /mapping/ de tâches.
La façon la plus simple de procéder est de faire en sorte que les particules se retrouvent sur les mêmes nœuds MPI.
** Premier problème des groupes
La disposition des particules sur les nœuds MPI étant décidé par un tri distribué, il était plus judicieux de sauvegarder le /mapping/ des particules dans un fichier puis de le charger (dans la version implicite) et d'utiliser ce /mapping/ pour influer le /mapping/ au niveau de la version implicite.
Le soucis du tri distribué est qu'il essaye d'équilibrer les particules sur les nœuds sans tenir compte des groupes de l'arbre groupé (/group tree/).
#+CAPTION: Problème issuent de la constitution des groupes.
#+NAME: fig:SED-HR4049
[[./group_issue1.png]]
Or le /mapping/ des données est fait avec la granularité des groupes de l'arbre groupé.
Une première solution serait de modifier un peu l'algorithme de l'arbre pour le forcer à faire des groupes un peu plus petit de telle sorte qu'ils correspondent aux groupes de la version MPI explicite.
Soucis, quand il faudra remonter dans l'arbre, que faire des cellules qui sont présentes sur plusieurs nœuds MPI, que faire de la racine ?
** Solution retenue
Plutôt que d'essayer de reproduire un /mapping/ de données identique à celui de la version explicite quel que soit les particules, nous avons choisi de limiter le nombre de cas reproductibles et de ségmenter ce /mapping/ par niveau.
Ainsi avec un arbre parfait où chaque indice de morton possède le même nombre de particules, il est possible de reproduire le même /mapping/ de données sur un certain de nombre de niveaux.
Ce nombre varie en fonction de la taille des groupes de l'arbre groupé.
#+CAPTION: Méthode pour générer une particule à un indice de Morton donné.
#+NAME: fig:SED-HR4049
[[./morton_box_center.png]]
** Validation des résultats
Pour valider ces résultats, j'ai réutilisé le système de nom de tâches fait pour simgrid. Ainsi j'ai pu indiquer, dans un fichier des informations à propos de chaque tâche.
Les indices de Morton ainsi que les nœuds MPI sur lesquels elles s'exécutent.
*** Observation
Si l'on fait exception des niveaux où l'on sait que des erreurs de tâches se trouveront, on a :
- Plus de tâches dans la version explicite car elle a des tâches (P2P, M2L) symetriques.
- Toutes les tâches issuent de l'algorithme implicite se retrouvent dans l'ensemble des tâches explicite.
- Toutes les tâches sont au moins mapper sur le même nœud MPI. Les tâches symetriques étant parfois mappé sur deux nœuds différents.
* Et après ?
- Comparaison des performances
- Répartition des GFlop
- Répartition du temps de calcul
- Mémoire utilisée par nœud
- Étude d'autres /mapping/
- Limiter l'empreinte mémoire
- Ne pas allouer les cellules numériques si ce n'est pas necessaire (/up/ et /down/)
- Ne pas allouer les cellules symboliques si ce n'est pas necessaire
- Distribuer l'arbre
...@@ -192,8 +192,6 @@ public: ...@@ -192,8 +192,6 @@ public:
taskNames = new FStarPUTaskNameParams(mpi_rank, nproc); taskNames = new FStarPUTaskNameParams(mpi_rank, nproc);
#endif #endif
#endif #endif
cout << mpi_rank << "/" << nproc << endl;
starpu_malloc_set_align(32); starpu_malloc_set_align(32);
starpu_pthread_mutex_t initMutex; starpu_pthread_mutex_t initMutex;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <deque> #include <deque>
#include <unordered_set> #include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <omp.h>
using namespace std; using namespace std;
#include "../../Src/Utils/FGlobal.hpp" #include "../../Src/Utils/FGlobal.hpp"
...@@ -49,6 +50,7 @@ struct Task ...@@ -49,6 +50,7 @@ struct Task
cout << taskNames[type]; cout << taskNames[type];
for(size_t i = 0; i < id.size(); ++i) for(size_t i = 0; i < id.size(); ++i)
cout << ", " << id[i]; cout << ", " << id[i];
cout << "(mpi " << mpiNode << ")";
cout << endl; cout << endl;
} }
}; };
...@@ -66,6 +68,7 @@ struct DagData ...@@ -66,6 +68,7 @@ struct DagData
{ {
unordered_set<Task> allTask; unordered_set<Task> allTask;
unordered_map<long long int, double> performence; unordered_map<long long int, double> performence;
int treeHeight;
}; };
bool parseLine(DagData & dagData, deque<string> & lineElements) bool parseLine(DagData & dagData, deque<string> & lineElements)
...@@ -84,6 +87,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) ...@@ -84,6 +87,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements)
task.id[2] = stoll(lineElements[11]); task.id[2] = stoll(lineElements[11]);
task.id[3] = stoll(lineElements[12]); task.id[3] = stoll(lineElements[12]);
task.mpiNode = stoi(lineElements[13]); task.mpiNode = stoi(lineElements[13]);
task.level = dagData.treeHeight - 1;
dagData.allTask.insert(task); dagData.allTask.insert(task);
} }
else if(lineElements.size() >= 10 && lineElements[0] == "P2P") else if(lineElements.size() >= 10 && lineElements[0] == "P2P")
...@@ -99,6 +103,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) ...@@ -99,6 +103,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements)
if(task.id[0] == 0 && task.id[1] == 0 && task.id[2] == 0 && task.id[3] == 0) if(task.id[0] == 0 && task.id[1] == 0 && task.id[2] == 0 && task.id[3] == 0)
cout << "Suricate" << endl; cout << "Suricate" << endl;
task.mpiNode = stoi(lineElements[9]); task.mpiNode = stoi(lineElements[9]);
task.level = dagData.treeHeight - 1;
dagData.allTask.insert(task); dagData.allTask.insert(task);
} }
else if(lineElements.size() >= 10 && lineElements[0] == "M2L" ) else if(lineElements.size() >= 10 && lineElements[0] == "M2L" )
...@@ -113,6 +118,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) ...@@ -113,6 +118,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements)
task.id[3] = stoll(lineElements[7]); task.id[3] = stoll(lineElements[7]);
task.id[4] = stoll(lineElements[8]); task.id[4] = stoll(lineElements[8]);
task.mpiNode = stoi(lineElements[9]); task.mpiNode = stoi(lineElements[9]);
task.level = task.id[0];
dagData.allTask.insert(task); dagData.allTask.insert(task);
} }
else if(lineElements.size() >= 13 && lineElements[0] == "M2L_out") else if(lineElements.size() >= 13 && lineElements[0] == "M2L_out")
...@@ -127,6 +133,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) ...@@ -127,6 +133,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements)
task.id[3] = stoll(lineElements[10]); task.id[3] = stoll(lineElements[10]);
task.id[4] = stoll(lineElements[11]); task.id[4] = stoll(lineElements[11]);
task.mpiNode = stoi(lineElements[12]); task.mpiNode = stoi(lineElements[12]);
task.level = task.id[0];
dagData.allTask.insert(task); dagData.allTask.insert(task);
} }
else if(lineElements.size() >= 13 && lineElements[0] == "M2M") else if(lineElements.size() >= 13 && lineElements[0] == "M2M")
...@@ -141,6 +148,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) ...@@ -141,6 +148,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements)
task.id[3] = stoll(lineElements[10]); task.id[3] = stoll(lineElements[10]);
task.id[4] = stoll(lineElements[11]); task.id[4] = stoll(lineElements[11]);
task.mpiNode = stoi(lineElements[12]); task.mpiNode = stoi(lineElements[12]);
task.level = task.id[0];
dagData.allTask.insert(task); dagData.allTask.insert(task);
} }
else if(lineElements.size() >= 13 && lineElements[0] == "L2L") else if(lineElements.size() >= 13 && lineElements[0] == "L2L")
...@@ -155,6 +163,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) ...@@ -155,6 +163,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements)
task.id[3] = stoll(lineElements[10]); task.id[3] = stoll(lineElements[10]);
task.id[4] = stoll(lineElements[11]); task.id[4] = stoll(lineElements[11]);
task.mpiNode = stoi(lineElements[12]); task.mpiNode = stoi(lineElements[12]);
task.level = task.id[0];
dagData.allTask.insert(task); dagData.allTask.insert(task);
} }
else if(lineElements.size() >= 8 && lineElements[0] == "L2P") else if(lineElements.size() >= 8 && lineElements[0] == "L2P")
...@@ -166,6 +175,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) ...@@ -166,6 +175,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements)
task.id[0] = stoll(lineElements[5]); task.id[0] = stoll(lineElements[5]);
task.id[1] = stoll(lineElements[6]); task.id[1] = stoll(lineElements[6]);
task.mpiNode = stoi(lineElements[7]); task.mpiNode = stoi(lineElements[7]);
task.level = dagData.treeHeight - 1;
dagData.allTask.insert(task); dagData.allTask.insert(task);
} }
else if(lineElements.size() >= 8 && lineElements[0] == "P2M") else if(lineElements.size() >= 8 && lineElements[0] == "P2M")
...@@ -177,6 +187,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) ...@@ -177,6 +187,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements)
task.id[0] = stoll(lineElements[5]); task.id[0] = stoll(lineElements[5]);
task.id[1] = stoll(lineElements[6]); task.id[1] = stoll(lineElements[6]);
task.mpiNode = stoi(lineElements[7]); task.mpiNode = stoi(lineElements[7]);
task.level = 0;
dagData.allTask.insert(task); dagData.allTask.insert(task);
} }
else else
...@@ -268,37 +279,91 @@ void fillPerformanceData(const char* const filename, DagData & dagData) ...@@ -268,37 +279,91 @@ void fillPerformanceData(const char* const filename, DagData & dagData)
} }
fichier.close(); // on ferme le fichier fichier.close(); // on ferme le fichier
} }
void compareDag(DagData& dag1, DagData& dag2, int treeHeight) void compareDag(DagData const& dag1, DagData const& dag2, int const treeHeight)
{ {
long long int notFoundCount[treeHeight] = {0}; #pragma omp parallel
bool notFound[treeHeight] = {false};
for(Task task : dag1.allTask)
{ {
bool found = false; #pragma omp single nowait
if(task.type == P2P || task.type == P2P_OUT || task.type == P2M || task.type == L2P)
notFound[treeHeight-1] = true;
else if(task.id[0] < treeHeight)
++notFound[task.id[0]] = true;
for(Task task2 : dag2.allTask)
{ {
if(task == task2) long long int notFoundCount[omp_get_num_threads()][treeHeight];
long long int differenceMapping[omp_get_num_threads()][treeHeight];
long long int taskCount[omp_get_num_threads()][treeHeight];
for(int i = 0; i < omp_get_num_threads(); ++i)
{ {
found = true; for(int j = 0; j < treeHeight; ++j)
break; {
notFoundCount[i][j] = 0;
taskCount[i][j] = 0;
differenceMapping[i][j] = 0;
}
}
for(Task task : dag1.allTask)
{
#pragma omp task default(shared) firstprivate(task)
{
bool found = false;
Task sameTask[2];
int sameTaskId = 0;
if(task.level < treeHeight)
++taskCount[omp_get_thread_num()][task.level];
for(auto it = dag2.allTask.begin(); it != dag2.allTask.end(); ++it)
{
if(task == *it)
{
sameTask[sameTaskId++] = *it;
found = true;
if(sameTaskId == 2)
break;
}
}
if(found == false)
{
//task.print();
if(task.level < treeHeight)
++notFoundCount[omp_get_thread_num()][task.level];
}
else
{
bool sameNode = false;
for(int i = 0; i < sameTaskId; ++i)
if(sameTask[i].mpiNode == task.mpiNode)
sameNode = true;
if(!sameNode)
{
#pragma omp critical
{
task.print();
sameTask[0].print();//Il y a au moins une tâche identique trouvée
if(sameTaskId == 2)
sameTask[1].print();//Il y a au moins une tâche identique trouvée
cout << sameTaskId << endl;
cout << endl;
}
if(task.level < treeHeight)
++differenceMapping[omp_get_thread_num()][task.level];
}
}
}
}
#pragma omp taskwait
for(int i = 0; i < treeHeight; ++i)
{
long long int sum = 0;
long long int sumDiffMapping = 0;
long long int sumTaskCount = 0;
for(int j = 0; j < omp_get_num_threads(); ++j)
if(taskCount[j][i] > 0)
{
sum += notFoundCount[j][i];
sumDiffMapping += differenceMapping[j][i];
sumTaskCount += taskCount[j][i];
}
if(sum > 0 || sumDiffMapping > 0)
std::cout << "Diff lvl " << i << " -> " << sum << " (Mapping error : " << sumDiffMapping << "/" << sumTaskCount << ")" << std::endl;
} }
}
if(found == false)
{
task.print();
if(task.type == P2P || task.type == P2P_OUT || task.type == P2M || task.type == L2P)
++notFoundCount[treeHeight-1];
else
++notFoundCount[task.id[0]];
} }
} }
for(int i = 0; i < treeHeight; ++i)
if(notFound[i] == true)
cout << "Diff lvl " << i << " -> " << notFoundCount[i] << endl;
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
...@@ -334,10 +399,12 @@ int main(int argc, char* argv[]) ...@@ -334,10 +399,12 @@ int main(int argc, char* argv[])
DagData implicitData, explicitData; DagData implicitData, explicitData;
bool implicitGood, explicitGood; bool implicitGood, explicitGood;
std::thread explicitThread([&](){ std::thread explicitThread([&](){
explicitData.treeHeight = treeHeight;
fillPerformanceData(explicitTraceFilename, explicitData); fillPerformanceData(explicitTraceFilename, explicitData);
explicitGood = fillDagData(explicitFilename, explicitData); explicitGood = fillDagData(explicitFilename, explicitData);
}); });
std::thread implicitThread([&](){ std::thread implicitThread([&](){
implicitData.treeHeight = treeHeight;
fillPerformanceData(implicitTraceFilename, implicitData); fillPerformanceData(implicitTraceFilename, implicitData);
implicitGood = fillDagData(implicitFilename, implicitData); implicitGood = fillDagData(implicitFilename, implicitData);
}); });
...@@ -347,7 +414,7 @@ int main(int argc, char* argv[]) ...@@ -347,7 +414,7 @@ int main(int argc, char* argv[])
{ {
cout << explicitData.allTask.size() << " tasks in explicit." << endl; cout << explicitData.allTask.size() << " tasks in explicit." << endl;
cout << implicitData.allTask.size() << " tasks in implicit." << endl; cout << implicitData.allTask.size() << " tasks in implicit." << endl;
compareDag(explicitData, implicitData, treeHeight); compareDag(implicitData, explicitData, treeHeight);
} }
return 0; return 0;
} }
...@@ -39,6 +39,7 @@ using namespace std; ...@@ -39,6 +39,7 @@ using namespace std;
#include "../../Src/Core/FFmmAlgorithm.hpp" #include "../../Src/Core/FFmmAlgorithm.hpp"
std::vector<MortonIndex> getMortonIndex(const char* const mapping_filename); std::vector<MortonIndex> getMortonIndex(const char* const mapping_filename);
void timeAverage(int mpi_rank, int nproc, double elapsedTime);
int main(int argc, char* argv[]){ int main(int argc, char* argv[]){
setenv("STARPU_NCPU","1",1); setenv("STARPU_NCPU","1",1);
...@@ -131,18 +132,22 @@ int main(int argc, char* argv[]){ ...@@ -131,18 +132,22 @@ int main(int argc, char* argv[]){
// Run the algorithm // Run the algorithm
GroupKernelClass groupkernel; GroupKernelClass groupkernel;
GroupAlgorithm groupalgo(&groupedTree,&groupkernel, distributedMortonIndex); GroupAlgorithm groupalgo(&groupedTree,&groupkernel, distributedMortonIndex);
FTic timerExecute;
groupalgo.execute(); groupalgo.execute();
double elapsedTime = timerExecute.tacAndElapsed();
cout << "Executing time (implicit node " << groupalgo.getRank() << ") " << elapsedTime << "s\n";
timeAverage(groupalgo.getRank(), groupalgo.getNProc(), elapsedTime);
// Usual algorithm // Usual algorithm
KernelClass kernels; // FTestKernels FBasicKernels KernelClass kernels; // FTestKernels FBasicKernels
FmmClass algo(&tree,&kernels); //FFmmAlgorithm FFmmAlgorithmThread FmmClass algo(&tree,&kernels); //FFmmAlgorithm FFmmAlgorithmThread
algo.execute(); algo.execute();
int rank = groupalgo.getRank(); int rank = groupalgo.getRank();
for(int i = 0; i < groupedTree.getHeight(); ++i) for(int i = 2; i < groupedTree.getHeight(); ++i)//No task at level 0 and 1
{ {
if(groupedTree.getNbCellGroupAtLevel(i) < groupalgo.getNProc() && rank == 0) if(groupedTree.getNbCellGroupAtLevel(i) < groupalgo.getNProc() && rank == 0)
std::cout << "Error at level " << i << std::endl; std::cout << "Error at level " << i << std::endl;
} }
return 0;
// Validate the result // Validate the result
for(int idxLevel = 2 ; idxLevel < groupedTree.getHeight() ; ++idxLevel){ for(int idxLevel = 2 ; idxLevel < groupedTree.getHeight() ; ++idxLevel){
for(int idxGroup = 0 ; idxGroup < groupedTree.getNbCellGroupAtLevel(idxLevel) ; ++idxGroup){ for(int idxGroup = 0 ; idxGroup < groupedTree.getNbCellGroupAtLevel(idxLevel) ; ++idxGroup){
...@@ -217,3 +222,22 @@ std::vector<MortonIndex> getMortonIndex(const char* const mapping_filename) ...@@ -217,3 +222,22 @@ std::vector<MortonIndex> getMortonIndex(const char* const mapping_filename)
cerr << "Impossible d'ouvrir le fichier !" << endl; cerr << "Impossible d'ouvrir le fichier !" << endl;
return ret; return ret;
} }
void timeAverage(int mpi_rank, int nproc, double elapsedTime)
{
if(mpi_rank == 0)
{
double sumElapsedTime = elapsedTime;
for(int i = 1; i < nproc; ++i)
{
double tmp;
MPI_Recv(&tmp, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, 0);
sumElapsedTime += tmp;
}
sumElapsedTime = sumElapsedTime / (double)nproc;
std::cout << "Average time per node : " << sumElapsedTime << "s" << std::endl;
}
else
{
MPI_Send(&elapsedTime, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
}
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
void timeAverage(int mpi_rank, int nproc, double elapsedTime);
int main(int argc, char* argv[]){ int main(int argc, char* argv[]){
const FParameterNames LocalOptionBlocSize { const FParameterNames LocalOptionBlocSize {
...@@ -128,15 +129,13 @@ int main(int argc, char* argv[]){ ...@@ -128,15 +129,13 @@ int main(int argc, char* argv[]){
mpiComm.global().processId()+1, 0, mpiComm.global().processId()+1, 0,
mpiComm.global().getComm()), __LINE__); mpiComm.global().getComm()), __LINE__);
} }
FLOG(std::cout << "My last index is " << leftLimite << "\n");
FLOG(std::cout << "My left limite is " << myLeftLimite << "\n");
for(int i = 0; i < mpiComm.global().processCount(); ++i) for(int i = 0; i < mpiComm.global().processCount(); ++i)
{ {
if(i == mpiComm.global().processId()) if(i == mpiComm.global().processId())
{ {
std::cout << "My last index is (" << mpiComm.global().processId() << ") " << leftLimite << "\n"; FLOG(std::cout << "My last index is (" << mpiComm.global().processId() << ") " << leftLimite << "\n");
std::cout << "My left limite is (" << mpiComm.global().processId() << ") " << myLeftLimite << "\n"; FLOG(std::cout << "My left limite is (" << mpiComm.global().processId() << ") " << myLeftLimite << "\n");
std::cout << "Size (" << mpiComm.global().processId() << ") " << allParticles.getNbParticles() << "\n"; FLOG(std::cout << "Size (" << mpiComm.global().processId() << ") " << allParticles.getNbParticles() << "\n");
} }
mpiComm.global().barrier(); mpiComm.global().barrier();
} }
...@@ -144,15 +143,16 @@ int main(int argc, char* argv[]){ ...@@ -144,15 +143,16 @@ int main(int argc, char* argv[]){
// Put the data into the tree // Put the data into the tree
GroupOctreeClass groupedTree(NbLevels, loader.getBoxWidth(), loader.getCenterOfBox(), groupSize, GroupOctreeClass groupedTree(NbLevels, loader.getBoxWidth(), loader.getCenterOfBox(), groupSize,
&allParticles, true, leftLimite); &allParticles, true, leftLimite);
//groupedTree.printInfoBlocks();
// Run the algorithm // Run the algorithm
GroupKernelClass groupkernel; GroupKernelClass groupkernel;
GroupAlgorithm groupalgo(mpiComm.global(), &groupedTree,&groupkernel); GroupAlgorithm groupalgo(mpiComm.global(), &groupedTree,&groupkernel);
FTic timerExecute;
groupalgo.execute(); groupalgo.execute();
double elapsedTime = timerExecute.tacAndElapsed();
std::cout << "Wait Others... " << std::endl; std::cout << "Executing time (explicit node " << mpiComm.global().processId() << ") " << elapsedTime << "s\n";
mpiComm.global().barrier(); mpiComm.global().barrier();
timeAverage(mpiComm.global().processId(), mpiComm.global().processCount(), elapsedTime);
groupedTree.forEachCellLeaf<GroupContainerClass>([&](GroupCellClass cell, GroupContainerClass* leaf){ groupedTree.forEachCellLeaf<GroupContainerClass>([&](GroupCellClass cell, GroupContainerClass* leaf){
const FSize nbPartsInLeaf = leaf->getNbParticles(); const FSize nbPartsInLeaf = leaf->getNbParticles();
...@@ -209,4 +209,22 @@ int main(int argc, char* argv[]){ ...@@ -209,4 +209,22 @@ int main(int argc, char* argv[]){
return 0; return 0;
} }
void timeAverage(int mpi_rank, int nproc, double elapsedTime)
{
if(mpi_rank == 0)
{
double sumElapsedTime = elapsedTime;
for(int i = 1; i < nproc; ++i)
{
double tmp;
MPI_Recv(&tmp, 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, 0);
sumElapsedTime += tmp;
}
sumElapsedTime = sumElapsedTime / (double)nproc;
std::cout << "Average time per node : " << sumElapsedTime << "s" << std::endl;
}
else
{
MPI_Send(&elapsedTime, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
}
}
#!/bin/sh #!/bin/sh
export SCALFMM_SIMGRIDOUT='scalfmm.out' export SCALFMM_SIMGRIDOUT='scalfmm.out'
export GROUP_SIZE=8 export STARPU_STATS=1
export GROUP_SIZE=32
export TREE_HEIGHT=6 export TREE_HEIGHT=6
export NB_NODE=8 export NB_NODE=8
export NB_PARTICLE_PER_NODE=$((`awk "BEGIN{print 8 ** ($TREE_HEIGHT-1)}"` / $NB_NODE)) export NB_PARTICLE_PER_NODE=$((`awk "BEGIN{print 8 ** ($TREE_HEIGHT-1)}"` / $NB_NODE))
...@@ -9,28 +10,59 @@ echo "TREE_HEIGHT=$TREE_HEIGHT" ...@@ -9,28 +10,59 @@ echo "TREE_HEIGHT=$TREE_HEIGHT"
echo "NB_NODE=$NB_NODE" echo "NB_NODE=$NB_NODE"
echo "NB_PARTICLE_PER_NODE=$NB_PARTICLE_PER_NODE" echo "NB_PARTICLE_PER_NODE=$NB_PARTICLE_PER_NODE"
#Compile only what we need
make testBlockedImplicitAlgorithm generateMapping testBlockedMpiAlgorithm compareDAGmapping -j $((`nproc`*2)) make testBlockedImplicitAlgorithm generateMapping testBlockedMpiAlgorithm compareDAGmapping -j $((`nproc`*2))
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
exit exit
fi fi
#Execute explicit mpi version
mpiexec -n $NB_NODE ./Tests/Release/testBlockedMpiAlgorithm -nb $NB_PARTICLE_PER_NODE -bs $GROUP_SIZE -h $TREE_HEIGHT mpiexec -n $NB_NODE ./Tests/Release/testBlockedMpiAlgorithm -nb $NB_PARTICLE_PER_NODE -bs $GROUP_SIZE -h $TREE_HEIGHT
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
exit exit
fi fi
#Aggregate task information from explicit execution
a=`ls $SCALFMM_SIMGRIDOUT\_*` a=`ls $SCALFMM_SIMGRIDOUT\_*`
rm -f $SCALFMM_SIMGRIDOUT rm -f $SCALFMM_SIMGRIDOUT
echo $a echo $a