Download c source code

/*
 * 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);
}