/*
* Copyright 2021 Jeisson Hidalgo-Cespedes - Universidad de Costa Rica
*/
#include <assert.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "static_simulation.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
typedef struct private_thread_data {
size_t thread_number; // rank
static_simulation_t* static_simulation;
} private_thread_data_t;
int create_threads(static_simulation_t* static_simulation);
void print_statistics(static_simulation_t* static_simulation);
void* run(void* data);
void* run_block_map(private_thread_data_t* private_data);
void* run_cyclic_map(private_thread_data_t* private_data);
int static_simulation_init(static_simulation_t* static_simulation
, static_mapping_t mapping_type, simulation_t* simulation) {
assert(static_simulation);
assert(simulation);
static_simulation->mapping_type = mapping_type;
static_simulation->simulation = simulation;
static_simulation->durations = calloc(simulation->thread_count
, sizeof(data_t));
return 0;
}
void static_simulation_destroy(static_simulation_t* static_simulation) {
assert(static_simulation);
free(static_simulation->durations);
}
int static_simulation_run(static_simulation_t* static_simulation) {
assert(static_simulation);
create_threads(static_simulation);
print_statistics(static_simulation);
return 0;
}
int create_threads(static_simulation_t* static_simulation) {
int error = 0;
simulation_t* simulation = static_simulation->simulation;
pthread_t* threads = (pthread_t*)
malloc(simulation->thread_count * sizeof(pthread_t));
private_thread_data_t* private_thread_data = (private_thread_data_t*)
calloc(simulation->thread_count, sizeof(private_thread_data_t));
if (threads && private_thread_data) {
clock_gettime(/*clk_id*/CLOCK_MONOTONIC, &static_simulation->start_time);
for (size_t index = 0; index < simulation->thread_count; ++index) {
private_thread_data[index].thread_number = index;
private_thread_data[index].static_simulation = static_simulation;
error = pthread_create(&threads[index], NULL, run
, &private_thread_data[index]);
if (error) {
fprintf(stderr, "error: could not create thread %zu\n", index);
error = 21;
}
}
for (size_t index = 0; index < simulation->thread_count; ++index) {
pthread_join(threads[index], NULL);
}
free(private_thread_data);
free(threads);
} else {
fprintf(stderr, "error: could not allocate memory for %zu threads\n"
, simulation->thread_count);
error = 22;
}
return error;
}
void print_statistics(static_simulation_t* static_simulation) {
assert(static_simulation);
simulation_t* simulation = static_simulation->simulation;
printf("%-7s", static_mapping_text[static_simulation->mapping_type]);
data_t max_duration = 0;
for (size_t index = 0; index < simulation->thread_count; ++index) {
if (static_simulation->durations[index] > max_duration) {
max_duration = static_simulation->durations[index];
}
printf(" %4u", static_simulation->durations[index]);
}
printf(" %8u", max_duration);
assert(max_duration > 0);
const double speedup = (double)static_simulation->simulation->total_duration
/ max_duration;
printf(" %7.2lf", speedup);
const double efficiency = speedup / simulation->thread_count;
printf(" %10.2lf", efficiency);
printf(" %12.9lf", calculate_elapsed_time(&static_simulation->start_time));
putchar('\n');
}
void* run(void* data) {
private_thread_data_t* private_data = (private_thread_data_t*)data;
static_simulation_t* static_simulation = private_data->static_simulation;
switch (static_simulation->mapping_type) {
case MAPPING_BLOCK: return run_block_map(private_data);
case MAPPING_CYCLIC: return run_cyclic_map(private_data);
default: assert(false); break;
}
}
void* run_block_map(private_thread_data_t* private_data) {
static_simulation_t* static_simulation = private_data->static_simulation;
simulation_t* simulation = static_simulation->simulation;
const size_t rank = private_data->thread_number;
const size_t start = calculate_block_start(rank
, simulation->work.count, simulation->thread_count);
const size_t finish = calculate_block_finish(rank
, simulation->work.count, simulation->thread_count);
for (size_t index = start; index < finish; ++index) {
const data_t duration = simulation->work.elements[index];
usleep(duration);
static_simulation->durations[rank] += duration;
}
return NULL;
}
void* run_cyclic_map(private_thread_data_t* private_data) {
static_simulation_t* static_simulation = private_data->static_simulation;
simulation_t* simulation = static_simulation->simulation;
const size_t rank = private_data->thread_number;
for (size_t index = rank; index < simulation->work.count;
index += simulation->thread_count) {
const data_t duration = simulation->work.elements[index];
usleep(duration);
static_simulation->durations[rank] += duration;
}
return NULL;
}
size_t calculate_block_start(size_t rank, size_t work_count
, size_t worker_count) {
return rank * (work_count / worker_count)
+ MIN(rank, work_count % worker_count);
}
size_t calculate_block_finish(size_t rank, size_t work_count
, size_t worker_count) {
return calculate_block_start(rank + 1, work_count, worker_count);
}