Logo AND Algorithmique Numérique Distribuée

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