+Contenu
+=======
+* Compilation de SimGrid
+* Compilation...
+* Utilisation
+* Communications
+* Pour ajouter un nouvel algorithme d'équilibrage
+* Pour ajouter une nouvelle option au programme
+* Liste des fichiers
+----------------------------------------------------------------------
+
Compilation de SimGrid
======================
Utilisation
===========
-Pour avoir l'aide en ligne :
-$ ./loba -hhh
+Idée générale : on dispose d'une plate-forme (décrite dans le fichier
+XML qui va bien), et on déploie dessus une application. La notion de
+voisinage entre les processus est *liée à l'application*. Il faut
+évidemment que la plate-forme sous-jacente autorise les communications
+entre voisins...
+
+Pour avoir l'aide en ligne sur les différents paramètres :
+ $ ./loba -h (ou -hh, ou -hhh, pour plus de détails)
Pour changer le niveau de détail des affichages :
--log=category.thres:level
avec
category : simu, main, depl, comm, proc, loba
et
- level : trace, debug, verbose, info, warning, error, critical
+ level : trace, debug, verbose, info, warning, error, critical
+
+Pour plus de détail sur les options de logging :
+ http://simgrid.gforge.inria.fr/doc/group__XBT__log.html#log_use
+
+Communications
+==============
+
+Pour communiquer, chaque processus écoute sur 2 mailboxes (sortes de
+ports) :
+ - une pour les message de contrôle ;
+ - une pour les transferts de charge.
+
+Ceci afin d'éviter de bloquer les échanges d'information pendant un
+transfert de charge.
+
+À la fin, chaque processus envoie un message "CLOSE" à tous ses
+voisins (sur chaque mailbox), et attends d'avoir reçu deux messages par
+voisin (un sur chaque mailbox).
+
+Ceci permet de synchroniser les processus à la terminaison (ça, à la
+rigueur, on s'en fout un peu), et surtout de s'assurer qu'il n'y a
+plus de communication qui « traîne » dans les canaux.
+
+Cela permet aussi de ne pas réarmer les communications non bloquantes
+qu'on ne sait pas annuler proprement (un manque dans SimGrid).
+
+Il ne faut bien sûr plus envoyer de message après avoir envoyé un
+"CLOSE".
+
+Attention : lors du déploiement de l'application, il faut s'assurer que
+la relation de voisinage est symétrique !
+*Ce n'est pas vérifié par le programme.*
+
+Pour ajouter un nouvel algorithme d'équilibrage
+===============================================
+
+1. Imiter ce qui est fait pour loba_simple :
+ - définir une nouvelle classe dérivant de process
+ - attention, il faut construire le process explicitement
+ - redéfinir la méthode load_balance qui :
+ - reçoit en paramètre la charge à prendre en compte ;
+ - peut utiliser et éventuellement réordonner le tableau process::pneigh ;
+ - peut récupérer l'information de charge d'un voisin avec
+ pneigh[i]->get_load() ;
+ - définit la charge à envoyer avec
+ pneigh[i]->set_to_send(quantité) ;
+ - retourne la somme des quantités définies avec set_to_send,
+ éventuellement à l'aide de la méthode process::sum_of_to_send()
+ qui clacule cette somme.
+
+2. Ajouter l'algorithme dans la liste des options. Dans options.cpp :
+ - faire le #include adéquat ;
+ - ajouter une ligne NOL_INSERT(...) dans la liste existante
+ (dans loba_algorithms_type::loba_algorithms_type()).
+
+Pour ajouter une nouvelle option au programme
+=============================================
+
+1. Ajouter une variable, déclarée dans options.h et définie dans options.cpp
+ (classement plus ou moins thématique).
+
+2. Toujours dans options.cpp, il faut :
+ - compléter la fonction opt::parse_args(), normalement le 3e paramètre à
+ getopt() et le switch..case qui suit (garder l'ordre alphabétique) ;
+ - compléter la fonction opt::print() (avec le même ordre que en 1.) ;
+ - compléter la fonction opt::usage() (avec le même ordre que en 1.).
+
+3. Utiliser la nouvelle variable au(x) bon(s) endroit(s) !
-Fichiers
-========
+Liste de fichiers
+=================
* fichiers de description de plates-formes
hostdata.h gestion des boites de réception, par hôte
hostdata.cpp
- loba_simple.h load-balancing simple (à copier pour ajouter
- loba_simple.cpp d'autres algorithmes)
+ loba_simple.h équilibrage simple
+ loba_simple.cpp (à imiter pour ajouter d'autres algorithmes)
main.cpp le programme principal
misc.cpp
named_object_list.h gestion d'une table de constructeurs
- avec des noms et des descritpions
+ avec des noms et des descriptions
neighbor.h un voisin pour un processus
neighbor.cpp
-> implement some random initial distribution of load
* add synchronized mode
+
+* translate README file ?
communicator();
~communicator();
+ // Start to listen for incoming messages
void listen();
+ // Send a message to the "dest" mailbox
void send(const char* dest, message* msg);
+
+ // Try to receive a message. Returns true on success.
+ // If "wait" is true, blocks until success or error.
bool recv(message*& msg, m_host_t& from, bool wait);
+
+ // Try to flush pending sending communications.
+ // If "wait" is true, blocks until success.
void flush(bool wait);
+ // Advertise that the next "close" message is the last one, and
+ // that we do not await any message after that, either on the
+ // control or the data channel.
void next_close_on_ctrl_is_last();
void next_close_on_data_is_last();
int send_counter;
// Control channel for receiving
- m_task_t ctrl_task;
- msg_comm_t ctrl_comm;
- bool ctrl_close_is_last;
+ m_task_t ctrl_task; // receive buffer
+ msg_comm_t ctrl_comm; // receive communication
+ bool ctrl_close_is_last; // do not rearm comm after next close
// Data channel for receiving
- m_task_t data_task;
- msg_comm_t data_comm;
- bool data_close_is_last;
+ m_task_t data_task; // receive buffer
+ msg_comm_t data_comm; // receive communication
+ bool data_close_is_last; // do not rearm comm after next close
const char* get_ctrl_mbox() const { return host->get_ctrl_mbox(); }
const char* get_data_mbox() const { return host->get_data_mbox(); }
+ // Used to test if a communication is over, and to destroy it if it is.
static bool comm_test_n_destroy(msg_comm_t comm);
};
#include <iostream>
#include <string>
+// Define a polynomial function
+// The factors are given at construction time, in a C-style string,
+// separated by commas (eg. "1, 2, 3" for x^2 + 2x +3).
class cost_func {
public:
cost_func(const char* param);
#include <vector>
+// Deploy an application automatically, according to the global parameters
void MY_launch_application();
+// Base class for deployment generators...
class deployment_generator {
public:
deployment_generator();
#include <xbt/log.h>
#include <xbt/sysdep.h>
#include "misc.h"
+#include "options.h"
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(main);
{
int nhosts = MSG_get_host_number();
m_host_t* host_list = MSG_get_host_table();
- // fixme: only sort hosts for automatically created deployment
- // fixme: add an option to disable sorting
- std::sort(host_list, host_list + nhosts, m_host_less());
+ // only sort hosts for automatically created deployment
+ if (opt::auto_depl::enabled)
+ std::sort(host_list, host_list + nhosts, m_host_less());
hosts.assign(host_list, host_list + nhosts);
xbt_free(host_list);
#include <vector>
#include <msg/msg.h>
+// Helper class that associates instances of itself with each host.
+// Facilitate global operations on hosts, and retreiving of host name
+// and mailboxes.
class hostdata {
public:
static void create();
int imax = -1;
double min = my_load;
double max = -1.0;
- for (unsigned i = 0 ; i < neigh.size() ; ++i) {
- double l = neigh[i].get_load();
+ for (unsigned i = 0 ; i < pneigh.size() ; ++i) {
+ double l = pneigh[i]->get_load();
if (l >= my_load)
continue;
if (l < min) {
// found someone
double balance = (my_load - max) / 2;
DEBUG6("%d:%g %d:%g %g %g", imin, min, imax, max, my_load, balance);
- neigh[imin].set_to_send(balance);
+ pneigh[imin]->set_to_send(balance);
return balance;
} else {
return 0.0;
#include <map>
#include <string>
+// Define an associative container that maps a name with a class and a
+// description. All classes must be derived from a same base class.
+//
+// We can then use the name to create an object of the associated
+// class, and to retrieve a pointer to this object.
+//
+// Furthermore, it is possible to iterate over the elements to get
+// their name and their description.
+
+// I am too lazy to comment the code, which should be obvious...
+
//===== arity 0 =====
template <typename Base>
//===================
+// "NOL" like in Named_Object_List....
#define NOL_INSERT(name, descr, class) insert(name, new creator<class>(descr))
#endif // !NAMED_OBJECT_LIST_H
neighbor(const char* hostname);
~neighbor();
+ // returns name, ctrl or data mbox
const char* get_name() const { return host->get_name(); }
const char* get_ctrl_mbox() const { return host->get_ctrl_mbox(); }
const char* get_data_mbox() const { return host->get_data_mbox(); }
+ // Getter and setter for load
double get_load() const { return load; }
void set_load(double amount) { load = amount; }
+ // Getter and setter for debt
double get_debt() const { return debt; }
void set_debt(double amount) { debt = amount; }
+ // Getter and setter for to_send
double get_to_send() const { return to_send; }
void set_to_send(double amount) { to_send = amount; }
private:
- const hostdata* host;
+ const hostdata* host; // pointer to this neighbor's hostdata
- double load;
- double debt;
+ double load; // the load information we know for it
+ double debt; // the load we had to send to it, but
+ // that we have not currently sent
+ // (in bookkeeping mode)
- double to_send;
+ double to_send; // the load we have to send to it
};
#endif // !NEIGHBOR_H
using namespace std::tr1;
using namespace std::tr1::placeholders;
- // fixme: shall we send data at all iterations?
if (opt::bookkeeping) {
std::for_each(neigh.begin(), neigh.end(),
bind(&process::send1_bookkeeping, this, _1));
}
}
-// Returns false if a CLOSE message was received.
bool process::receive(recv_wait_mode wait)
{
bool result = true;
typedef std::vector<neighbor> neigh_type;
typedef std::vector<neighbor*> pneigh_type;
- neigh_type neigh;
- pneigh_type pneigh;
+ pneigh_type pneigh; // list of pointers to neighbors that
+ // we are free to reorder
+
+ // Returns the sum of "to_send" for all neighbors.
+ double sum_of_to_send() const;
private:
typedef MAP_TEMPLATE<m_host_t, neighbor*> rev_neigh_type;
- enum recv_wait_mode { NO_WAIT = 0, WAIT, WAIT_FOR_CLOSE };
- rev_neigh_type rev_neigh;
+ neigh_type neigh; // list of neighbors (do not alter
+ // after construction!)
+ rev_neigh_type rev_neigh; // map m_host_t -> neighbor
- communicator comm;
- int ctrl_close_pending;
- int data_close_pending;
+ communicator comm; // communicator for this process
+ int ctrl_close_pending; // number of "close" messages to wait
+ // on ctrl channel
+ int data_close_pending; // number of "close" messages to wait
+ // on data channel
- unsigned iter;
+ unsigned iter; // counter of iterations
- double prev_load_broadcast;
- double load;
- double expected_load;
+ double prev_load_broadcast; // used to ensure that we do not send
+ // a same information messages
+ double load; // current load
+ double expected_load; // expected load in bookkeeping mode
- double sum_of_to_send() const;
+ // The load balancing algorithm comes here...
+ // Parameter "my_load" is the load to take into account for myself
+ // (may be load or expected load).
+ // Returns the total load sent to neighbors.
virtual double load_balance(double my_load);
+ // Virtually do some computation
void compute();
+
+ // Send procedures, with helpers for bookkeeping mode or not
void send1_no_bookkeeping(neighbor& nb);
void send1_bookkeeping(neighbor& nb);
void send();
+
+ // Receive procedure: wait (or not) for a message to come.
+ // Returns false if some "close" message was received, returns true
+ // otherwise.
+ enum recv_wait_mode { NO_WAIT = 0, WAIT, WAIT_FOR_CLOSE };
bool receive(recv_wait_mode wait);
+
+ // Finalize sends a "close" message to each neighbor and wait for
+ // all of them to answer.
void finalize1(neighbor& nb);
void finalize();
+
+ // Print with given priority what we know about our neighbors' loads
void print_loads(e_xbt_log_priority_t logp = xbt_log_priority_info);
};
#ifndef SIMGRID_FEATURES_H
#define SIMGRID_FEATURES_H
-// fixme: dirty hack
+// Try to guess if MSG_wait destroys communications or not, because it
+// changed after SimGrid 3.5.
+//
+// Use some define introduced after that. fixme: dirty hack
+//
#if defined(XBT_RUNNING_CTX_INITIALIZER)
# define MSG_WAIT_DESTROYS_COMMS 0
#else