]> AND Private Git Repository - loba.git/blob - process.cpp
Logo AND Algorithmique Numérique Distribuée

Private GIT Repository
Add load balancing algorithm selection facility.
[loba.git] / process.cpp
1 #include "process.h"
2
3 #include <algorithm>
4 #include <tr1/functional>
5 #include <iterator>
6 #include <numeric>
7 #include <stdexcept>
8 #include <sstream>
9 #include <xbt/log.h>
10 #include <xbt/time.h>
11 #include "misc.h"
12 #include "options.h"
13
14 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(proc);
15
16 process::process(int argc, char* argv[])
17 {
18     if (argc < 2 || !(std::istringstream(argv[1]) >> load))
19         throw std::invalid_argument("bad or missing initial load parameter");
20
21     neigh.assign(argv + 2, argv + argc);
22
23     pneigh.reserve(neigh.size());
24     for (unsigned i = 0 ; i < neigh.size() ; i++) {
25         neighbor* ptr = &neigh[i];
26         m_host_t host = MSG_get_host_by_name(ptr->get_name());
27         pneigh.push_back(ptr);
28         rev_neigh.insert(std::make_pair(host, ptr));
29     }
30
31     expected_load = load;
32
33     ctrl_close_pending = data_close_pending = neigh.size();
34     if (neigh.size() == 1) {
35         comm.next_close_on_ctrl_is_last();
36         comm.next_close_on_data_is_last();
37     }
38     if (neigh.size() > 0)
39         comm.listen();
40
41     e_xbt_log_priority_t logp = xbt_log_priority_verbose;
42     if (!LOG_ISENABLED(logp))
43         return;
44     std::ostringstream oss;
45     oss << neigh.size() << " neighbor";
46     if (!neigh.empty()) {
47         oss << ESSE(neigh.size()) << ": ";
48         std::transform(neigh.begin(), neigh.end() - 1,
49                        std::ostream_iterator<const char*>(oss, ", "),
50                        std::tr1::mem_fn(&neighbor::get_name));
51         oss << neigh.back().get_name();
52     }
53     LOG1(logp, "Got %s.", oss.str().c_str());
54     print_loads(logp);
55 }
56
57 process::~process()
58 {
59 }
60
61 int process::run()
62 {
63     bool one_more = true;
64
65     INFO1("Initial load: %g", load);
66     VERB0("Starting...");
67     // first send() to inform neighbors about our load (force it)
68     prev_load_broadcast = !(opt::bookkeeping? expected_load: load);
69     send();
70     iter = 0;
71     while (one_more) {
72         if (opt::log_rate && iter % opt::log_rate == 0) {
73             if (opt::bookkeeping)
74                 INFO3("(%u) current load: %g ; expected: %g",
75                       iter, load, expected_load);
76             else
77                 INFO2("(%u) current load: %g",
78                       iter, load);
79         }
80         print_loads(xbt_log_priority_debug);
81
82         bool do_compute = load > 0.0;
83         if (do_compute) {
84             compute();
85             ++iter;
86         }
87
88         bool close_received = !receive(do_compute? NO_WAIT: WAIT);
89
90         if (opt::bookkeeping)
91             expected_load -= load_balance(expected_load);
92         else
93             load -= load_balance(load);
94
95         send();
96
97         if (opt::exit_on_close && close_received)
98             one_more = false;
99         if (opt::maxiter && iter >= opt::maxiter)
100             one_more = false;
101     }
102     VERB0("Going to finalize...");
103     finalize();
104
105     /* Open Questions :
106      * - definition of load on heterogeneous hosts ?
107      * - how to detect convergence ?
108      * - how to manage link failures ?
109      */
110
111     VERB0("Done.");
112     if (opt::bookkeeping)
113         INFO4("Final load after %d iteration%s: %g ; expected: %g",
114               iter, ESSE(iter), load, expected_load);
115     else
116         INFO3("Final load after %d iteration%s: %g", iter, ESSE(iter), load);
117     return 0;
118 }
119
120 double process::sum_of_to_send() const
121 {
122     using namespace std::tr1;
123     using namespace std::tr1::placeholders;
124
125     return std::accumulate(neigh.begin(), neigh.end(), 0.0,
126                            bind(std::plus<double>(),
127                                 _1, bind(&neighbor::get_to_send, _2)));
128 }
129
130 double process::load_balance(double /*my_load*/)
131 {
132     if (iter == 1)
133         WARN0("process::load_balance is a no-op!");
134     return 0.0;
135 }
136
137 void process::compute()
138 {
139     double duration = opt::comp_cost(load);
140     m_task_t task = MSG_task_create("computation", duration, 0.0, NULL);
141     DEBUG2("compute %g flop%s.", duration, ESSE(duration));
142     MSG_task_execute(task);
143     MSG_task_destroy(task);
144 }
145
146 void process::send1_no_bookkeeping(neighbor& nb)
147 {
148     if (load != prev_load_broadcast)
149         comm.send(nb.get_ctrl_mbox(), new message(message::INFO, load));
150     double load_to_send = nb.get_to_send();
151     if (load_to_send > 0.0) {
152         comm.send(nb.get_data_mbox(), new message(message::LOAD, load_to_send));
153         nb.set_to_send(0.0);
154     }
155 }
156
157 void process::send1_bookkeeping(neighbor& nb)
158 {
159     if (expected_load != prev_load_broadcast)
160         comm.send(nb.get_ctrl_mbox(),
161                   new message(message::INFO, expected_load));
162     double load_to_send;
163     double new_debt;
164     double debt_to_send = nb.get_to_send();
165     if (debt_to_send > 0.0) {
166         comm.send(nb.get_ctrl_mbox(),
167                   new message(message::CREDIT, debt_to_send));
168         nb.set_to_send(0.0);
169         new_debt = nb.get_debt() + debt_to_send;
170     } else {
171         new_debt = nb.get_debt();
172     }
173     if (load <= new_debt) {
174         load_to_send = load;
175         nb.set_debt(new_debt - load_to_send);
176         load = 0.0;
177     } else {
178         load_to_send = new_debt;
179         nb.set_debt(0.0);
180         load -= load_to_send;
181     }
182     if (load_to_send > 0.0)
183         comm.send(nb.get_data_mbox(), new message(message::LOAD, load_to_send));
184 }
185
186 void process::send()
187 {
188     using namespace std::tr1;
189     using namespace std::tr1::placeholders;
190
191     // fixme: shall we send data at all iterations?
192     if (opt::bookkeeping) {
193         std::for_each(neigh.begin(), neigh.end(),
194                       bind(&process::send1_bookkeeping, this, _1));
195         prev_load_broadcast = expected_load;
196     } else {
197         std::for_each(neigh.begin(), neigh.end(),
198                       bind(&process::send1_no_bookkeeping, this, _1));
199         prev_load_broadcast = load;
200     }
201 }
202
203 // Returns false if a CLOSE message was received. 
204 bool process::receive(recv_wait_mode wait)
205 {
206     bool result = true;
207     message* msg;
208     m_host_t from;
209     bool do_recv = ctrl_close_pending || data_close_pending;
210     while (do_recv && comm.recv(msg, from, wait)) {
211         switch (msg->get_type()) {
212         case message::INFO: {
213             neighbor* n = rev_neigh[from];
214             n->set_load(msg->get_amount());
215             break;
216         }
217         case message::CREDIT:
218             expected_load += msg->get_amount();
219             break;
220         case message::LOAD:
221             load += msg->get_amount();
222             break;
223         case message::CTRL_CLOSE:
224             if (--ctrl_close_pending == 1)
225                 comm.next_close_on_ctrl_is_last();
226             DEBUG1("ctrl_close_pending = %d", ctrl_close_pending);
227             result = false;
228             break;
229         case message::DATA_CLOSE:
230             if (--data_close_pending == 1)
231                 comm.next_close_on_data_is_last();
232             DEBUG1("data_close_pending = %d", data_close_pending);
233             result = false;
234             break;
235         }
236         delete msg;
237         do_recv = (wait == WAIT_FOR_CLOSE) &&
238             (ctrl_close_pending || data_close_pending);
239     }
240     return result;
241 }
242
243 void process::finalize1(neighbor& nb)
244 {
245     comm.send(nb.get_ctrl_mbox(), new message(message::CTRL_CLOSE, 0.0));
246     comm.send(nb.get_data_mbox(), new message(message::DATA_CLOSE, 0.0));    
247 }
248
249 void process::finalize()
250 {
251     using namespace std::tr1;
252     using namespace std::tr1::placeholders;
253
254     DEBUG2("send CLOSE to %d neighbor%s.",
255            (int )neigh.size(), ESSE(neigh.size()));
256     std::for_each(neigh.begin(), neigh.end(),
257                   bind(&process::finalize1, this, _1));
258
259     DEBUG2("wait for CLOSE from %d neighbor%s.",
260            (int )neigh.size(), ESSE(neigh.size()));
261     receive(WAIT_FOR_CLOSE);
262
263     comm.flush(true);
264 }
265
266 void process::print_loads(e_xbt_log_priority_t logp)
267 {
268     if (!LOG_ISENABLED(logp))
269         return;
270
271     std::ostringstream oss;
272     if (neigh.empty()) {
273         oss << "no neighbor!";
274     } else {
275         std::transform(neigh.begin(), neigh.end() - 1,
276                        std::ostream_iterator<double>(oss, ", "),
277                        std::tr1::mem_fn(&neighbor::get_load));
278         oss << neigh.back().get_load();
279     }
280     LOG1(logp, "Neighbor loads: %s", oss.str().c_str());
281 }
282
283 // Local variables:
284 // mode: c++
285 // End: