Main.c 19.4 KB
Newer Older
1
2
3
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
iker_martin's avatar
iker_martin committed
4
#include <fcntl.h>
5
#include <unistd.h>
iker_martin's avatar
iker_martin committed
6
#include <sys/stat.h>
7
8
#include "process_stage.h"
#include "Main_datatypes.h"
9
#include "configuration.h"
10
#include "../IOcodes/results.h"
11
#include "../malleability/CommDist.h"
12
13
#include "../malleability/malleabilityManager.h"
#include "../malleability/malleabilityStates.h"
14

15
16
#define DR_MAX_SIZE 1000000000

iker_martin's avatar
iker_martin committed
17
int work();
18
double iterate(int async_comm);
19
20
double iterate_relaxed(double *time, double *times_stages);
double iterate_rigid(double *time, double *times_stages);
21

22
void init_group_struct(char *argv[], int argc, int myId, int numP);
23
void init_application();
24
void obtain_op_times();
25
26
void free_application_data();

27
void print_general_info(int myId, int grp, int numP);
28
int print_local_results();
29
int print_final_results();
iker_martin's avatar
iker_martin committed
30
int create_out_file(char *nombre, int *ptr, int newstdout);
31

iker_martin's avatar
iker_martin committed
32
33
configuration *config_file;
group_data *group;
34
results_data *results;
35
MPI_Comm comm;
36
int run_id = 0; // Utilizado para diferenciar más fácilmente ejecuciones en el análisis
37

38
int main(int argc, char *argv[]) {
39
    int numP, myId, res;
iker_martin's avatar
iker_martin committed
40
    int req;
41
    int im_child;
42
    size_t i;
43

44
    int num_cpus, num_nodes;
45
    char *nodelist = NULL;
46
    num_cpus = 20; //FIXME NUMERO MAGICO //TODO Usar openMP para obtener el valor con un pragma
47
48
49
    if (argc >= 5) {
      nodelist = argv[3];
      num_nodes = atoi(argv[4]);
50
      num_cpus = num_nodes * num_cpus;
51
52
    }

53
    MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &req);
iker_martin's avatar
iker_martin committed
54
    MPI_Comm_rank(MPI_COMM_WORLD, &myId);
55
    MPI_Comm_size(MPI_COMM_WORLD, &numP);
56
    comm = MPI_COMM_WORLD;
iker_martin's avatar
iker_martin committed
57

58
59
    if(req != MPI_THREAD_MULTIPLE) {
      printf("No se ha obtenido la configuración de hilos necesaria\nSolicitada %d -- Devuelta %d\n", req, MPI_THREAD_MULTIPLE);
60
61
      fflush(stdout);
      MPI_Abort(MPI_COMM_WORLD, -50);
62
63
    }

64
    init_group_struct(argv, argc, myId, numP);
65
    im_child = init_malleability(myId, numP, ROOT, comm, argv[0], nodelist, num_cpus, num_nodes);
66

67
    if(!im_child) { //TODO REFACTOR Simplificar inicio
68
69
      init_application();

70
      set_benchmark_grp(group->grp);
71
72
      set_benchmark_configuration(config_file);

73
74
75
76
      if(config_file->n_groups > 1) {
        set_malleability_configuration(config_file->groups[group->grp+1].sm, config_file->groups[group->grp+1].ss, 
  	  config_file->groups[group->grp+1].phy_dist, config_file->groups[group->grp+1].rm, config_file->groups[group->grp+1].rs);
        set_children_number(config_file->groups[group->grp+1].procs); // TODO TO BE DEPRECATED
77

78
79
80
        malleability_add_data(&(group->grp), 1, MAL_INT, 1, 1);
        malleability_add_data(&run_id, 1, MAL_INT, 1, 1);
        malleability_add_data(&(group->iter_start), 1, MAL_INT, 1, 1);
81
        //malleability_add_data(&(results->exec_start), 1, MAL_DOUBLE, 1, 1);
82
83
84
85
86
87
88
89
90
91
92
93

        if(config_file->sdr) {
	  for(i=0; i<group->sync_data_groups; i++) {
            malleability_add_data(group->sync_array[i], group->sync_qty[i], MAL_CHAR, 0, 1);
	  }
        }
        if(config_file->adr) {
	  for(i=0; i<group->async_data_groups; i++) {
            malleability_add_data(group->async_array[i], group->async_qty[i], MAL_CHAR, 0, 0);
	  }
        }
      }
94

95
      MPI_Barrier(comm);
96
      results->exec_start = MPI_Wtime();
97
    } else { //Init hijos
98

99
      get_malleability_user_comm(&comm);
100
      get_benchmark_configuration(&config_file);
101

102
103
      // TODO Refactor - Que sea una unica funcion
      // Obtiene las variables que van a utilizar los hijos
104
      void *value = NULL;
105
      size_t entries;
106
107
      malleability_get_data(&value, 0, 1, 1);
      group->grp = *((int *)value);
108

109
110
      malleability_get_data(&value, 1, 1, 1);
      run_id = *((int *)value);
111
112
113
      
      malleability_get_data(&value, 2, 1, 1);
      group->iter_start = *((int *)value);
114

115
116
117
      //malleability_get_data(&value, 3, 1, 1);
      //results->exec_start = *((double *)value);

118
119
      if(config_file->sdr) {
        malleability_get_entries(&entries, 0, 1);
120
        group->sync_qty = (int *) malloc(entries * sizeof(int));
121
122
123
124
        group->sync_array = (char **) malloc(entries * sizeof(char *));
	for(i=0; i<entries; i++) {
          malleability_get_data(&value, i, 0, 1);
          group->sync_array[i] = (char *)value;
125
          group->sync_qty[i] = DR_MAX_SIZE;
126
	}
127
128
        group->sync_qty[entries-1] = config_file->sdr % DR_MAX_SIZE ? config_file->sdr % DR_MAX_SIZE : DR_MAX_SIZE;
        group->sync_data_groups = entries;
129
130
131
      }
      if(config_file->adr) {
        malleability_get_entries(&entries, 0, 0);
132
        group->async_qty = (int *) malloc(entries * sizeof(int));
133
134
135
136
        group->async_array = (char **) malloc(entries * sizeof(char *));
	for(i=0; i<entries; i++) {
          malleability_get_data(&value, i, 0, 0);
          group->async_array[i] = (char *)value;
137
          group->async_qty[i] = DR_MAX_SIZE;
138
	}
139
140
        group->async_qty[entries-1] = config_file->adr % DR_MAX_SIZE ? config_file->adr % DR_MAX_SIZE : DR_MAX_SIZE;
        group->async_data_groups = entries;
141
142
      }

143
      group->grp = group->grp + 1;
144
145
      results = malloc(sizeof(results_data));
      init_results_data(results, config_file->n_resizes, config_file->n_stages, config_file->groups[group->grp].iters);
146

147
148
    }

149
150
151
    //
    // EMPIEZA LA EJECUCION-------------------------------
    //
152
153
    group->grp = group->grp - 1; // TODO REFACTOR???
    do {
154
155
156
157

      get_malleability_user_comm(&comm);
      MPI_Comm_size(comm, &(group->numP));
      MPI_Comm_rank(comm, &(group->myId));
158
159
      group->grp = group->grp + 1;
      set_benchmark_grp(group->grp);
160

161
      if(group->grp != 0) {
162
        obtain_op_times(0); //Obtener los nuevos valores de tiempo para el computo
163
        malleability_retrieve_times(&results->spawn_time[group->grp - 1], &results->sync_time[group->grp - 1], &results->async_time[group->grp - 1], &results->malleability_time[group->grp - 1]);
164
      }
165

166
      if(config_file->n_groups != group->grp + 1) { //TODO Llevar a otra funcion
167
        set_malleability_configuration(config_file->groups[group->grp+1].sm, config_file->groups[group->grp+1].ss, 
168
			config_file->groups[group->grp+1].phy_dist, config_file->groups[group->grp+1].rm, config_file->groups[group->grp+1].rs);
169
        set_children_number(config_file->groups[group->grp+1].procs); // TODO TO BE DEPRECATED
170

171
172
        if(group->grp != 0) {
          malleability_modify_data(&(group->grp), 0, 1, MAL_INT, 1, 1);
173
          malleability_modify_data(&(group->iter_start), 2, 1, MAL_INT, 1, 1);
174
        }
175
      }
176
177

      res = work();
178
      if(res == MALL_ZOMBIE) break;
179
      if(res==1) { // Se ha llegado al final de la aplicacion
180
        MPI_Barrier(comm);
181
182
        results->exec_time = MPI_Wtime() - results->exec_start - results->wasted_time;
      }
183

184
      print_local_results();
185
      reset_results_index(results);
186
    } while(config_file->n_groups > group->grp + 1 && config_file->groups[group->grp+1].sm == MALL_SPAWN_MERGE);
187

188
189
190
    //
    // TERMINA LA EJECUCION ----------------------------------------------------------
    //
191
    print_final_results(); // Pasado este punto ya no pueden escribir los procesos
192

193
    MPI_Barrier(comm);
194
195
196
197
    if(comm != MPI_COMM_WORLD && comm != MPI_COMM_NULL) {
      MPI_Comm_free(&comm);
    }

198
    if(group->myId == ROOT && config_file->groups[group->grp].sm == MALL_SPAWN_MERGE) {
199
200
      MPI_Abort(MPI_COMM_WORLD, -100);
    }
201
    free_application_data();
202

203
204
205
206
207
    MPI_Finalize();
    return 0;
}

/*
208
209
210
211
212
213
214
215
216
 * Función de trabajo principal.
 *
 * Incializa los datos para realizar el computo y a continuacion
 * pasa a realizar "maxiter" iteraciones de computo.
 *
 * Terminadas las iteraciones realiza el redimensionado de procesos.
 * Si el redimensionado se realiza de forma asincrona se 
 * siguen realizando iteraciones de computo hasta que termine la 
 * comunicacion asincrona y realizar entonces la sincrona.
217
218
219
220
 *
 * Si el grupo de procesos es el ultimo que va a ejecutar, se devuelve
 * el valor 1 para indicar que no se va a seguir trabajando con nuevos grupos
 * de procesos. En caso contrario se devuelve 0.
221
 */
iker_martin's avatar
iker_martin committed
222
int work() {
223
  int iter, maxiter, state, res;
224

225
  maxiter = config_file->groups[group->grp].iters;
226
  state = MALL_NOT_STARTED;
227

228
  res = 0;
229
  for(iter=group->iter_start; iter < maxiter; iter++) {
230
    iterate(state);
231
  }
232

233
  if(config_file->n_groups != group->grp + 1)
234
235
    state = malleability_checkpoint();

236
  iter = 0;
237
238
  while(state == MALL_DIST_PENDING || state == MALL_SPAWN_PENDING || state == MALL_SPAWN_SINGLE_PENDING || state == MALL_SPAWN_ADAPT_POSTPONE || state == MALL_SPAWN_ADAPT_PENDING) {
    if(group->grp+1 < config_file->n_groups && iter < config_file->groups[group->grp+1].iters) {
239
      iterate(state);
240
241
242
      iter++;
      group->iter_start = iter;
    }
243
    state = malleability_checkpoint();
244
  }
245

246
  
247
  if(config_file->n_groups == group->grp + 1) res=1;
248
  if(state == MALL_ZOMBIE) res=state;
249
  return res;
250
251
}

252
253
254
255
256
257
258
259
260
261

/////////////////////////////////////////
/////////////////////////////////////////
//COMPUTE FUNCTIONS
/////////////////////////////////////////
/////////////////////////////////////////


/*
 * Simula la ejecucción de una iteración de computo en la aplicación
262
263
 * que dura al menos un tiempo determinado por la suma de todas las
 * etapas definidas en la configuracion.
264
 */
265
double iterate(int async_comm) {
266
  double time, *times_stages_aux;
267
  size_t i;
268
269
  double aux = 0;

270
  times_stages_aux = malloc(config_file->n_stages * sizeof(double));
271

272
  if(config_file->rigid_times) {
273
    aux = iterate_rigid(&time, times_stages_aux);
274
  } else {
275
    aux = iterate_relaxed(&time, times_stages_aux);
276
277
  }

278
279
  // Se esta realizando una redistribucion de datos asincrona
  if(async_comm == MALL_DIST_PENDING || async_comm == MALL_SPAWN_PENDING || async_comm == MALL_SPAWN_SINGLE_PENDING) { 
280
  // TODO Que diferencie entre ambas en el IO
281
    results->iters_async += 1;
282
283
  }

284
  // TODO Pasar el resto de este código a results.c
285
  if(results->iter_index == results->iters_size) { // Aumentar tamaño de ambos vectores de resultados
286
    realloc_results_iters(results, config_file->n_stages, results->iters_size + 100);
287
  }
288
  results->iters_time[results->iter_index] = time;
289
  for(i=0; i < config_file->n_stages; i++) {
290
    results->stage_times[i][results->iter_index] = times_stages_aux[i];
291
  }
292
  results->iter_index = results->iter_index + 1;
293
  // TODO Pasar hasta aqui
294

295
  free(times_stages_aux);
296

297
  return aux;
298
299
}

300
301
302

/*
 * Performs an iteration. The gathered times for iterations
303
 * and stages could be IMPRECISE in order to ensure the 
304
305
306
307
308
 * global execution time is precise.
 */
double iterate_relaxed(double *time, double *times_stages) {
  size_t i;
  double start_time, start_time_stage, aux=0;
309
  start_time = MPI_Wtime(); // Imprecise timings
310
311

  for(i=0; i < config_file->n_stages; i++) {
312
    start_time_stage = MPI_Wtime(); 
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
    aux+= process_stage(*config_file, config_file->stages[i], *group, comm);
    times_stages[i] = MPI_Wtime() - start_time_stage;
  }

  *time = MPI_Wtime() - start_time; // Guardar tiempos
  return aux;
}

/*
 * Performs an iteration. The gathered times for iterations
 * and stages are ensured to be precise but the global 
 * execution time could be imprecise.
 */
double iterate_rigid(double *time, double *times_stages) {
  size_t i;
  double start_time, start_time_stage, aux=0;

  MPI_Barrier(comm);
  start_time = MPI_Wtime();

  for(i=0; i < config_file->n_stages; i++) {
    start_time_stage = MPI_Wtime();
    aux+= process_stage(*config_file, config_file->stages[i], *group, comm);
336
    MPI_Barrier(comm);
337
338
339
    times_stages[i] = MPI_Wtime() - start_time_stage;
  }

340
  MPI_Barrier(comm);
341
342
343
344
  *time = MPI_Wtime() - start_time; // Guardar tiempos
  return aux;
}

345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
//======================================================||
//======================================================||
//=============INIT/FREE/PRINT FUNCTIONS================||
//======================================================||
//======================================================||

/*
 * Muestra datos generales sobre los procesos, su grupo,
 * en que nodo residen y la version de MPI utilizada.
 */
void print_general_info(int myId, int grp, int numP) {
  int len;
  char *name = malloc(MPI_MAX_PROCESSOR_NAME * sizeof(char));
  char *version = malloc(MPI_MAX_LIBRARY_VERSION_STRING * sizeof(char));
  MPI_Get_processor_name(name, &len);
  MPI_Get_library_version(version, &len);
  printf("P%d Nuevo GRUPO %d de %d procs en nodo %s con %s\n", myId, grp, numP, name, version);

  free(name);
  free(version);
}

367

368
369
370
/*
 * Pide al proceso raiz imprimir los datos sobre las iteraciones realizadas por el grupo de procesos.
 */
371
372
int print_local_results() {
  int ptr_local, ptr_out, err;
373
374
  char *file_name;

375
  compute_results_iter(results, group->myId, group->numP, ROOT, config_file->n_stages, config_file->capture_method, comm);
376
  if(group->myId == ROOT) {
377
378
    ptr_out = dup(1);

379
380
381
382
383
384
385
386
    file_name = NULL;
    file_name = malloc(40 * sizeof(char));
    if(file_name == NULL) return -1; // No ha sido posible alojar la memoria
    err = snprintf(file_name, 40, "R%d_G%dNP%dID%d.out", run_id, group->grp, group->numP, group->myId);
    if(err < 0) return -2; // No ha sido posible obtener el nombre de fichero
    create_out_file(file_name, &ptr_local, 1);
  
    print_config_group(config_file, group->grp);
387
    print_iter_results(*results);
388
    print_stage_results(*results, config_file->n_stages);
389
390
    free(file_name);

391
    fflush(stdout);
392
393
    close(1);
    dup(ptr_out);
394
    close(ptr_out);
395
396
397
398
399
400
401
402
403
  }
  return 0;
}

/*
 * Si es el ultimo grupo de procesos, pide al proceso raiz mostrar los datos obtenidos de tiempo de ejecucion, creacion de procesos
 * y las comunicaciones.
 */
int print_final_results() {
404
  int ptr_global, err, ptr_out;
405
406
407
408
  char *file_name;

  if(group->myId == ROOT) {

409
    if(config_file->n_groups == group->grp+1) {
410
411
412
413
414
415
      file_name = NULL;
      file_name = malloc(20 * sizeof(char));
      if(file_name == NULL) return -1; // No ha sido posible alojar la memoria
      err = snprintf(file_name, 20, "R%d_Global.out", run_id);
      if(err < 0) return -2; // No ha sido posible obtener el nombre de fichero

416
      ptr_out = dup(1);
417
      create_out_file(file_name, &ptr_global, 1);
418
419
      print_config(config_file);
      print_global_results(*results, config_file->n_resizes);
420
      fflush(stdout);
421
      free(file_name);
422
423
424

      close(1);
      dup(ptr_out);
425
426
427
428
429
430
431
432
433
    }
  }
  return 0;
}

/*
 * Inicializa la estructura group
 */
void init_group_struct(char *argv[], int argc, int myId, int numP) {
434
  group = malloc(sizeof(group_data));
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  group->myId        = myId;
  group->numP        = numP;
  group->grp         = 0;
  group->iter_start  = 0;
  group->argc        = argc;
  group->argv        = argv;
}

/*
 * Inicializa los datos para este grupo de procesos.
 *
 * En caso de ser el primer grupo de procesos, lee el fichero de configuracion
 * e inicializa los vectores de comunicacion.
 *
 * En caso de ser otro grupo de procesos entra a la funcion "Sons_init()" donde
 * se comunican con los padres para inicializar sus datos.
 */
void init_application() {
453
454
  int i, last_index;

455
456
457
458
459
460
461
462
  if(group->argc < 2) {
    printf("Falta el fichero de configuracion. Uso:\n./programa config.ini id\nEl argumento numerico id es opcional\n");
    MPI_Abort(MPI_COMM_WORLD, -1);
  }
  if(group->argc > 2) {
    run_id = atoi(group->argv[2]);
  }

463
  init_config(group->argv[1], &config_file);
464
  results = malloc(sizeof(results_data));
465
  init_results_data(results, config_file->n_resizes, config_file->n_stages, config_file->groups[group->grp].iters);
466
  if(config_file->sdr) {
467
468
469
470
471
472
473
474
475
476
    group->sync_data_groups = config_file->sdr % DR_MAX_SIZE ? config_file->sdr/DR_MAX_SIZE+1 : config_file->sdr/DR_MAX_SIZE;
    group->sync_qty = (int *) malloc(group->sync_data_groups * sizeof(int));
    group->sync_array = (char **) malloc(group->sync_data_groups * sizeof(char *));
    last_index = group->sync_data_groups-1; 
    for(i=0; i<last_index; i++) {
      group->sync_qty[i] = DR_MAX_SIZE;
      malloc_comm_array(&(group->sync_array[i]), group->sync_qty[i], group->myId, group->numP);
    }
    group->sync_qty[last_index] = config_file->sdr % DR_MAX_SIZE ? config_file->sdr % DR_MAX_SIZE : DR_MAX_SIZE;
    malloc_comm_array(&(group->sync_array[last_index]), group->sync_qty[last_index], group->myId, group->numP);
477
  }
478

479
  if(config_file->adr) {
480
481
482
483
484
485
486
487
488
489
    group->async_data_groups = config_file->adr % DR_MAX_SIZE ? config_file->adr/DR_MAX_SIZE+1 : config_file->adr/DR_MAX_SIZE;
    group->async_qty = (int *) malloc(group->async_data_groups * sizeof(int));
    group->async_array = (char **) malloc(group->async_data_groups * sizeof(char *));
    last_index = group->async_data_groups-1; 
    for(i=0; i<last_index; i++) {
      group->async_qty[i] = DR_MAX_SIZE;
      malloc_comm_array(&(group->async_array[i]), group->async_qty[i], group->myId, group->numP);
    }
    group->async_qty[last_index] = config_file->adr % DR_MAX_SIZE ? config_file->adr % DR_MAX_SIZE : DR_MAX_SIZE;
    malloc_comm_array(&(group->async_array[last_index]), group->async_qty[last_index], group->myId, group->numP);
490
  }
491
492

  obtain_op_times(1);
493
494
495
496
}

/*
 * Obtiene cuanto tiempo es necesario para realizar una operacion de PI
497
498
499
500
501
502
503
504
 *
 * Si compute esta a 1 se considera que se esta inicializando el entorno
 * y realizará trabajo extra.
 *
 * Si compute esta a 0 se considera un entorno inicializado y solo hay que
 * realizar algunos cambios de reserva de memoria. Si es necesario recalcular
 * algo se obtiene el total de tiempo utilizado en dichas tareas y se resta
 * al tiempo total de ejecucion.
505
 */
506
void obtain_op_times(int compute) {
507
  size_t i;
508
  double time = 0;
509
  for(i=0; i<config_file->n_stages; i++) {
510
    time+=init_stage(config_file, i, *group, comm, compute);
511
  }
512
  if(!compute) {results->wasted_time += time;}
513
514
515
516
517
518
}

/*
 * Libera toda la memoria asociada con la aplicacion
 */
void free_application_data() {
519
520
521
522
523
524
525
526
527
  size_t i;

  if(config_file->sdr && group->sync_array != NULL) {
    for(i=0; i<group->sync_data_groups; i++) {
      free(group->sync_array[i]);
      group->sync_array[i] = NULL;
    }
    free(group->sync_qty);
    group->sync_qty = NULL;
528
    free(group->sync_array);
529
530
    group->sync_array = NULL;

531
  }
532
533
534
535
536
537
538
  if(config_file->adr && group->async_array != NULL) {
    for(i=0; i<group->async_data_groups; i++) {
      free(group->async_array[i]);
      group->async_array[i] = NULL;
    }
    free(group->async_qty);
    group->async_qty = NULL;
539
    free(group->async_array);
540
    group->async_array = NULL;
541
  }
542
543
  free_malleability();

544
545
546
  free_results_data(results, config_file->n_stages);
  free(results);

547
  free_config(config_file);
548
  
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
  free(group);
}


/* 
 * Función para crear un fichero con el nombre pasado como argumento.
 * Si el nombre ya existe, se escribe la informacion a continuacion.
 *
 * El proceso que llama a la función pasa a tener como salida estandar
 * dicho fichero si el valor "newstdout" es verdadero.
 *
 */
int create_out_file(char *nombre, int *ptr, int newstdout) {
  int err;

  *ptr = open(nombre, O_WRONLY | O_CREAT | O_APPEND, 0644);
  if(*ptr < 0) return -1; // No ha sido posible crear el fichero

  if(newstdout) {
    err = close(1);
    if(err < 0) return -2; // No es posible modificar la salida estandar
    err = dup(*ptr);
    if(err < 0) return -3; // No es posible modificar la salida estandar
  }

  return 0;
}