Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
rewrite this example to betterly demo SMPI_SAMPLE_LOCAL and SMPI_SAMPLE_GLOBAL
[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 typedef struct {
112   int count;
113   double sum;
114   double sum_pow2;
115   double mean;
116   double relstderr;
117   int iters;
118   double threshold;
119   int running;
120 } local_data_t;
121
122 void smpi_bench_destroy(void)
123 {
124   xbt_dict_free(&allocs);
125   xbt_dict_free(&samples);
126   xbt_dict_free(&calls);
127 }
128
129 static void smpi_execute_flops(double flops)
130 {
131   smx_action_t action;
132   smx_host_t host;
133   host = SIMIX_host_self();
134
135   XBT_DEBUG("Handle real computation time: %f flops", flops);
136   action = simcall_host_execute("computation", host, flops, 1);
137 #ifdef HAVE_TRACING
138   simcall_set_category (action, TRACE_internal_smpi_get_category());
139 #endif
140   simcall_host_execution_wait(action);
141 }
142
143 static void smpi_execute(double duration)
144 {
145   /* FIXME: a global variable would be less expensive to consult than a call to xbt_cfg_get_double() right on the critical path */
146   if (duration >= xbt_cfg_get_double(_surf_cfg_set, "smpi/cpu_threshold")) {
147     XBT_DEBUG("Sleep for %f to handle real computation time", duration);
148     smpi_execute_flops(duration *
149                        xbt_cfg_get_double(_surf_cfg_set,
150                                           "smpi/running_power"));
151   } else {
152     XBT_DEBUG("Real computation took %f while threshold is set to %f; ignore it",
153         duration, xbt_cfg_get_double(_surf_cfg_set, "smpi/cpu_threshold"));
154   }
155 }
156
157 void smpi_bench_begin(void)
158 {
159   xbt_os_timer_start(smpi_process_timer());
160   smpi_current_rank = smpi_process_index();
161 }
162
163 void smpi_bench_end(void)
164 {
165   xbt_os_timer_t timer = smpi_process_timer();
166
167   xbt_os_timer_stop(timer);
168   smpi_execute(xbt_os_timer_elapsed(timer));
169 }
170
171 unsigned int smpi_sleep(unsigned int secs)
172 {
173   smpi_bench_end();
174   smpi_execute((double) secs);
175   smpi_bench_begin();
176   return secs;
177 }
178
179 int smpi_gettimeofday(struct timeval *tv, struct timezone *tz)
180 {
181   double now;
182   smpi_bench_end();
183   now = SIMIX_get_clock();
184   if (tv) {
185     tv->tv_sec = (time_t)now;
186     tv->tv_usec = (suseconds_t)((now - tv->tv_sec) * 1e6);
187   }
188   smpi_bench_begin();
189   return 0;
190 }
191
192 extern double sg_maxmin_precision;
193 unsigned long long smpi_rastro_resolution (void)
194 {
195   smpi_bench_end();
196   double resolution = (1/sg_maxmin_precision);
197   smpi_bench_begin();
198   return (unsigned long long)resolution;
199 }
200
201 unsigned long long smpi_rastro_timestamp (void)
202 {
203   smpi_bench_end();
204   double now = SIMIX_get_clock();
205
206   unsigned long long sec = (unsigned long long)now;
207   unsigned long long pre = (now - sec) * smpi_rastro_resolution();
208   smpi_bench_begin();
209   return (unsigned long long)sec * smpi_rastro_resolution() + pre;
210 }
211
212 static char *sample_location(int global, const char *file, int line)
213 {
214   if (global) {
215     return bprintf("%s:%d", file, line);
216   } else {
217     return bprintf("%s:%d:%d", file, line, smpi_process_index());
218   }
219 }
220
221 int smpi_sample_1(int global, const char *file, int line, int iters, double threshold)
222 {
223   char *loc = sample_location(global, file, line);
224   local_data_t *data;
225
226   smpi_bench_end();     /* Take time from previous MPI call into account */
227   if (!samples) {
228     samples = xbt_dict_new_homogeneous(free);
229   }
230   data = xbt_dict_get_or_null(samples, loc);
231   if (!data) {
232     data = (local_data_t *) xbt_new(local_data_t, 1);
233     data->count = 0;
234     data->sum = 0.0;
235     data->sum_pow2 = 0.0;
236     data->iters = iters;
237     data->threshold = threshold;
238     data->running = 0;
239     xbt_dict_set(samples, loc, data, NULL);
240     return 0;
241   }
242   free(loc);
243   return 1;
244 }
245
246 int smpi_sample_2(int global, const char *file, int line)
247 {
248   char *loc = sample_location(global, file, line);
249   local_data_t *data;
250
251   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
252   data = xbt_dict_get(samples, loc);
253
254   if (!data->running) {
255     if ((data->iters > 0 && data->count >= data->iters)
256         || (data->count > 1 && data->threshold > 0.0 && data->relstderr <= data->threshold)) {
257       XBT_DEBUG("Perform some wait of %f", data->mean);
258       smpi_execute(data->mean);
259     } else {
260       data->running = 1;
261       data->count++;
262     }
263   } else {
264     data->running = 0;
265   }
266   free(loc);
267   smpi_bench_begin();
268   smpi_process_simulated_start();
269   return data->running;
270 }
271
272 void smpi_sample_3(int global, const char *file, int line)
273 {
274   char *loc = sample_location(global, file, line);
275   local_data_t *data;
276   double sample, n;
277
278   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
279   data = xbt_dict_get(samples, loc);
280   smpi_bench_end();
281   if(data->running && data->count < data->iters) {
282     sample = smpi_process_simulated_elapsed();
283     data->sum += sample;
284     data->sum_pow2 += sample * sample;
285     n = (double)data->count;
286     data->mean = data->sum / n;
287     data->relstderr = sqrt((data->sum_pow2 / n - data->mean * data->mean) / n) / data->mean;
288     XBT_DEBUG("Average mean after %d steps is %f, relative standard error is %f (sample was %f)", data->count,
289            data->mean, data->relstderr, sample);
290   }
291   free(loc);
292 }
293
294 void smpi_sample_flops(double flops)
295 {
296   smpi_execute_flops(flops);
297 }
298
299 void *smpi_shared_malloc(size_t size, const char *file, int line)
300 {
301   char *loc = bprintf("%zu_%s_%d", (size_t)getpid(), file, line);
302   size_t len = strlen(loc);
303   size_t i;
304   int fd;
305   void* mem;
306   shared_data_t *data;
307
308   for(i = 0; i < len; i++) {
309     /* Make the 'loc' ID be a flat filename */
310     if(loc[i] == '/') {
311       loc[i] = '_';
312     }
313   }
314   if (!allocs) {
315     allocs = xbt_dict_new_homogeneous(free);
316   }
317   data = xbt_dict_get_or_null(allocs, loc);
318   if(!data) {
319     fd = shm_open(loc, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
320     if(fd < 0) {
321       switch(errno) {
322         case EEXIST:
323           xbt_die("Please cleanup /dev/shm/%s", loc);
324         default:
325           xbt_die("An unhandled error occured while opening %s: %s", loc, strerror(errno));
326       }
327     }
328     data = xbt_new(shared_data_t, 1);
329     data->fd = fd;
330     data->count = 1;
331     data->loc = loc;
332     mem = shm_map(fd, size, data);
333     if(shm_unlink(loc) < 0) {
334       XBT_WARN("Could not early unlink %s: %s", loc, strerror(errno));
335     }
336     xbt_dict_set(allocs, loc, data, NULL);
337     XBT_DEBUG("Mapping %s at %p through %d", loc, mem, fd);
338   } else {
339     mem = shm_map(data->fd, size, data);
340     data->count++;
341   }
342   XBT_DEBUG("Malloc %zu in %p (metadata at %p)", size, mem, data);
343   return mem;
344 }
345
346 void smpi_shared_free(void *ptr)
347 {
348   char loc[PTR_STRLEN];
349   shared_metadata_t* meta;
350   shared_data_t* data;
351
352   if (!allocs) {
353     XBT_WARN("Cannot free: nothing was allocated");
354     return;
355   }
356   if(!allocs_metadata) {
357     XBT_WARN("Cannot free: no metadata was allocated");
358   }
359   snprintf(loc, PTR_STRLEN, "%p", ptr);
360   meta = (shared_metadata_t*)xbt_dict_get_or_null(allocs_metadata, loc);
361   if (!meta) {
362     XBT_WARN("Cannot free: %p was not shared-allocated by SMPI", ptr);
363     return;
364   }
365   data = meta->data;
366   if(!data) {
367     XBT_WARN("Cannot free: something is broken in the metadata link");
368     return;
369   }
370   if(munmap(ptr, meta->size) < 0) {
371     XBT_WARN("Unmapping of fd %d failed: %s", data->fd, strerror(errno));
372   }
373   data->count--;
374   if (data->count <= 0) {
375     close(data->fd);
376     xbt_dict_remove(allocs, data->loc);
377     free(data->loc);
378   }
379 }
380
381 int smpi_shared_known_call(const char* func, const char* input) {
382    char* loc = bprintf("%s:%s", func, input);
383    xbt_ex_t ex;
384    int known;
385
386    if(!calls) {
387       calls = xbt_dict_new_homogeneous(NULL);
388    }
389    TRY {
390       xbt_dict_get(calls, loc); /* Succeed or throw */
391       known = 1;
392    }
393    CATCH(ex) {
394       if(ex.category == not_found_error) {
395          known = 0;
396          xbt_ex_free(ex);
397       } else {
398          RETHROW;
399       }
400    }
401    free(loc);
402    return known;
403 }
404
405 void* smpi_shared_get_call(const char* func, const char* input) {
406    char* loc = bprintf("%s:%s", func, input);
407    void* data;
408
409    if(!calls) {
410       calls = xbt_dict_new_homogeneous(NULL);
411    }
412    data = xbt_dict_get(calls, loc);
413    free(loc);
414    return data;
415 }
416
417 void* smpi_shared_set_call(const char* func, const char* input, void* data) {
418    char* loc = bprintf("%s:%s", func, input);
419
420    if(!calls) {
421       calls = xbt_dict_new_homogeneous(NULL);
422    }
423    xbt_dict_set(calls, loc, data, NULL);
424    free(loc);
425    return data;
426 }