diff --git a/Doc/noDist/Presentation/group_issue1.png b/Doc/noDist/Presentation/group_issue1.png new file mode 100644 index 0000000000000000000000000000000000000000..5772d5f2cf42818790087667bed5d10f148e13f1 Binary files /dev/null and b/Doc/noDist/Presentation/group_issue1.png differ diff --git a/Doc/noDist/Presentation/morton_box_center.png b/Doc/noDist/Presentation/morton_box_center.png new file mode 100644 index 0000000000000000000000000000000000000000..5f5be4a59904d18717f8193c673b94fad82d2368 Binary files /dev/null and b/Doc/noDist/Presentation/morton_box_center.png differ diff --git a/Doc/noDist/Presentation/naive_split.png b/Doc/noDist/Presentation/naive_split.png new file mode 100644 index 0000000000000000000000000000000000000000..497c6172676fdaad74c52e462456a2d9887217f6 Binary files /dev/null and b/Doc/noDist/Presentation/naive_split.png differ diff --git a/Doc/noDist/Presentation/presentation.html b/Doc/noDist/Presentation/presentation.html new file mode 100644 index 0000000000000000000000000000000000000000..81c0a1633e950014ee6ef72f2cbafd30a6b4fb3f --- /dev/null +++ b/Doc/noDist/Presentation/presentation.html @@ -0,0 +1,316 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr"> +<head> +<title>Usage implicit de MPI dans ScalFmm</title> +<!-- 2016-03-21 lun. 15:27 --> +<meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> +<meta name="generator" content="Org-mode" /> +<meta name="author" content="Martin Khannouz" /> +<style type="text/css"> + <!--/*--><![CDATA[/*><!--*/ + .title { text-align: center; } + .todo { font-family: monospace; color: red; } + .done { color: green; } + .tag { background-color: #eee; font-family: monospace; + padding: 2px; font-size: 80%; font-weight: normal; } + .timestamp { color: #bebebe; } + .timestamp-kwd { color: #5f9ea0; } + .right { margin-left: auto; margin-right: 0px; text-align: right; } + .left { margin-left: 0px; margin-right: auto; text-align: left; } + .center { margin-left: auto; margin-right: auto; text-align: center; } + .underline { text-decoration: underline; } + #postamble p, #preamble p { font-size: 90%; margin: .2em; } + p.verse { margin-left: 3%; } + pre { + border: 1px solid #ccc; + box-shadow: 3px 3px 3px #eee; + padding: 8pt; + font-family: monospace; + overflow: auto; + margin: 1.2em; + } + pre.src { + position: relative; + overflow: visible; + padding-top: 1.2em; + } + pre.src:before { + display: none; + position: absolute; + background-color: white; + top: -10px; + right: 10px; + padding: 3px; + border: 1px solid black; + } + pre.src:hover:before { display: inline;} + pre.src-sh:before { content: 'sh'; } + pre.src-bash:before { content: 'sh'; } + pre.src-emacs-lisp:before { content: 'Emacs Lisp'; } + pre.src-R:before { content: 'R'; } + pre.src-perl:before { content: 'Perl'; } + pre.src-java:before { content: 'Java'; } + pre.src-sql:before { content: 'SQL'; } + + table { border-collapse:collapse; } + caption.t-above { caption-side: top; } + caption.t-bottom { caption-side: bottom; } + td, th { vertical-align:top; } + th.right { text-align: center; } + th.left { text-align: center; } + th.center { text-align: center; } + td.right { text-align: right; } + td.left { text-align: left; } + td.center { text-align: center; } + dt { font-weight: bold; } + .footpara:nth-child(2) { display: inline; } + .footpara { display: block; } + .footdef { margin-bottom: 1em; } + .figure { padding: 1em; } + .figure p { text-align: center; } + .inlinetask { + padding: 10px; + border: 2px solid gray; + margin: 10px; + background: #ffffcc; + } + #org-div-home-and-up + { text-align: right; font-size: 70%; white-space: nowrap; } + textarea { overflow-x: auto; } + .linenr { font-size: smaller } + .code-highlighted { background-color: #ffff00; } + .org-info-js_info-navigation { border-style: none; } + #org-info-js_console-label + { font-size: 10px; font-weight: bold; white-space: nowrap; } + .org-info-js_search-highlight + { background-color: #ffff00; color: #000000; font-weight: bold; } + /*]]>*/--> +</style> +<script type="text/javascript"> +/* +@licstart The following is the entire license notice for the +JavaScript code in this tag. + +Copyright (C) 2012-2013 Free Software Foundation, Inc. + +The JavaScript code in this tag is free software: you can +redistribute it and/or modify it under the terms of the GNU +General Public License (GNU GPL) as published by the Free Software +Foundation, either version 3 of the License, or (at your option) +any later version. The code is distributed WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. + +As additional permission under GNU GPL version 3 section 7, you +may distribute non-source (e.g., minimized or compacted) forms of +that code without the copy of the GNU GPL normally required by +section 4, provided you include this license notice and a URL +through which recipients can access the Corresponding Source. + + +@licend The above is the entire license notice +for the JavaScript code in this tag. +*/ +<!--/*--><![CDATA[/*><!--*/ + function CodeHighlightOn(elem, id) + { + var target = document.getElementById(id); + if(null != target) { + elem.cacheClassElem = elem.className; + elem.cacheClassTarget = target.className; + target.className = "code-highlighted"; + elem.className = "code-highlighted"; + } + } + function CodeHighlightOff(elem, id) + { + var target = document.getElementById(id); + if(elem.cacheClassElem) + elem.className = elem.cacheClassElem; + if(elem.cacheClassTarget) + target.className = elem.cacheClassTarget; + } +/*]]>*///--> +</script> +</head> +<body> +<div id="content"> +<h1 class="title">Usage implicit de MPI dans ScalFmm</h1> +<div id="table-of-contents"> +<h2>Table des matières</h2> +<div id="text-table-of-contents"> +<ul> +<li><a href="#sec-1">1. Implémentation mpi implicite très naïve</a></li> +<li><a href="#sec-2">2. Implémentation moins naïve</a></li> +<li><a href="#sec-3">3. Reproduction du mapping mpi explicite</a> +<ul> +<li><a href="#sec-3-1">3.1. Premier problème des groupes</a></li> +<li><a href="#sec-3-2">3.2. Solution retenue</a></li> +<li><a href="#sec-3-3">3.3. Validation des résultats</a> +<ul> +<li><a href="#sec-3-3-1">3.3.1. Observation</a></li> +</ul> +</li> +</ul> +</li> +<li><a href="#sec-4">4. Et après ?</a></li> +</ul> +</div> +</div> + +<div id="outline-container-sec-1" class="outline-2"> +<h2 id="sec-1"><span class="section-number-2">1</span> Implémentation mpi implicite très naïve</h2> +<div class="outline-text-2" id="text-1"> +<p> +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 <i>tagger</i> les <i>handles</i> 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. +</p> + +<p> +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é à <i>mapper</i> 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. +</p> +</div> +</div> + +<div id="outline-container-sec-2" class="outline-2"> +<h2 id="sec-2"><span class="section-number-2">2</span> Implémentation moins naïve</h2> +<div class="outline-text-2" id="text-2"> +<p> +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 <i>mapping</i> des données. +Elle consistait à partager chaque niveau entre tous les processus de la manière la plus équitable possible. +</p> + + +<div class="figure"> +<p><img src="./naive_split.png" alt="naive_split.png" /> +</p> +<p><span class="figure-number">Figure 1 :</span> Division de chaque niveau entre chaque processus. Groupe de l'arbre de taille 4.</p> +</div> +</div> +</div> + +<div id="outline-container-sec-3" class="outline-2"> +<h2 id="sec-3"><span class="section-number-2">3</span> Reproduction du mapping mpi explicite</h2> +<div class="outline-text-2" id="text-3"> +<p> +Pour pouvoir effectuer des comparaisons il était nécessaire de reproduire le même <i>mapping</i> de tâches la version MPI explicite. +Dans le cas de la version implicite telle qu'elle est actuellement implémentée, le <i>mapping</i> des données infère le <i>mapping</i> 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. +</p> +</div> + +<div id="outline-container-sec-3-1" class="outline-3"> +<h3 id="sec-3-1"><span class="section-number-3">3.1</span> Premier problème des groupes</h3> +<div class="outline-text-3" id="text-3-1"> +<p> +La disposition des particules sur les nœuds MPI étant décidé par un tri distribué, il était plus judicieux de sauvegarder le <i>mapping</i> des particules dans un fichier puis de le charger (dans la version implicite) et d'utiliser ce <i>mapping</i> pour influer le <i>mapping</i> 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é (<i>group tree</i>). +</p> + + +<div id="fig:SED-HR4049" class="figure"> +<p><img src="./group_issue1.png" alt="group_issue1.png" /> +</p> +<p><span class="figure-number">Figure 2 :</span> Problème issuent de la constitution des groupes.</p> +</div> + +<p> +Or le <i>mapping</i> des données est fait avec la granularité des groupes de l'arbre groupé. +</p> + +<p> +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 ? +</p> +</div> +</div> + +<div id="outline-container-sec-3-2" class="outline-3"> +<h3 id="sec-3-2"><span class="section-number-3">3.2</span> Solution retenue</h3> +<div class="outline-text-3" id="text-3-2"> +<p> +Plutôt que d'essayer de reproduire un <i>mapping</i> 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 <i>mapping</i> 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 <i>mapping</i> de données sur un certain de nombre de niveaux. +Ce nombre varie en fonction de la taille des groupes de l'arbre groupé. +</p> + + +<div id="fig:SED-HR4049" class="figure"> +<p><img src="./morton_box_center.png" alt="morton_box_center.png" /> +</p> +<p><span class="figure-number">Figure 3 :</span> Méthode pour générer une particule à un indice de Morton donné.</p> +</div> +</div> +</div> + +<div id="outline-container-sec-3-3" class="outline-3"> +<h3 id="sec-3-3"><span class="section-number-3">3.3</span> Validation des résultats</h3> +<div class="outline-text-3" id="text-3-3"> +<p> +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. +</p> +</div> + +<div id="outline-container-sec-3-3-1" class="outline-4"> +<h4 id="sec-3-3-1"><span class="section-number-4">3.3.1</span> Observation</h4> +<div class="outline-text-4" id="text-3-3-1"> +<p> +Si l'on fait exception des niveaux où l'on sait que des erreurs de tâches se trouveront, on a : +</p> +<ul class="org-ul"> +<li>Plus de tâches dans la version explicite car elle a des tâches (P2P, M2L) symetriques. +</li> +<li>Toutes les tâches issuent de l'algorithme implicite se retrouvent dans l'ensemble des tâches explicite. +</li> +<li>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. +</li> +</ul> +</div> +</div> +</div> +</div> + +<div id="outline-container-sec-4" class="outline-2"> +<h2 id="sec-4"><span class="section-number-2">4</span> Et après ?</h2> +<div class="outline-text-2" id="text-4"> +<ul class="org-ul"> +<li>Comparaison des performances +<ul class="org-ul"> +<li>Répartition des GFlop +</li> +<li>Répartition du temps de calcul +</li> +<li>Mémoire utilisée par nœud +</li> +</ul> +</li> +<li>Étude d'autres <i>mapping</i> +</li> +<li>Limiter l'empreinte mémoire +<ul class="org-ul"> +<li>Ne pas allouer les cellules numériques si ce n'est pas necessaire (<i>up</i> et <i>down</i>) +</li> +<li>Ne pas allouer les cellules symboliques si ce n'est pas necessaire +</li> +<li>Distribuer l'arbre +</li> +</ul> +</li> +</ul> +</div> +</div> +</div> +<div id="postamble" class="status"> +<p class="author">Auteur: Martin Khannouz</p> +<p class="date">Created: 2016-03-21 lun. 15:27</p> +<p class="creator"><a href="http://www.gnu.org/software/emacs/">Emacs</a> 24.5.1 (<a href="http://orgmode.org">Org</a> mode 8.2.10)</p> +<p class="validation"><a href="http://validator.w3.org/check?uri=referer">Validate</a></p> +</div> +</body> +</html> diff --git a/Doc/noDist/Presentation/presentation.org b/Doc/noDist/Presentation/presentation.org new file mode 100644 index 0000000000000000000000000000000000000000..4f2905606b293f148d7b9ab0c60937eb78fc9aca --- /dev/null +++ b/Doc/noDist/Presentation/presentation.org @@ -0,0 +1,83 @@ +#+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 + + diff --git a/Src/GroupTree/Core/FGroupTaskStarpuImplicitAlgorithm.hpp b/Src/GroupTree/Core/FGroupTaskStarpuImplicitAlgorithm.hpp index 6cbfd9b803a8bcc937bc294fa5aa1a9cc5c925fe..9f7ad0ce0e74fb12cbafec769b385800ee71bc61 100644 --- a/Src/GroupTree/Core/FGroupTaskStarpuImplicitAlgorithm.hpp +++ b/Src/GroupTree/Core/FGroupTaskStarpuImplicitAlgorithm.hpp @@ -192,8 +192,6 @@ public: taskNames = new FStarPUTaskNameParams(mpi_rank, nproc); #endif #endif - cout << mpi_rank << "/" << nproc << endl; - starpu_malloc_set_align(32); starpu_pthread_mutex_t initMutex; diff --git a/Tests/GroupTree/compareDAGmapping.cpp b/Tests/GroupTree/compareDAGmapping.cpp index 1d00b97170eb4fa54197801b8bc08d410e1cd2e5..4347b631d2bcd013dfe7e4ddc2e969b02f946bd0 100644 --- a/Tests/GroupTree/compareDAGmapping.cpp +++ b/Tests/GroupTree/compareDAGmapping.cpp @@ -8,6 +8,7 @@ #include <deque> #include <unordered_set> #include <unordered_map> +#include <omp.h> using namespace std; #include "../../Src/Utils/FGlobal.hpp" @@ -49,6 +50,7 @@ struct Task cout << taskNames[type]; for(size_t i = 0; i < id.size(); ++i) cout << ", " << id[i]; + cout << "(mpi " << mpiNode << ")"; cout << endl; } }; @@ -66,6 +68,7 @@ struct DagData { unordered_set<Task> allTask; unordered_map<long long int, double> performence; + int treeHeight; }; 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[3] = stoll(lineElements[12]); task.mpiNode = stoi(lineElements[13]); + task.level = dagData.treeHeight - 1; dagData.allTask.insert(task); } else if(lineElements.size() >= 10 && lineElements[0] == "P2P") @@ -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) cout << "Suricate" << endl; task.mpiNode = stoi(lineElements[9]); + task.level = dagData.treeHeight - 1; dagData.allTask.insert(task); } else if(lineElements.size() >= 10 && lineElements[0] == "M2L" ) @@ -113,6 +118,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) task.id[3] = stoll(lineElements[7]); task.id[4] = stoll(lineElements[8]); task.mpiNode = stoi(lineElements[9]); + task.level = task.id[0]; dagData.allTask.insert(task); } else if(lineElements.size() >= 13 && lineElements[0] == "M2L_out") @@ -127,6 +133,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) task.id[3] = stoll(lineElements[10]); task.id[4] = stoll(lineElements[11]); task.mpiNode = stoi(lineElements[12]); + task.level = task.id[0]; dagData.allTask.insert(task); } else if(lineElements.size() >= 13 && lineElements[0] == "M2M") @@ -141,6 +148,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) task.id[3] = stoll(lineElements[10]); task.id[4] = stoll(lineElements[11]); task.mpiNode = stoi(lineElements[12]); + task.level = task.id[0]; dagData.allTask.insert(task); } else if(lineElements.size() >= 13 && lineElements[0] == "L2L") @@ -155,6 +163,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) task.id[3] = stoll(lineElements[10]); task.id[4] = stoll(lineElements[11]); task.mpiNode = stoi(lineElements[12]); + task.level = task.id[0]; dagData.allTask.insert(task); } else if(lineElements.size() >= 8 && lineElements[0] == "L2P") @@ -166,6 +175,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) task.id[0] = stoll(lineElements[5]); task.id[1] = stoll(lineElements[6]); task.mpiNode = stoi(lineElements[7]); + task.level = dagData.treeHeight - 1; dagData.allTask.insert(task); } else if(lineElements.size() >= 8 && lineElements[0] == "P2M") @@ -177,6 +187,7 @@ bool parseLine(DagData & dagData, deque<string> & lineElements) task.id[0] = stoll(lineElements[5]); task.id[1] = stoll(lineElements[6]); task.mpiNode = stoi(lineElements[7]); + task.level = 0; dagData.allTask.insert(task); } else @@ -268,37 +279,91 @@ void fillPerformanceData(const char* const filename, DagData & dagData) } 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}; - bool notFound[treeHeight] = {false}; - for(Task task : dag1.allTask) + #pragma omp parallel { - bool found = false; - 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) + #pragma omp single nowait { - 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; - break; + for(int j = 0; j < treeHeight; ++j) + { + 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[]) { @@ -334,10 +399,12 @@ int main(int argc, char* argv[]) DagData implicitData, explicitData; bool implicitGood, explicitGood; std::thread explicitThread([&](){ + explicitData.treeHeight = treeHeight; fillPerformanceData(explicitTraceFilename, explicitData); explicitGood = fillDagData(explicitFilename, explicitData); }); std::thread implicitThread([&](){ + implicitData.treeHeight = treeHeight; fillPerformanceData(implicitTraceFilename, implicitData); implicitGood = fillDagData(implicitFilename, implicitData); }); @@ -347,7 +414,7 @@ int main(int argc, char* argv[]) { cout << explicitData.allTask.size() << " tasks in explicit." << endl; cout << implicitData.allTask.size() << " tasks in implicit." << endl; - compareDag(explicitData, implicitData, treeHeight); + compareDag(implicitData, explicitData, treeHeight); } return 0; } diff --git a/Tests/GroupTree/testBlockedImplicitAlgorithm.cpp b/Tests/GroupTree/testBlockedImplicitAlgorithm.cpp index a6c70f034b09fb4158f8080c721261cf2bb4b82a..2d4726b883ab1fd5a9ded168e213c944d7ff9bc8 100644 --- a/Tests/GroupTree/testBlockedImplicitAlgorithm.cpp +++ b/Tests/GroupTree/testBlockedImplicitAlgorithm.cpp @@ -39,6 +39,7 @@ using namespace std; #include "../../Src/Core/FFmmAlgorithm.hpp" std::vector<MortonIndex> getMortonIndex(const char* const mapping_filename); +void timeAverage(int mpi_rank, int nproc, double elapsedTime); int main(int argc, char* argv[]){ setenv("STARPU_NCPU","1",1); @@ -131,18 +132,22 @@ int main(int argc, char* argv[]){ // Run the algorithm GroupKernelClass groupkernel; GroupAlgorithm groupalgo(&groupedTree,&groupkernel, distributedMortonIndex); + FTic timerExecute; groupalgo.execute(); + double elapsedTime = timerExecute.tacAndElapsed(); + cout << "Executing time (implicit node " << groupalgo.getRank() << ") " << elapsedTime << "s\n"; + timeAverage(groupalgo.getRank(), groupalgo.getNProc(), elapsedTime); + // Usual algorithm KernelClass kernels; // FTestKernels FBasicKernels FmmClass algo(&tree,&kernels); //FFmmAlgorithm FFmmAlgorithmThread algo.execute(); 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) std::cout << "Error at level " << i << std::endl; } - return 0; // Validate the result for(int idxLevel = 2 ; idxLevel < groupedTree.getHeight() ; ++idxLevel){ for(int idxGroup = 0 ; idxGroup < groupedTree.getNbCellGroupAtLevel(idxLevel) ; ++idxGroup){ @@ -217,3 +222,22 @@ std::vector<MortonIndex> getMortonIndex(const char* const mapping_filename) cerr << "Impossible d'ouvrir le fichier !" << endl; 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); + } +} diff --git a/Tests/GroupTree/testBlockedMpiAlgorithm.cpp b/Tests/GroupTree/testBlockedMpiAlgorithm.cpp index 509c9c600731716cbbdbe3c697c5de566861fdd0..a58e6bac3747c2daab80ccac323cdf2c1d006c91 100644 --- a/Tests/GroupTree/testBlockedMpiAlgorithm.cpp +++ b/Tests/GroupTree/testBlockedMpiAlgorithm.cpp @@ -43,6 +43,7 @@ +void timeAverage(int mpi_rank, int nproc, double elapsedTime); int main(int argc, char* argv[]){ const FParameterNames LocalOptionBlocSize { @@ -128,15 +129,13 @@ int main(int argc, char* argv[]){ mpiComm.global().processId()+1, 0, 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) { if(i == mpiComm.global().processId()) { - std::cout << "My last index is (" << mpiComm.global().processId() << ") " << leftLimite << "\n"; - std::cout << "My left limite is (" << mpiComm.global().processId() << ") " << myLeftLimite << "\n"; - std::cout << "Size (" << mpiComm.global().processId() << ") " << allParticles.getNbParticles() << "\n"; + FLOG(std::cout << "My last index is (" << mpiComm.global().processId() << ") " << leftLimite << "\n"); + FLOG(std::cout << "My left limite is (" << mpiComm.global().processId() << ") " << myLeftLimite << "\n"); + FLOG(std::cout << "Size (" << mpiComm.global().processId() << ") " << allParticles.getNbParticles() << "\n"); } mpiComm.global().barrier(); } @@ -144,15 +143,16 @@ int main(int argc, char* argv[]){ // Put the data into the tree GroupOctreeClass groupedTree(NbLevels, loader.getBoxWidth(), loader.getCenterOfBox(), groupSize, &allParticles, true, leftLimite); - //groupedTree.printInfoBlocks(); // Run the algorithm GroupKernelClass groupkernel; GroupAlgorithm groupalgo(mpiComm.global(), &groupedTree,&groupkernel); + FTic timerExecute; groupalgo.execute(); - - std::cout << "Wait Others... " << std::endl; + double elapsedTime = timerExecute.tacAndElapsed(); + std::cout << "Executing time (explicit node " << mpiComm.global().processId() << ") " << elapsedTime << "s\n"; mpiComm.global().barrier(); + timeAverage(mpiComm.global().processId(), mpiComm.global().processCount(), elapsedTime); groupedTree.forEachCellLeaf<GroupContainerClass>([&](GroupCellClass cell, GroupContainerClass* leaf){ const FSize nbPartsInLeaf = leaf->getNbParticles(); @@ -209,4 +209,22 @@ int main(int argc, char* argv[]){ 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); + } +} diff --git a/compile_dag_result.sh b/compile_dag_result.sh index 9a8f9e6ac0fcf26a4b54eefe23d1a2964116b816..7b49e8171000460db96f98a622e0b02c090957ca 100755 --- a/compile_dag_result.sh +++ b/compile_dag_result.sh @@ -1,6 +1,7 @@ #!/bin/sh export SCALFMM_SIMGRIDOUT='scalfmm.out' -export GROUP_SIZE=8 +export STARPU_STATS=1 +export GROUP_SIZE=32 export TREE_HEIGHT=6 export NB_NODE=8 export NB_PARTICLE_PER_NODE=$((`awk "BEGIN{print 8 ** ($TREE_HEIGHT-1)}"` / $NB_NODE)) @@ -9,28 +10,59 @@ echo "TREE_HEIGHT=$TREE_HEIGHT" echo "NB_NODE=$NB_NODE" echo "NB_PARTICLE_PER_NODE=$NB_PARTICLE_PER_NODE" +#Compile only what we need make testBlockedImplicitAlgorithm generateMapping testBlockedMpiAlgorithm compareDAGmapping -j $((`nproc`*2)) if [ $? -ne 0 ]; then exit fi + +#Execute explicit mpi version mpiexec -n $NB_NODE ./Tests/Release/testBlockedMpiAlgorithm -nb $NB_PARTICLE_PER_NODE -bs $GROUP_SIZE -h $TREE_HEIGHT if [ $? -ne 0 ]; then exit fi +#Aggregate task information from explicit execution a=`ls $SCALFMM_SIMGRIDOUT\_*` rm -f $SCALFMM_SIMGRIDOUT echo $a for i in $a; do - echo $i cat $i >> $SCALFMM_SIMGRIDOUT done +#Get task information cp -f $SCALFMM_SIMGRIDOUT scalfmm_explicit.out -mpiexec -n $NB_NODE ./Tests/Release/generateMapping -nb $NB_PARTICLE_PER_NODE -bs $GROUP_SIZE -h $TREE_HEIGHT +#Get task information from fxt +#a=`ls $STARPU_FXT_PREFIX/../installprof*` +#ARG_FXT_TOO="" +#for i in $a; do + #ARG_FXT_TOO="$ARG_FXT_TOO -i $i" +#done +#$STARPU_DIR/bin/starpu_fxt_tool $ARG_FXT_TOO +#cp $STARPU_FXT_PREFIX/../trace.rec explicit.rec +#rm -f $STARPU_FXT_PREFIX/../installprof* + +#Generate mapping for implicite version +mpiexec -n $NB_NODE ./Tests/Release/generateMapping -nb $NB_PARTICLE_PER_NODE -bs $GROUP_SIZE -h $TREE_HEIGHT > /dev/null +#Execute implicit version mpiexec -n $NB_NODE ./Tests/Release/testBlockedImplicitAlgorithm -map mapping -f canard.fma -bs $GROUP_SIZE -h $TREE_HEIGHT if [ $? -ne 0 ]; then exit fi + +#Get task information cp -f scalfmm.out_0 scalfmm_implicit.out +#Get task information from fxt +a=`ls $STARPU_FXT_PREFIX/../installprof*` +#ARG_FXT_TOO="" +#for i in $a; do + #ARG_FXT_TOO="$ARG_FXT_TOO -i $i" +#done +#$STARPU_DIR/bin/starpu_fxt_tool $ARG_FXT_TOO +#cp $STARPU_FXT_PREFIX/../trace.rec implicit.rec +#rm -f $STARPU_FXT_PREFIX/../installprof* + +#Compare DAGs ./Tests/Release/compareDAGmapping -e scalfmm_explicit.out -i scalfmm_implicit.out -h $TREE_HEIGHT > output + +