Logo AND Algorithmique Numérique Distribuée

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