Logo AND Algorithmique Numérique Distribuée

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