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

Private GIT Repository
339938a703df6b4215fb45e04c9b02049fcc5016
[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");
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     return 0.0;
133 }
134
135 void process::compute()
136 {
137     double duration = opt::comp_cost(load);
138     m_task_t task = MSG_task_create("computation", duration, 0.0, NULL);
139     DEBUG2("compute %g flop%s.", duration, ESSE(duration));
140     MSG_task_execute(task);
141     MSG_task_destroy(task);
142 }
143
144 void process::send1_no_bookkeeping(neighbor& nb)
145 {
146     if (load != prev_load_broadcast)
147         comm.send(nb.get_ctrl_mbox(), new message(message::INFO, load));
148     double load_to_send = nb.get_to_send();
149     if (load_to_send > 0.0) {
150         comm.send(nb.get_data_mbox(), new message(message::LOAD, load_to_send));
151         nb.set_to_send(0.0);
152     }
153 }
154
155 void process::send1_bookkeeping(neighbor& nb)
156 {
157     if (expected_load != prev_load_broadcast)
158         comm.send(nb.get_ctrl_mbox(),
159                   new message(message::INFO, expected_load));
160     double load_to_send;
161     double new_debt;
162     double debt_to_send = nb.get_to_send();
163     if (debt_to_send > 0.0) {
164         comm.send(nb.get_ctrl_mbox(),
165                   new message(message::CREDIT, debt_to_send));
166         nb.set_to_send(0.0);
167         new_debt = nb.get_debt() + debt_to_send;
168     } else {
169         new_debt = nb.get_debt();
170     }
171     if (load <= new_debt) {
172         load_to_send = load;
173         nb.set_debt(new_debt - load_to_send);
174         load = 0.0;
175     } else {
176         load_to_send = new_debt;
177         nb.set_debt(0.0);
178         load -= load_to_send;
179     }
180     if (load_to_send > 0.0)
181         comm.send(nb.get_data_mbox(), new message(message::LOAD, load_to_send));
182 }
183
184 void process::send()
185 {
186     using namespace std::tr1;
187     using namespace std::tr1::placeholders;
188
189     // fixme: shall we send data at all iterations?
190     if (opt::bookkeeping) {
191         std::for_each(neigh.begin(), neigh.end(),
192                       bind(&process::send1_bookkeeping, this, _1));
193         prev_load_broadcast = expected_load;
194     } else {
195         std::for_each(neigh.begin(), neigh.end(),
196                       bind(&process::send1_no_bookkeeping, this, _1));
197         prev_load_broadcast = load;
198     }
199 }
200
201 // Returns false if a CLOSE message was received. 
202 bool process::receive(recv_wait_mode wait)
203 {
204     bool result = true;
205     message* msg;
206     m_host_t from;
207     bool do_recv = ctrl_close_pending || data_close_pending;
208     while (do_recv && comm.recv(msg, from, wait)) {
209         switch (msg->get_type()) {
210         case message::INFO: {
211             neighbor* n = rev_neigh[from];
212             n->set_load(msg->get_amount());
213             break;
214         }
215         case message::CREDIT:
216             expected_load += msg->get_amount();
217             break;
218         case message::LOAD:
219             load += msg->get_amount();
220             break;
221         case message::CTRL_CLOSE:
222             if (--ctrl_close_pending == 1)
223                 comm.next_close_on_ctrl_is_last();
224             DEBUG1("ctrl_close_pending = %d", ctrl_close_pending);
225             result = false;
226             break;
227         case message::DATA_CLOSE:
228             if (--data_close_pending == 1)
229                 comm.next_close_on_data_is_last();
230             DEBUG1("data_close_pending = %d", data_close_pending);
231             result = false;
232             break;
233         }
234         delete msg;
235         do_recv = (wait == WAIT_FOR_CLOSE) &&
236             (ctrl_close_pending || data_close_pending);
237     }
238     return result;
239 }
240
241 void process::finalize1(neighbor& nb)
242 {
243     comm.send(nb.get_ctrl_mbox(), new message(message::CTRL_CLOSE, 0.0));
244     comm.send(nb.get_data_mbox(), new message(message::DATA_CLOSE, 0.0));    
245 }
246
247 void process::finalize()
248 {
249     using namespace std::tr1;
250     using namespace std::tr1::placeholders;
251
252     DEBUG2("send CLOSE to %d neighbor%s.",
253            (int )neigh.size(), ESSE(neigh.size()));
254     std::for_each(neigh.begin(), neigh.end(),
255                   bind(&process::finalize1, this, _1));
256
257     DEBUG2("wait for CLOSE from %d neighbor%s.",
258            (int )neigh.size(), ESSE(neigh.size()));
259     receive(WAIT_FOR_CLOSE);
260
261     comm.flush(true);
262 }
263
264 void process::print_loads(e_xbt_log_priority_t logp)
265 {
266     if (!LOG_ISENABLED(logp))
267         return;
268
269     std::ostringstream oss;
270     if (neigh.empty()) {
271         oss << "no neighbor!";
272     } else {
273         std::transform(neigh.begin(), neigh.end() - 1,
274                        std::ostream_iterator<double>(oss, ", "),
275                        std::tr1::mem_fn(&neighbor::get_load));
276         oss << neigh.back().get_load();
277     }
278     LOG1(logp, "Neighbor loads: %s", oss.str().c_str());
279 }
280
281 // Local variables:
282 // mode: c++
283 // End: