Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
4b5648ca0412cb24df75b62ac9ca10e81e53d669
[simgrid.git] / src / smpi / smpi_bench.c
1 /* Copyright (c) 2007, 2009-2013. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5   * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include "private.h"
8 #include "xbt/dict.h"
9 #include "xbt/sysdep.h"
10 #include "xbt/ex.h"
11 #include "xbt/hash.h"
12 #include "surf/surf.h"
13 #include "simgrid/sg_config.h"
14
15 #ifndef WIN32
16 #include <sys/mman.h>
17 #endif
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <math.h> // sqrt
23 #include <unistd.h>
24 #include <string.h>
25 #include <stdio.h>
26
27 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(smpi_bench, smpi,
28                                 "Logging specific to SMPI (benchmarking)");
29
30 /* Shared allocations are handled through shared memory segments.
31  * Associated data and metadata are used as follows:
32  *
33  *                                                                    mmap #1
34  *    `allocs' dict                                                     ---- -.
35  *    ----------      shared_data_t               shared_metadata_t   / |  |  |
36  * .->| <name> | ---> -------------------- <--.   -----------------   | |  |  |
37  * |  ----------      | fd of <name>     |    |   | size of mmap  | --| |  |  |
38  * |                  | count (2)        |    |-- | data          |   \ |  |  |
39  * `----------------- | <name>           |    |   -----------------     ----  |
40  *                    --------------------    |   ^                           |
41  *                                            |   |                           |
42  *                                            |   |   `allocs_metadata' dict  |
43  *                                            |   |   ----------------------  |
44  *                                            |   `-- | <addr of mmap #1>  |<-'
45  *                                            |   .-- | <addr of mmap #2>  |<-.
46  *                                            |   |   ----------------------  |
47  *                                            |   |                           |
48  *                                            |   |                           |
49  *                                            |   |                           |
50  *                                            |   |                   mmap #2 |
51  *                                            |   v                     ---- -'
52  *                                            |   shared_metadata_t   / |  |
53  *                                            |   -----------------   | |  |
54  *                                            |   | size of mmap  | --| |  |
55  *                                            `-- | data          |   | |  |
56  *                                                -----------------   | |  |
57  *                                                                    \ |  |
58  *                                                                      ----
59  */
60
61 #define PTR_STRLEN (2 + 2 * sizeof(void*) + 1)
62
63 xbt_dict_t allocs = NULL;          /* Allocated on first use */
64 xbt_dict_t allocs_metadata = NULL; /* Allocated on first use */
65 xbt_dict_t samples = NULL;         /* Allocated on first use */
66 xbt_dict_t calls = NULL;           /* Allocated on first use */
67 __thread int smpi_current_rank = 0;      /* Updated after each MPI call */
68
69 typedef struct {
70   int fd;
71   int count;
72   char* loc;
73 } shared_data_t;
74
75 typedef struct  {
76   size_t size;
77   shared_data_t* data;
78 } shared_metadata_t;
79
80 static size_t shm_size(int fd) {
81   struct stat st;
82
83   if(fstat(fd, &st) < 0) {
84     xbt_die("Could not stat fd %d: %s", fd, strerror(errno));
85   }
86   return (size_t)st.st_size;
87 }
88
89 #ifndef WIN32
90 static void* shm_map(int fd, size_t size, shared_data_t* data) {
91   void* mem;
92   char loc[PTR_STRLEN];
93   shared_metadata_t* meta;
94
95   if(size > shm_size(fd)) {
96     if(ftruncate(fd, (off_t)size) < 0) {
97       xbt_die("Could not truncate fd %d to %zu: %s", fd, size, strerror(errno));
98     }
99   }
100
101   mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
102   if(mem == MAP_FAILED) {
103     xbt_die("Could not map fd %d: %s", fd, strerror(errno));
104   }
105   if(!allocs_metadata) {
106     allocs_metadata = xbt_dict_new_homogeneous(xbt_free);
107   }
108   snprintf(loc, PTR_STRLEN, "%p", mem);
109   meta = xbt_new(shared_metadata_t, 1);
110   meta->size = size;
111   meta->data = data;
112   xbt_dict_set(allocs_metadata, loc, meta, NULL);
113   XBT_DEBUG("MMAP %zu to %p", size, mem);
114   return mem;
115 }
116 #endif
117
118 void smpi_bench_destroy(void)
119 {
120   xbt_dict_free(&allocs);
121   xbt_dict_free(&allocs_metadata);
122   xbt_dict_free(&samples);
123   xbt_dict_free(&calls);
124 }
125
126 void smpi_execute_flops(double flops) {
127   smx_action_t action;
128   smx_host_t host;
129   host = SIMIX_host_self();
130   XBT_DEBUG("Handle real computation time: %f flops", flops);
131   action = simcall_host_execute("computation", host, flops, 1);
132 #ifdef HAVE_TRACING
133   simcall_set_category (action, TRACE_internal_smpi_get_category());
134 #endif
135   simcall_host_execution_wait(action);
136 }
137
138 static void smpi_execute(double duration)
139 {
140   /* FIXME: a global variable would be less expensive to consult than a call to xbt_cfg_get_double() right on the critical path */
141   if (duration >= sg_cfg_get_double("smpi/cpu_threshold")) {
142     XBT_DEBUG("Sleep for %f to handle real computation time", duration);
143     double flops = duration *
144         sg_cfg_get_double("smpi/running_power");
145 #ifdef HAVE_TRACING
146     int rank = smpi_process_index();
147     instr_extra_data extra = xbt_new(s_instr_extra_data_t,1);
148     extra->type=TRACING_COMPUTING;
149     extra->comp_size=flops;
150     TRACE_smpi_computing_in(rank, extra);
151 #endif
152     smpi_execute_flops(flops);
153
154 #ifdef HAVE_TRACING
155   TRACE_smpi_computing_out(rank);
156 #endif
157
158   } else {
159     XBT_DEBUG("Real computation took %f while option smpi/cpu_threshold is set to %f => ignore it",
160         duration, sg_cfg_get_double("smpi/cpu_threshold"));
161   }
162 }
163
164 void smpi_bench_begin(void)
165 {
166   xbt_os_threadtimer_start(smpi_process_timer());
167   smpi_current_rank = smpi_process_index();
168 }
169
170 void smpi_bench_end(void)
171 {
172   xbt_os_timer_t timer = smpi_process_timer();
173
174   xbt_os_threadtimer_stop(timer);
175   smpi_execute(xbt_os_timer_elapsed(timer));
176 }
177
178 unsigned int smpi_sleep(unsigned int secs)
179 {
180   smx_action_t action;
181
182   smpi_bench_end();
183
184   double flops = (double) secs*simcall_host_get_speed(SIMIX_host_self());
185   XBT_DEBUG("Sleep for: %f flops", flops);
186   action = simcall_host_execute("computation", SIMIX_host_self(), flops, 1);
187   #ifdef HAVE_TRACING
188     simcall_set_category (action, TRACE_internal_smpi_get_category());
189   #endif
190   simcall_host_execution_wait(action);
191
192   smpi_bench_begin();
193   return secs;
194 }
195
196 int smpi_gettimeofday(struct timeval *tv)
197 {
198   double now;
199   smpi_bench_end();
200   now = SIMIX_get_clock();
201   if (tv) {
202     tv->tv_sec = (time_t)now;
203 #ifdef WIN32
204     tv->tv_usec = (useconds_t)((now - tv->tv_sec) * 1e6);
205 #else
206     tv->tv_usec = (suseconds_t)((now - tv->tv_sec) * 1e6);
207 #endif
208   }
209   smpi_bench_begin();
210   return 0;
211 }
212
213 extern double sg_maxmin_precision;
214 unsigned long long smpi_rastro_resolution (void)
215 {
216   smpi_bench_end();
217   double resolution = (1/sg_maxmin_precision);
218   smpi_bench_begin();
219   return (unsigned long long)resolution;
220 }
221
222 unsigned long long smpi_rastro_timestamp (void)
223 {
224   smpi_bench_end();
225   double now = SIMIX_get_clock();
226
227   unsigned long long sec = (unsigned long long)now;
228   unsigned long long pre = (now - sec) * smpi_rastro_resolution();
229   smpi_bench_begin();
230   return (unsigned long long)sec * smpi_rastro_resolution() + pre;
231 }
232
233 /* ****************************** Functions related to the SMPI_SAMPLE_ macros ************************************/
234 typedef struct {
235   int iters;        /* amount of requested iterations */
236   int count;        /* amount of iterations done so far */
237   double threshold; /* maximal stderr requested (if positive) */
238   double relstderr; /* observed stderr so far */
239   double mean;      /* mean of benched times, to be used if the block is disabled */
240   double sum;       /* sum of benched times (to compute the mean and stderr) */
241   double sum_pow2;  /* sum of the square of the benched times (to compute the stderr) */
242   int benching;     /* 1: we are benchmarking; 0: we have enough data, no bench anymore */
243 } local_data_t;
244
245 int smpi_sample_is_running = 0;
246
247 static char *sample_location(int global, const char *file, int line) {
248   if (global) {
249     return bprintf("%s:%d", file, line);
250   } else {
251     return bprintf("%s:%d:%d", file, line, smpi_process_index());
252   }
253 }
254 static int sample_enough_benchs(local_data_t *data) {
255   int res = data->count >= data->iters;
256   if (data->threshold>0.0) {
257     if (data->count <2)
258       res = 0; // not enough data
259     if (data->relstderr > data->threshold)
260       res = 0; // stderr too high yet
261   }
262   XBT_DEBUG("%s (count:%d iter:%d stderr:%f thres:%f mean:%fs)",
263       (res?"enough benchs":"need more data"),
264       data->count, data->iters, data->relstderr, data->threshold, data->mean);
265   return res;
266 }
267
268 void smpi_sample_1(int global, const char *file, int line, int iters, double threshold)
269 {
270   char *loc = sample_location(global, file, line);
271   local_data_t *data;
272
273   smpi_bench_end();     /* Take time from previous, unrelated computation into account */
274   smpi_sample_is_running++;
275
276   if (!samples)
277     samples = xbt_dict_new_homogeneous(free);
278
279   data = xbt_dict_get_or_null(samples, loc);
280   if (!data) {
281     xbt_assert(threshold>0 || iters>0,
282         "You should provide either a positive amount of iterations to bench, or a positive maximal stderr (or both)");
283     data = (local_data_t *) xbt_new(local_data_t, 1);
284     data->count = 0;
285     data->sum = 0.0;
286     data->sum_pow2 = 0.0;
287     data->iters = iters;
288     data->threshold = threshold;
289     data->benching = 1; // If we have no data, we need at least one
290     data->mean = 0;
291     xbt_dict_set(samples, loc, data, NULL);
292     XBT_DEBUG("XXXXX First time ever on benched nest %s.",loc);
293   } else {
294     if (data->iters != iters || data->threshold != threshold) {
295       XBT_ERROR("Asked to bench block %s with different settings %d, %f is not %d, %f. How did you manage to give two numbers at the same line??",
296           loc, data->iters, data->threshold, iters,threshold);
297       THROW_IMPOSSIBLE;
298     }
299
300     // if we already have some data, check whether sample_2 should get one more bench or whether it should emulate the computation instead
301     data->benching = !sample_enough_benchs(data);
302     XBT_DEBUG("XXXX Re-entering the benched nest %s. %s",loc, (data->benching?"more benching needed":"we have enough data, skip computes"));
303   }
304   xbt_free(loc);
305 }
306
307 int smpi_sample_2(int global, const char *file, int line)
308 {
309   char *loc = sample_location(global, file, line);
310   local_data_t *data;
311
312   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
313   data = xbt_dict_get(samples, loc);
314   XBT_DEBUG("sample2 %s",loc);
315   xbt_free(loc);
316
317   if (data->benching==1) {
318     // we need to run a new bench
319     XBT_DEBUG("benchmarking: count:%d iter:%d stderr:%f thres:%f; mean:%f",
320         data->count, data->iters, data->relstderr, data->threshold, data->mean);
321     smpi_bench_begin();
322     return 1;
323   } else {
324     // Enough data, no more bench (either we got enough data from previous visits to this benched nest, or we just ran one bench and need to bail out now that our job is done).
325     // Just sleep instead
326     XBT_DEBUG("No benchmark (either no need, or just ran one): count >= iter (%d >= %d) or stderr<thres (%f<=%f). apply the %fs delay instead",
327         data->count, data->iters, data->relstderr, data->threshold, data->mean);
328     smpi_execute(data->mean);
329
330     smpi_sample_is_running--;
331     smpi_bench_begin(); // prepare to capture future, unrelated computations
332     return 0;
333   }
334 }
335
336
337 void smpi_sample_3(int global, const char *file, int line)
338 {
339   char *loc = sample_location(global, file, line);
340   local_data_t *data;
341
342   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
343   data = xbt_dict_get(samples, loc);
344   XBT_DEBUG("sample3 %s",loc);
345   xbt_free(loc);
346
347   if (data->benching==0) {
348     THROW_IMPOSSIBLE;
349   }
350
351   // ok, benchmarking this loop is over
352   xbt_os_threadtimer_stop(smpi_process_timer());
353
354   // update the stats
355   double sample, n;
356   data->count++;
357   sample = xbt_os_timer_elapsed(smpi_process_timer());
358   data->sum += sample;
359   data->sum_pow2 += sample * sample;
360   n = (double)data->count;
361   data->mean = data->sum / n;
362   data->relstderr = sqrt((data->sum_pow2 / n - data->mean * data->mean) / n) / data->mean;
363   if (!sample_enough_benchs(data)) {
364     data->mean = sample; // Still in benching process; We want sample_2 to simulate the exact time of this loop occurrence before leaving, not the mean over the history
365   }
366   XBT_DEBUG("Average mean after %d steps is %f, relative standard error is %f (sample was %f)", data->count,
367       data->mean, data->relstderr, sample);
368
369   // That's enough for now, prevent sample_2 to run the same code over and over
370   data->benching = 0;
371 }
372
373 #ifndef WIN32
374 static void smpi_shared_alloc_free(void *p)
375 {
376   shared_data_t *data = p;
377   xbt_free(data->loc);
378   xbt_free(data);
379 }
380
381 static char *smpi_shared_alloc_hash(char *loc)
382 {
383   char hash[42];
384   char s[7];
385   unsigned val;
386   int i, j;
387
388   xbt_sha(loc, hash);
389   hash[41] = '\0';
390   s[6] = '\0';
391   loc = xbt_realloc(loc, 30);
392   loc[0] = '/';
393   for (i = 0; i < 40; i += 6) { /* base64 encode */
394     memcpy(s, hash + i, 6);
395     val = strtoul(s, NULL, 16);
396     for (j = 0; j < 4; j++) {
397       unsigned char x = (val >> (18 - 3 * j)) & 0x3f;
398       loc[1 + 4 * i / 6 + j] =
399         "ABCDEFGHIJKLMNOPQRSTUVZXYZabcdefghijklmnopqrstuvzxyz0123456789-_"[x];
400     }
401   }
402   loc[29] = '\0';
403   return loc;
404 }
405
406 void *smpi_shared_malloc(size_t size, const char *file, int line)
407 {
408   void* mem;
409   if (sg_cfg_get_boolean("smpi/use_shared_malloc")){
410     char *loc = bprintf("%zu_%s_%d", (size_t)getpid(), file, line);
411     int fd;
412     shared_data_t *data;
413     loc = smpi_shared_alloc_hash(loc); /* hash loc, in order to have something
414                                         * not too long */
415     if (!allocs) {
416       allocs = xbt_dict_new_homogeneous(smpi_shared_alloc_free);
417     }
418     data = xbt_dict_get_or_null(allocs, loc);
419     if (!data) {
420       fd = shm_open(loc, O_RDWR | O_CREAT | O_EXCL,
421                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
422       if (fd < 0) {
423         switch(errno) {
424           case EEXIST:
425             xbt_die("Please cleanup /dev/shm/%s", loc);
426           default:
427             xbt_die("An unhandled error occured while opening %s. shm_open: %s", loc, strerror(errno));
428         }
429       }
430       data = xbt_new(shared_data_t, 1);
431       data->fd = fd;
432       data->count = 1;
433       data->loc = loc;
434       mem = shm_map(fd, size, data);
435       if (shm_unlink(loc) < 0) {
436         XBT_WARN("Could not early unlink %s. shm_unlink: %s", loc, strerror(errno));
437       }
438       xbt_dict_set(allocs, loc, data, NULL);
439       XBT_DEBUG("Mapping %s at %p through %d", loc, mem, fd);
440     } else {
441       xbt_free(loc);
442       mem = shm_map(data->fd, size, data);
443       data->count++;
444     }
445     XBT_DEBUG("Shared malloc %zu in %p (metadata at %p)", size, mem, data);
446   } else {
447     mem = xbt_malloc(size);
448     XBT_DEBUG("Classic malloc %zu in %p", size, mem);
449   }
450
451   return mem;
452 }
453 void smpi_shared_free(void *ptr)
454 {
455   char loc[PTR_STRLEN];
456   shared_metadata_t* meta;
457   shared_data_t* data;
458   if (sg_cfg_get_boolean("smpi/use_shared_malloc")){
459   
460     if (!allocs) {
461       XBT_WARN("Cannot free: nothing was allocated");
462       return;
463     }
464     if(!allocs_metadata) {
465       XBT_WARN("Cannot free: no metadata was allocated");
466     }
467     snprintf(loc, PTR_STRLEN, "%p", ptr);
468     meta = (shared_metadata_t*)xbt_dict_get_or_null(allocs_metadata, loc);
469     if (!meta) {
470       XBT_WARN("Cannot free: %p was not shared-allocated by SMPI", ptr);
471       return;
472     }
473     data = meta->data;
474     if(!data) {
475       XBT_WARN("Cannot free: something is broken in the metadata link");
476       return;
477     }
478     if(munmap(ptr, meta->size) < 0) {
479       XBT_WARN("Unmapping of fd %d failed: %s", data->fd, strerror(errno));
480     }
481     data->count--;
482     XBT_DEBUG("Shared free - no removal - of %p, count = %d", ptr, data->count);
483     if (data->count <= 0) {
484       close(data->fd);
485       xbt_dict_remove(allocs, data->loc);
486       XBT_DEBUG("Shared free - with removal - of %p", ptr);
487     }
488   }else{
489     XBT_DEBUG("Classic free of %p", ptr);
490     xbt_free(ptr);
491   }
492 }
493 #endif
494
495 int smpi_shared_known_call(const char* func, const char* input) {
496    char* loc = bprintf("%s:%s", func, input);
497    xbt_ex_t ex;
498    int known;
499
500    if(!calls) {
501       calls = xbt_dict_new_homogeneous(NULL);
502    }
503    TRY {
504       xbt_dict_get(calls, loc); /* Succeed or throw */
505       known = 1;
506    }
507    CATCH(ex) {
508       if(ex.category == not_found_error) {
509          known = 0;
510          xbt_ex_free(ex);
511       } else {
512          RETHROW;
513       }
514    }
515    free(loc);
516    return known;
517 }
518
519 void* smpi_shared_get_call(const char* func, const char* input) {
520    char* loc = bprintf("%s:%s", func, input);
521    void* data;
522
523    if(!calls) {
524       calls = xbt_dict_new_homogeneous(NULL);
525    }
526    data = xbt_dict_get(calls, loc);
527    free(loc);
528    return data;
529 }
530
531 void* smpi_shared_set_call(const char* func, const char* input, void* data) {
532    char* loc = bprintf("%s:%s", func, input);
533
534    if(!calls) {
535       calls = xbt_dict_new_homogeneous(NULL);
536    }
537    xbt_dict_set(calls, loc, data, NULL);
538    free(loc);
539    return data;
540 }