Commit 653d505d authored by Thomas Ponweiser's avatar Thomas Ponweiser
Browse files

renamed 'wireworld' to 'cellular_automaton'; added first C code variant

parent 98b3ac44
# Packages are optional: if they are not present, certain code samples are not compiled
cmake_minimum_required(VERSION 2.8.10 FATAL_ERROR)
find_package(MPI) # Built-in in CMake
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake/common.cmake)
# ==================================================================================================
if ("${DWARF_PREFIX}" STREQUAL "")
set(DWARF_PREFIX 5_structured)
endif()
set(NAME ${DWARF_PREFIX}_wireworld_c)
# C compiler settings
find_package(Common)
if (MPI_FOUND)
cmake_policy(SET CMP0003 OLD)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
include_directories(${MPI_INCLUDE_PATH})
add_executable(${NAME} main.c configuration.c mpitypes.c world.c io.c simulation.c)
target_link_libraries(${NAME} ${MPI_LIBRARIES} stdc++)
install(TARGETS ${NAME} DESTINATION bin)
message("** Enabling '${NAME}': with MPI")
else()
message("## Skipping '${NAME}': no MPI support found")
# dummy_install(${NAME} "MPI")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_FLAGS}")
unset(NAME)
# ==================================================================================================
#include <getopt.h>
#include <mpi.h>
#include <stdlib.h>
#include <stdio.h>
#include "configuration.h"
const char *verbosity_levels[] = {"OFF", "INFO", "DEBUG", "TRACE"};
const char *communication_computation_modes[] = {
"Blocking (No overlap of communication and computation)",
"Non-blocking (Overlapping communication and computation)"
};
const char *transmission_modes[] = {
"Sparse collective - MPI_Dist_graph_create_adjacent / MPI_[N/In]eighbor_alltoallw",
"Point-to-point - MPI_ISend / MPI_IRecv / MPI_Waitall",
"Persistent request - MPI_Send_init / MPI_Recv_init / MPI_Startall / MPI_Waitall"
};
// --------------------------------------------- Helper function declarations
void conf_set_or_validate_nprocs(conf_t *c);
long parse_long(const char *str);
int log_enabled(const conf_t *c, int lvl);
// ==========================================================================
void conf_init_default(conf_t *c)
{
c->verbosity_level = INFO;
c->transmission_mode = SPARSE_COLLECTIVE;
c->communication_computation_mode = OVERLAP;
c->nprocs[0] = 0;
c->nprocs[1] = 0;
// Default values:
c->n_iterations = 1;
c->n_generations_per_iteration = 5000;
c->file_basename[0] = '\0';
}
void conf_init_from_args(conf_t *c, int argc, char* argv[])
{
struct option long_options[] = {
{"verbose", required_argument, NULL, 'v'},
// Transmission modes
{"sparse", no_argument, &c->transmission_mode, SPARSE_COLLECTIVE},
{"p2p", no_argument, &c->transmission_mode, POINT_TO_POINT},
{"persist", no_argument, &c->transmission_mode, PERSISTENT_REQUEST},
// Communication computation overlap
{"no-overlap", no_argument, &c->communication_computation_mode, NO_OVERLAP},
{"overlap", no_argument, &c->communication_computation_mode, OVERLAP},
{"nprocs-x", required_argument, NULL, 'x'},
{"nprocs-y", required_argument, NULL, 'y'},
{"iterations", required_argument, NULL, 'i'},
{"generations-per-iteration", required_argument, NULL, 'g'},
{NULL, 0, NULL, 0}
};
conf_init_default(c);
int option;
while((option = getopt_long(argc, argv, "v:x:y:i:g:", long_options, NULL)) >= 0) {
switch(option) {
case 'v': c->verbosity_level = atoi(optarg); break;
case 'x': c->nprocs[0] = atoi(optarg); break;
case 'y': c->nprocs[1] = atoi(optarg); break;
case 'i': c->n_iterations = atoi(optarg); break;
case 'g': c->n_generations_per_iteration = parse_long(optarg); break;
}
}
if(optind > argc) {
fprintf(stderr, "Expected base name of input file (omitting '.wi' file extension)\n");
MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
}
if(strlen(argv[optind]) > sizeof(c->file_basename)-1) {
fprintf(stderr, "Input filename too long.\n");
MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
}
strncpy(c->file_basename, argv[optind], sizeof(c->file_basename));
conf_set_or_validate_nprocs(c);
}
void conf_set_or_validate_nprocs(conf_t *c)
{
int nprocs, rc;
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
rc = MPI_Dims_create(nprocs, 2, c->nprocs);
if(rc != MPI_SUCCESS) {
fprintf(stderr, "Cannot create 2D Cartesian grid with specified number of processes in x or y direction.\n");
MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
}
}
void conf_print(const conf_t *c, FILE *f)
{
int i = c->verbosity_level;
if(i < 0) i = 0; else if(i > 3) i = 3;
fprintf(f, "Configuration:\n");
fprintf(f, " * Verbosity level: %s (%d)\n", verbosity_levels[i], c->verbosity_level);
fprintf(f, " * Input file: %s%s\n", c->file_basename, FILE_EXT);
fprintf(f, " * Transmission mode: %s\n", transmission_modes[c->transmission_mode]);
fprintf(f, " * Communication mode: %s\n", communication_computation_modes[c->communication_computation_mode]);
fprintf(f, " * Grid of processes: %d x %d\n", c->nprocs[0], c->nprocs[1]);
fprintf(f, " * Number of iterations: %d\n", c->n_iterations);
fprintf(f, " * Generations per iteration: %ld\n", c->n_generations_per_iteration);
fprintf(f, "\n");
}
int log_enabled(const conf_t *c, int lvl)
{
return c->verbosity_level >= lvl;
}
int info_enabled(const conf_t *c)
{
static int rank = -1;
if(rank < 0) {
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
}
return log_enabled(c, INFO) && rank == 0;
}
int debug_enabled(const conf_t *c)
{
return log_enabled(c, DEBUG);
}
int trace_enabled(const conf_t *c)
{
return log_enabled(c, TRACE);
}
// --------------------------------------------------------- Helper functions
long parse_long(const char *str)
{
char* p;
long result = strtol(str, &p, 10);
if(*p == 'k') {
result <<= 10;
} else if(*p == 'M') {
result <<= 20;
}
return result;
}
#ifndef CONFIGURATION_H
#define CONFIGURATION_H
#include <stdio.h>
enum verbosity_level_enum
{
OFF = 0,
INFO = 1,
DEBUG = 2,
TRACE = 3
};
enum transmission_mode_enum
{
SPARSE_COLLECTIVE = 0,
POINT_TO_POINT = 1,
PERSISTENT_REQUEST = 2
};
enum communication_computation_mode_enum
{
NO_OVERLAP = 0,
OVERLAP = 1
};
#define FILE_BASENAME_SZ 1024
#define FILE_EXT ".wi"
#define FILE_EXT_LEN (sizeof(FILE_EXT)-1)
#define FILE_NAME_SZ (FILE_BASENAME_SZ + FILE_EXT_LEN)
typedef struct
{
int verbosity_level;
int transmission_mode;
int communication_computation_mode;
int nprocs[2];
int n_iterations;
long n_generations_per_iteration;
char file_basename[FILE_BASENAME_SZ];
} conf_t;
// Meta-information for MPI-Datatype creation (see mpitypes.c)
#define CONF_T_N_INT_MEMBERS 6
#define CONF_T_FIRST_INT_MEMBER verbosity_level
#define CONF_T_N_LONG_MEMBERS 2
#define CONF_T_FIRST_LONG_MEMBER n_generations_per_iteration
#define CONF_T_N_CHAR_MEMBERS FILE_BASENAME_SZ
#define CONF_T_FIRST_CHAR_MEMBER file_basename[0]
void conf_init_default(conf_t *c);
void conf_init_from_args(conf_t *c, int argc, char* argv[]);
void conf_print(const conf_t *c, FILE *file);
int info_enabled(const conf_t *c);
int warn_enabled(const conf_t *c);
int debug_enabled(const conf_t *c);
int trace_enabled(const conf_t *c);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "io.h"
#define MAX_HEADER_LENGTH 128
#define MAX_CELLS_X (1<<15)
#define MAX_CELLS_Y (1<<15)
void file_set_view(MPI_File file, const world_t *world, size_t header_length);
void file_read_header(MPI_File file, size_t *global_size, size_t *header_length)
{
char header[MAX_HEADER_LENGTH+1] = {0};
char *begin, *end;
long x, y;
// Read header (and most likely something more)
MPI_File_read_all(file, header, MAX_HEADER_LENGTH, MPI_CHAR, MPI_STATUS_IGNORE);
// Parse global number of cells in x and y direction
begin = header;
x = strtol(begin, &end, 10);
begin = end;
y = strtol(begin, &end, 10);
// Check x and y
if(x < 0 || x > MAX_CELLS_X || y < 0 || y > MAX_CELLS_Y) {
fprintf(stderr,
"Global number of cells out of range (x/y): %ld / %ld\n",
x, y
);
MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
}
// Check that header has been read to end
if(*end != '\n') {
*end = '\0';
fprintf(stderr,
"File header should end with newline character.\n"
"Parsed portion of header: \"%s\"\n",
header
);
MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
}
global_size[0] = x+1; // (+1 for trailing '\n' on each input file line)
global_size[1] = y;
*header_length = end - header + 1;
}
void file_write_header(MPI_File file, const world_t *world, size_t *header_length)
{
int rank, len;
char header[MAX_HEADER_LENGTH+1];
len = snprintf(
header, MAX_HEADER_LENGTH+1, "%ld %ld\n",
world->global_size[0]-1, // (-1 ... do not count trailing '\n')
world->global_size[1]
);
*header_length = len;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if(rank == 0) {
MPI_File_write(file, header, len, MPI_CHAR, MPI_STATUS_IGNORE);
}
}
void file_set_view(MPI_File file, const world_t *world, size_t header_length)
{
MPI_Datatype world_type;
int sizes[2], subsizes[2], starts[2];
int dim;
for(dim = 0; dim < 2; dim++) {
sizes[dim] = world->global_size[dim];
subsizes[dim] = world->local_size[dim];
starts[dim] = world->local_start[dim];
}
MPI_Type_create_subarray(
2, sizes, subsizes, starts, MPI_ORDER_FORTRAN, MPI_CHAR, &world_type
);
MPI_Type_commit(&world_type);
MPI_File_set_view(file, header_length, MPI_CHAR, world_type, "native", MPI_INFO_NULL);
MPI_Type_free(&world_type);
}
void file_read_world(MPI_File file, world_t *world, size_t header_length)
{
const size_t storage_size = world_get_storage_size(world);
file_set_view(file, world, header_length);
MPI_File_read_all(file, world->cells_next, 1, world->transfer.io_type, MPI_STATUS_IGNORE);
memcpy(world->cells_prev, world->cells_next, storage_size);
}
void file_write_world(MPI_File file, const world_t *world, size_t header_length)
{
file_set_view(file, world, header_length);
MPI_File_write_all(file, world->cells_next, 1, world->transfer.io_type, MPI_STATUS_IGNORE);
}
#ifndef _IO_H_
#define _IO_H_
#include <stddef.h>
#include <mpi.h>
#include "world.h"
void file_read_header(MPI_File file, size_t *global_size, size_t *header_length);
void file_write_header(MPI_File file, const world_t *world, size_t *header_length);
void file_read_world(MPI_File file, world_t *world, size_t header_length);
void file_write_world(MPI_File file, const world_t *world, size_t header_length);
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>
#include "configuration.h"
#include "mpitypes.h"
#include "io.h"
#include "world.h"
#include "simulation.h"
void broadcast_configuration(conf_t *c);
void read_input(const conf_t *c, world_t *world);
void iterate(const conf_t *c, world_t *world);
int main(int argc, char* argv[])
{
conf_t config;
world_t world;
MPI_Init(&argc, &argv);
// Parse command line arguments
conf_init_from_args(&config, argc, argv);
if(info_enabled(&config)) conf_print(&config, stdout);
// Read input file
read_input(&config, &world);
// Run simulation
iterate(&config, &world);
if(info_enabled(&config)) printf("Done.\n");
MPI_Finalize();
return EXIT_SUCCESS;
}
void read_input(const conf_t *c, world_t *world) {
int rank;
const int periods[2] = {0}; // non-periodic boundaries
const int allow_reorder = 1;
MPI_Comm cart_comm;
char input_filename[FILE_NAME_SZ];
MPI_File file;
size_t global_sizes[2];
size_t header_length;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
// Determine input file name
snprintf(input_filename, sizeof(input_filename), "%s%s", c->file_basename, FILE_EXT);
if(info_enabled(c)) printf("Reading '%s'...\n\n", input_filename);
MPI_File_open(MPI_COMM_WORLD, input_filename, MPI_MODE_RDONLY, MPI_INFO_NULL, &file);
// Read header
file_read_header(file, global_sizes, &header_length);
if(info_enabled(c)) printf(
"Read header (%ld characters).\n"
"Global size: %ld x %ld\n\n",
header_length, global_sizes[0], global_sizes[1]
);
if(debug_enabled(c)) printf(
"%03d: Local tile: [%ld %ld) x [%ld %ld)\n", rank,
world->local_start[0], world->local_start[0]+world->local_size[0],
world->local_start[1], world->local_start[1]+world->local_size[1]
);
// Initialize cells (determine local tile, allocate memory)
MPI_Cart_create(MPI_COMM_WORLD, 2, c->nprocs, periods, allow_reorder, &cart_comm);
world_init(world, cart_comm, global_sizes);
// Collectively read cell data
file_read_world(file, world, header_length);
MPI_File_close(&file);
}
void iterate(const conf_t *c, world_t *world)
{
const size_t n_it = c->n_iterations;
const size_t n_gen = c->n_generations_per_iteration;
size_t i, g;
double total_time, sim_time = 0, io_time = 0;
char output_filename[FILE_NAME_SZ+8];
MPI_File file;
size_t header_length;
if(info_enabled(c)) printf(
"Running %d iterations with %ld generations per iteration.\n\n",
n_it, n_gen
);
total_time = -MPI_Wtime();
g = 0;
for(i = 1; i <= n_it; i++) {
// Run n_gen generations
sim_time -= MPI_Wtime();
do_simulation(world, n_gen); g += n_gen;
sim_time += MPI_Wtime();
// Determine output filename
snprintf(
output_filename, sizeof(output_filename),
"%s+%07ld%s",
c->file_basename, g, FILE_EXT
);
// Write output file
io_time -= MPI_Wtime();
MPI_File_open(
MPI_COMM_WORLD, output_filename,
MPI_MODE_CREATE | MPI_MODE_WRONLY,
MPI_INFO_NULL, &file
);
file_write_header(file, world, &header_length);
file_write_world(file, world, header_length);
MPI_File_close(&file);
io_time += MPI_Wtime();
if(info_enabled(c)) printf("Done iteration %-4d - Generation %-7ld - written '%s'.\n", i, g, output_filename);
}
total_time += MPI_Wtime();
if(info_enabled(c)) printf(
"\nStatistics:\n"
" * Generations per second: %.0f\n"
" * Net simulation time (s): %f\n"
" * Net I/O time (s): %f\n"
" * Total time (s): %f\n\n",
g / sim_time, sim_time, io_time, total_time
);
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment