Initial commit

This commit is contained in:
Simponic 2021-12-04 13:34:49 -07:00
commit aa1d7c6e28
33 changed files with 1190 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.bin
*.mp4
*.swp
*.o

82
Readme.org Normal file
View File

@ -0,0 +1,82 @@
#+AUTHOR: Logan Hunt
* CS 5030 Final Project
This is GOL. That's it. (Not done yet)
[[https://youtu.be/N_aUWYNqpeY][A Video Example]]
There are multiple implementations in this project. Each one uses the same code, just modified slightly. Each directory contains a Makefile which will build that implementation. For most, a simple ~cd~ into each directory and ~make~ will do (see build instructions).
Every ~make~ will end you up with a ~gol~ binary. However, each implementation takes a different number of arguments (the Cuda one needs to be run in a slightly different fashion).
** Compiling binary output to a video
Every implementation produces file I/O exactly the same. When logging is turned on, each iteration in the output directory is labelled ~iteration-XXXXXXX.bin~ where iteration number is padded by 7 zeros.
There is a script in ~graphics~ that converts a raw ~unsigned char~ data binary into a .bmp where a zero is black and (with some help from [[https://stackoverflow.com/a/47785639/15819675][this Stack Overflow post]]). This program is utilized by ~make-movie.sh~ to convert every .bin in a directory to a .bmp. Then, these .bmps can be compiled into a video file with the arguments that are described in ~make-movie.sh~ (just provide none and a usage string will be ~echo~ed).
For example to make a movie of the outputs generated in ~cuda-global/output~ where each binary file is a grid of size 1920x1080 (at 8fps to a file named output-1920.mp4):
~cd graphics~
~make~
(On CHPC you will need to ~module load ffmpeg~)
~./make-movie.sh ../cuda-global/output 1920 1080 8 output-1920~
** Building
*** MPI
Not done yet
*** Cuda
Firstly, ~cd~ into ~cuda-global~ and ~make~.
Then start an interactive gpu session on notchpeak:
~salloc -n 1 -N 1 -t 0:10:00 -p notchpeak-shared-short -A notchpeak-shared-short --gres=gpu:k80:1~
This implementation takes these arguments:
~srun ./gol simulate <filename | random> <width> <height> <iterations> <log-each-step?1:0> <block-size>~
For example to do 1000 iterations at 1920x1080 with a random starting position (the last ~1~ will log each iteration into the ~output~ directory) with a block size of 32:
~srun ./gol simulate random 1920 1080 1000 1 32~
*** OpenMP
Firstly, ~cd~ into ~openmp~ and ~make~.
This implementation takes these arguments:
~./gol simulate <filename | random> <width> <height> <iterations> <log-each-step?1:0> <num_threads>~
For example to do 100 iterations with 8 threads at 800x600 with a random starting position (and log each iteration into the ~output~ directory):
~./gol simulate random 800 600 100 1 8~
*** Serial
The most basic of the three implementations.
Firstly, ~cd~ into ~serial~ and ~make~.
This implementation takes these arguments:
~./gol simulate <filename | random> <width> <height> <iterations> <log-each-step?1:0>~
For example to do 10 iterations with 8 threads at 400x400 with a random starting position (and log to ~output~):
~./gol simulate random 400 400 10 1~
** Creating an initial starting grid
Each ~gol~ binary also has a ~create-grid~ mode, mainly used for debugging:
~./gol create-grid <width> <height> <filename>~
You'll be prompted to enter in grid values (0/1) for each row, each seperated by a space.
For example to make a 10x10 grid and output it to ~output/testing.bin~:
~./gol create-grid 10 10 output/testing.bin~
And then this file can be used in the ~filename~ argument when using ~simulate~.

BIN
cuda-global/.DS_Store vendored Normal file

Binary file not shown.

20
cuda-global/Makefile Normal file
View File

@ -0,0 +1,20 @@
.DEFAULT_GOAL := all
INCLUDES = -I include/
game.o: include/game.cuh src/game.cu
nvcc -c src/game.cu $(INCLUDES) -o build/game.o
file.o: game.o include/file.cuh src/file.cu
nvcc -c src/file.cu $(INCLUDES) -o build/file.o
create_grid.o: file.o game.o include/create_grid.cuh src/create_grid.cu
nvcc -c src/create_grid.cu $(INCLUDES) -o build/create_grid.o
gol: game.o file.o create_grid.o
nvcc $(INCLUDES) -o gol build/game.o build/file.o build/create_grid.o src/main.cu
clean:
$(RM) build/* gol output/*
all: gol

BIN
cuda-global/gol Executable file

Binary file not shown.

View File

@ -0,0 +1,12 @@
#include <stdio.h>
#include <stdlib.h>
#include "file.cuh"
#include "game.cuh"
#ifndef CREATE_GRID_H
#define CREATE_GRID_H
void print_grid(struct GAME* game);
void create_grid(int argc, char** argv);
#endif // CREATE_GRID_H

View File

@ -0,0 +1,11 @@
#include "game.cuh"
#include <stdlib.h>
#include <stdio.h>
#ifndef FILE_H
#define FILE_H
void read_in(char* filename, struct GAME* game);
void write_out(char* filename, struct GAME* game);
#endif //FILE_H

View File

@ -0,0 +1,21 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <omp.h>
#ifndef GAME_H
#define GAME_H
struct GAME {
unsigned char* grid;
int padding;
int width;
int height;
};
__device__ int neighbors(struct GAME game, int x, int y);
__global__ void next(struct GAME game, unsigned char* newGrid);
void randomize(struct GAME* game);
#endif // GAME_H

View File

@ -0,0 +1,38 @@
#include "create_grid.cuh"
void print_grid(struct GAME* game) {
printf("\n===GRID===\n");
for (int y = 0; y < game->height; y++) {
for (int x = 0; x < game->width; x++) {
printf("%i ", game->grid[y*(game->width+game->padding*2) + x]);
}
printf("\n");
}
}
void create_grid(int argc, char** argv) {
char* filename;
struct GAME game;
game.padding = 0;
if (argc == 5) {
game.width = atoi(argv[2]);
game.height = atoi(argv[3]);
filename = argv[4];
} else {
printf("Usage: ./gol create-grid <width> <height> <filename>\n");
exit(1);
}
int size = (game.width+game.padding*2) * (game.height+game.padding*2);
unsigned char* grid = (unsigned char*)malloc(sizeof(unsigned char) * size);
for (int y = 0; y < game.height; y++) {
printf("Row %i: ", y);
for (int x = 0; x < game.width; x++) {
char temp;
scanf("%i%c", (unsigned int*)&game.grid[y*(game.width+game.padding*2) + x],&temp);
}
}
game.grid = grid;
write_out(filename, &game);
print_grid(&game);
}

17
cuda-global/src/file.cu Normal file
View File

@ -0,0 +1,17 @@
#include "file.cuh"
void read_in(char* filename, struct GAME* game) {
FILE* file = fopen(filename, "rb");
for (int i = game->padding; i < game->height+game->padding; i++) {
fread(&game->grid[i*(game->width + 2*game->padding) + game->padding], sizeof(unsigned char), game->width, file);
}
fclose(file);
}
void write_out(char* filename, struct GAME* game) {
FILE* file = fopen(filename, "w+");
for (int i = game->padding; i < game->height+game->padding; i++) {
fwrite(&game->grid[i*(game->width + 2*game->padding) + game->padding], sizeof(unsigned char), game->width, file);
}
fclose(file);
}

46
cuda-global/src/game.cu Normal file
View File

@ -0,0 +1,46 @@
#include "game.cuh"
__device__ int neighbors(struct GAME game, int x, int y) {
int n = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if (!(dx == 0 && dy == 0) && (x+dx) > 0 && (y+dy) > 0 && (x+dx) < game.width+(game.padding*2) && (y+dy) < game.height+(game.padding*2)) {
if (game.grid[(y+dy) * (game.width+game.padding*2) + (x+dx)]) {
n++;
}
}
}
}
return n;
}
__global__ void next(struct GAME game, unsigned char* newGrid) {
int idy = blockDim.y * blockIdx.y + threadIdx.y;
int idx = blockDim.x * blockIdx.x + threadIdx.x;
if (idy <= game.height+game.padding*2 && idx <= game.width+game.padding*2) {
int my_neighbors = neighbors(game, idx, idy);
int my_coord = idy * (game.width+game.padding*2) + idx;
newGrid[my_coord] = 0; // It's possible that there are artifacts from the last iteration
if (game.grid[my_coord]) {
if (my_neighbors < 2 || my_neighbors > 3) {
newGrid[my_coord] = 0;
} else {
newGrid[my_coord] = 1;
}
} else {
if (my_neighbors == 3) {
newGrid[my_coord] = 1;
}
}
}
}
void randomize(struct GAME* game) {
for (int y = game->padding; y < game->height+game->padding; y++) {
for (int x = game->padding; x < game->width+game->padding; x++) {
game->grid[y*(game->width+game->padding*2) + x] = (unsigned char) rand() & 1;
}
}
}

150
cuda-global/src/main.cu Normal file
View File

@ -0,0 +1,150 @@
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <cstring>
#include "file.cuh"
#include "game.cuh"
#include "create_grid.cuh"
/*
Rules for life:
Any live cell with fewer than two live neighbors dies (underpopulation).
Any live cell with two or three live neighbors continues to live.
Any live cell with more than three live neighbors dies (overpopulation).
Any dead cell with exactly three live neighbors becomes a live cell (reproduction).
*/
#define BLOCK 32
#define PADDING 10
//#define VERBOSE 1
#define SEED 100
// gpuErrchk source: https://stackoverflow.com/questions/14038589/what-is-the-canonical-way-to-check-for-errors-using-the-cuda-runtime-api
#define gpuErrchk(ans) { gpuAssert((ans), __FILE__, __LINE__); }
inline void gpuAssert(cudaError_t code, const char *file, int line, bool abort =
true) {
if (code != cudaSuccess) {
fprintf(stderr, "GPUassert: %s %s %d\n", cudaGetErrorString(code), file,
line);
if (abort)
exit(code);
}
}
void simulate(int argc, char** argv) {
srand(SEED);
clock_t totalStart = clock();
char* filename;
struct GAME game;
game.padding = PADDING;
int iterations, log_each_step, block_size;
if (argc == 8) {
filename = argv[2];
game.width = atoi(argv[3]);
game.height = atoi(argv[4]);
iterations = atoi(argv[5]);
log_each_step = atoi(argv[6]);
block_size = atoi(argv[7]);
} else {
printf("Usage: ./gol simulate <filename | random> <width> <height> <iterations> <log-each-step?1:0> <block-size>\n");
filename = "random";
game.height = 10;
game.width = 10;
iterations = 5;
log_each_step = 0;
}
// Allocate space for current grid (1 byte per tile)
int size = (game.height+(2*game.padding)) * (game.width+(2*game.padding)) * sizeof(unsigned char);
game.grid = (unsigned char*)malloc(size);
memset(game.grid, 0, size);
if (strcmp(filename, "random") == 0) {
randomize(&game);
} else {
read_in(filename, &game);
}
char iteration_file[1024];
unsigned char* grid_d;
unsigned char* newGrid_d;
gpuErrchk(cudaMalloc(&grid_d, size));
gpuErrchk(cudaMemcpy(grid_d, game.grid, size, cudaMemcpyHostToDevice));
gpuErrchk(cudaMalloc(&newGrid_d, size));
unsigned char* grid_h = (unsigned char*)malloc(size);
unsigned char* temp;
game.grid = grid_d;
int grid_num = (int)ceil((game.width+(2*game.padding))/(float)block_size);
dim3 dim_grid(grid_num, grid_num, 1);
dim3 dim_block(block_size, block_size, 1);
cudaEvent_t startLife, stopLife;
cudaEventCreate(&startLife);
cudaEventCreate(&stopLife);
double timeComputingLife = 0;
float localTime = 0;
for (int i = 0; i <= iterations; i++) {
if (i > 0) {
cudaEventRecord(startLife);
next<<<dim_grid, dim_block>>>(game, newGrid_d);
cudaEventRecord(stopLife);
cudaEventSynchronize(stopLife);
cudaEventElapsedTime(&localTime, startLife, stopLife);
timeComputingLife += localTime/1000;
temp = game.grid;
game.grid = newGrid_d;
newGrid_d = temp;
}
if (log_each_step) {
gpuErrchk(cudaMemcpy(grid_h, game.grid, size, cudaMemcpyDeviceToHost));
#ifdef VERBOSE
printf("\n===Iteration %i===\n", i);
for (int y = game.padding; y < game.height+game.padding; y++) {
for (int x = game.padding; x < game.width+game.padding; x++) {
printf("%s ", grid_h[y*(game.width+2*game.padding) + x] ? "X" : " ");
}
printf("\n");
}
printf("===End iteration %i===\n", i);
#endif
sprintf(iteration_file, "output/iteration-%07d.bin", i);
temp = game.grid;
game.grid = grid_h;
write_out(iteration_file, &game);
game.grid = temp;
}
}
clock_t totalEnd = clock();
printf("\n===Timing===\nTime computing life: %f\nClock time: %f\n", timeComputingLife, ((double)totalEnd - (double)totalStart)/CLOCKS_PER_SEC);
cudaFree(&newGrid_d);
cudaFree(&grid_d);
cudaFree(&game.grid);
free(grid_h);
}
int main(int argc, char** argv) {
if (argc >= 2) {
if (strcmp(argv[1], "simulate") == 0) {
simulate(argc, argv);
} else if (strcmp(argv[1], "create-grid") == 0) {
create_grid(argc, argv);
} else {
printf("Unknown input: %s\n", argv[1]);
exit(1);
}
} else {
printf("Usage: ./gol <simulate | create-grid>\n");
exit(1);
}
return 0;
}

4
graphics/Makefile Normal file
View File

@ -0,0 +1,4 @@
.DEFAULT_GOAL := convert_bin_to_img
convert_bin_to_img: convert_bin_to_img.c
gcc convert_bin_to_img.c -o convert_bin_to_img

BIN
graphics/convert_bin_to_img Executable file

Binary file not shown.

View File

@ -0,0 +1,137 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Most BMP Image generation code is from https://stackoverflow.com/a/47785639/15819675
const int BYTES_PER_PIXEL = 3; /// red, green, & blue
const int FILE_HEADER_SIZE = 14;
const int INFO_HEADER_SIZE = 40;
void generateBitmapImage(unsigned char* image, int height, int width, char* imageFileName);
unsigned char* createBitmapFileHeader(int height, int stride);
unsigned char* createBitmapInfoHeader(int height, int width);
void generateBitmapImage (unsigned char* image, int height, int width, char* imageFileName)
{
int widthInBytes = width * BYTES_PER_PIXEL;
unsigned char padding[3] = {0, 0, 0};
int paddingSize = (4 - (widthInBytes) % 4) % 4;
int stride = (widthInBytes) + paddingSize;
FILE* imageFile = fopen(imageFileName, "wb");
unsigned char* fileHeader = createBitmapFileHeader(height, stride);
fwrite(fileHeader, 1, FILE_HEADER_SIZE, imageFile);
unsigned char* infoHeader = createBitmapInfoHeader(height, width);
fwrite(infoHeader, 1, INFO_HEADER_SIZE, imageFile);
int i;
for (i = 1; i <= height; i++) {
fwrite(image + ((height - i)*widthInBytes), BYTES_PER_PIXEL, width, imageFile);
fwrite(padding, 1, paddingSize, imageFile);
}
fclose(imageFile);
}
unsigned char* createBitmapFileHeader (int height, int stride)
{
int fileSize = FILE_HEADER_SIZE + INFO_HEADER_SIZE + (stride * height);
static unsigned char fileHeader[] = {
0,0, /// signature
0,0,0,0, /// image file size in bytes
0,0,0,0, /// reserved
0,0,0,0, /// start of pixel array
};
fileHeader[ 0] = (unsigned char)('B');
fileHeader[ 1] = (unsigned char)('M');
fileHeader[ 2] = (unsigned char)(fileSize );
fileHeader[ 3] = (unsigned char)(fileSize >> 8);
fileHeader[ 4] = (unsigned char)(fileSize >> 16);
fileHeader[ 5] = (unsigned char)(fileSize >> 24);
fileHeader[10] = (unsigned char)(FILE_HEADER_SIZE + INFO_HEADER_SIZE);
return fileHeader;
}
unsigned char* createBitmapInfoHeader (int height, int width)
{
static unsigned char infoHeader[] = {
0,0,0,0, /// header size
0,0,0,0, /// image width
0,0,0,0, /// image height
0,0, /// number of color planes
0,0, /// bits per pixel
0,0,0,0, /// compression
0,0,0,0, /// image size
0,0,0,0, /// horizontal resolution
0,0,0,0, /// vertical resolution
0,0,0,0, /// colors in color table
0,0,0,0, /// important color count
};
infoHeader[ 0] = (unsigned char)(INFO_HEADER_SIZE);
infoHeader[ 4] = (unsigned char)(width );
infoHeader[ 5] = (unsigned char)(width >> 8);
infoHeader[ 6] = (unsigned char)(width >> 16);
infoHeader[ 7] = (unsigned char)(width >> 24);
infoHeader[ 8] = (unsigned char)(height );
infoHeader[ 9] = (unsigned char)(height >> 8);
infoHeader[10] = (unsigned char)(height >> 16);
infoHeader[11] = (unsigned char)(height >> 24);
infoHeader[12] = (unsigned char)(1);
infoHeader[14] = (unsigned char)(BYTES_PER_PIXEL*8);
return infoHeader;
}
void convertBinToRaw(char* file, int width, int height) {
unsigned char white[3] = {255,255,255};
unsigned char black[3] = {0,0,0};
unsigned char* grid = malloc(sizeof(unsigned char) * width * height);
FILE* fp = fopen(file, "rb");
fread(grid, sizeof(unsigned char), width*height, fp);
fclose(fp);
int i;
char* raw_file_name;
sprintf(raw_file_name, "%s.bmp", file);
unsigned char* full_image = malloc(sizeof(unsigned char) * width * height * 3);
for (i = 0; i < width*height; i++) {
if (grid[i]) {
memcpy(&full_image[i*3], &white, sizeof(unsigned char) * 3);
} else {
memcpy(&full_image[i*3], &black, sizeof(unsigned char) * 3);
}
}
generateBitmapImage(full_image, height, width, raw_file_name);
free(grid);
free(full_image);
}
int main(int argc, char** argv) {
char* file_name;
int width, height;
if (argc == 4) {
file_name = argv[1];
width = atoi(argv[2]);
height = atoi(argv[3]);
} else {
printf("Usage: ./convert_bin_to_img <file> <width> <height>\n");
exit(1);
}
convertBinToRaw(file_name, width, height);
return 0;
}

11
graphics/make_movie.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/bash
if [ $# == 5 ]; then
for x in $(find $1 -type f -name "*.bin"); do
./convert_bin_to_img $x $2 $3
done
ffmpeg -r $4 -f image2 -s "${2}x${3}" -i "${1}/iteration-%07d.bin.bmp" -vcodec libx264 -crf 25 -pix_fmt yuv420p "${5}.mp4"
else
echo "Usage: ./make_movie.sh <directory> <width> <height> <fps> <output_video_name>"
fi

57
openmp/Makefile Normal file
View File

@ -0,0 +1,57 @@
# This is some Makefile I've been using for projects for a while and can't find
# where I originally got it
CC ?= gcc
SRC_PATH = src
BUILD_PATH = build
BIN_PATH = $(BUILD_PATH)/bin
BIN_NAME = gol
SRC_EXT = c
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' | sort -k 1nr | cut -f2-)
OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o)
DEPS = $(OBJECTS:.o=.d)
COMPILE_FLAGS = -Wall -g -fopenmp -lm -std=c99
INCLUDES = -I include/
LIBS = -fopenmp -lm
.PHONY: default_target
default_target: release
.PHONY: release
release: dirs
@$(MAKE) all
.PHONY: dirs
dirs:
@echo "Creating directories"
@mkdir -p $(dir $(OBJECTS))
@mkdir -p $(BIN_PATH)
.PHONY: clean
clean:
@echo "Deleting $(BIN_NAME) symlink"
@$(RM) $(BIN_NAME)
@echo "Deleting directories"
@$(RM) -r $(BUILD_PATH)
@$(RM) -r $(BIN_PATH)
.PHONY: all
all: $(BIN_PATH)/$(BIN_NAME)
@echo "Making symlink: $(BIN_NAME) -> $<"
@$(RM) $(BIN_NAME)
@ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
$(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
@echo "Linking: $@"
$(CC) $(OBJECTS) -o $@ ${LIBS}
-include $(DEPS)
$(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT)
@echo "Compiling: $< -> $@"
$(CC) $(COMPILE_FLAGS) $(INCLUDES) -MP -MMD -c $< -o $@

View File

@ -0,0 +1,12 @@
#include <stdio.h>
#include <stdlib.h>
#include "file.h"
#include "game.h"
#ifndef CREATE_GRID_H
#define CREATE_GRID_H
void print_grid(struct GAME* game);
void create_grid(int argc, char** argv);
#endif // CREATE_GRID_H

11
openmp/include/file.h Normal file
View File

@ -0,0 +1,11 @@
#include "game.h"
#include <stdlib.h>
#include <stdio.h>
#ifndef FILE_H
#define FILE_H
void read_in(char* filename, struct GAME* game);
void write_out(char* filename, struct GAME* game);
#endif //FILE_H

23
openmp/include/game.h Normal file
View File

@ -0,0 +1,23 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <omp.h>
#include <math.h>
#ifndef GAME_H
#define GAME_H
struct GAME {
unsigned char** grid;
int padding;
int width;
int height;
};
int neighbors(struct GAME* game, int x, int y);
void next(struct GAME* game, int num_threads);
void update(struct GAME* game, int x1, int x2, int y1, int y2);
void randomize(struct GAME* game);
#endif // GAME_H

38
openmp/src/create_grid.c Normal file
View File

@ -0,0 +1,38 @@
#include "create_grid.h"
void print_grid(struct GAME* game) {
printf("\n===GRID===\n");
for (int y = 0; y < game->height; y++) {
for (int x = 0; x < game->width; x++) {
printf("%i ", game->grid[y][x]);
}
printf("\n");
}
}
void create_grid(int argc, char** argv) {
char* filename;
struct GAME game;
game.padding = 0;
if (argc == 5) {
game.width = atoi(argv[2]);
game.height = atoi(argv[3]);
filename = argv[4];
} else {
printf("Usage: ./gol create-grid <width> <height> <filename>\n");
exit(1);
}
unsigned char** grid = malloc(sizeof(unsigned char*) * game.height);
for (int y = 0; y < game.height; y++) {
grid[y] = malloc(sizeof(unsigned char*) * game.width);
printf("Row %i: ", y);
for (int x = 0; x < game.width; x++) {
char temp;
scanf("%i%c", (unsigned int*)&grid[y][x],&temp);
}
}
game.grid = grid;
write_out(filename, &game);
print_grid(&game);
}

17
openmp/src/file.c Normal file
View File

@ -0,0 +1,17 @@
#include "file.h"
void read_in(char* filename, struct GAME* game) {
FILE* file = fopen(filename, "rb");
for (int i = game->padding; i < game->height+game->padding; i++) {
fread(game->grid[i] + game->padding, sizeof(unsigned char), game->width, file);
}
fclose(file);
}
void write_out(char* filename, struct GAME* game) {
FILE* file = fopen(filename, "w+");
for (int i = game->padding; i < game->height+game->padding; i++) {
fwrite(game->grid[i] + game->padding, sizeof(unsigned char), game->width, file);
}
fclose(file);
}

64
openmp/src/game.c Normal file
View File

@ -0,0 +1,64 @@
#include "game.h"
int neighbors(struct GAME* game, int x, int y) {
int n = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if (!(dx == 0 && dy == 0) && (x+dx) > 0 && (y+dy) > 0 && (x+dx) < game->width+(game->padding*2) && (y+dy) < game->height+(game->padding*2)) {
if (game->grid[y+dy][x+dx]) {
n++;
}
}
}
}
return n;
}
void next(struct GAME* game, int threads) {
unsigned char** newGrid = malloc(sizeof(unsigned char*) * (game->height+(game->padding*2)));
int y,x,i,size;
size = sizeof(unsigned char) * (game->width+(game->padding*2));
for (y = 0; y < game->height+(game->padding*2); y++) {
newGrid[y] = malloc(size);
memset(newGrid[y], 0, size);
}
int total_width = game->width+(game->padding*2);
int total_height = game->height+(game->padding*2);
int per_thread = (total_width * total_height) / threads;
#pragma omp parallel num_threads(threads) shared(per_thread, threads, total_width, total_height, newGrid, game) private(y,x,i)
{
int me = omp_get_thread_num();
int thread_start = per_thread * me;
int thread_end = thread_start + per_thread + (me == threads-1 ? (total_width*total_height) % per_thread : 0);
for (i = thread_start; i < thread_end; i++) {
y = i / total_width;
x = i % total_width;
int my_neighbors = neighbors(game, x, y);
if (game->grid[y][x]) {
if (my_neighbors < 2 || my_neighbors > 3) {
newGrid[y][x] = 0;
} else {
newGrid[y][x] = 1;
}
} else {
if (my_neighbors == 3) {
newGrid[y][x] = 1;
}
}
}
}
free(game->grid);
game->grid = newGrid;
}
void randomize(struct GAME* game) {
for (int y = game->padding; y < game->height+game->padding; y++) {
for (int x = game->padding; x < game->width+game->padding; x++) {
game->grid[y][x] = rand() & 1;
}
}
}

106
openmp/src/main.c Normal file
View File

@ -0,0 +1,106 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <omp.h>
#include "file.h"
#include "game.h"
#include "create_grid.h"
/*
Rules for life:
Any live cell with fewer than two live neighbors dies (underpopulation).
Any live cell with two or three live neighbors continues to live.
Any live cell with more than three live neighbors dies (overpopulation).
Any dead cell with exactly three live neighbors becomes a live cell (reproduction).
*/
#define PADDING 10
//#define VERBOSE 1
#define SEED 100
void simulate(int argc, char** argv) {
srand(SEED);
char* filename;
struct GAME game;
game.padding = PADDING;
int iterations, log_each_step, threads;
if (argc == 8) {
filename = argv[2];
game.width = atoi(argv[3]);
game.height = atoi(argv[4]);
iterations = atoi(argv[5]);
log_each_step = atoi(argv[6]);
threads = atoi(argv[7]);
} else {
printf("Usage: ./gol simulate <filename | random> <width> <height> <iterations> <log-each-step?1:0> <threads>\n");
filename = "output/out.bin";
game.height = 10;
game.width = 10;
iterations = 5;
log_each_step = 0;
threads = 1;
}
double global_start = omp_get_wtime();
// Allocate space for current grid (1 byte per tile)
game.grid = malloc(sizeof(unsigned char*) * (game.height+(2*game.padding)));
for (int i = 0; i < game.height+(2*game.padding); i++) {
game.grid[i] = malloc(sizeof(unsigned char) * (game.width+(2*game.padding)));
memset(game.grid[i], 0, game.width+(2*game.padding));
}
if (strcmp(filename, "random") == 0) {
randomize(&game);
} else {
read_in(filename, &game);
}
char iteration_file[1024];
double time_computing_life = 0;
double start, end;
for (int i = 0; i <= iterations; i++) {
if (i > 0) {
// Iteration 0 is just the input board
start = omp_get_wtime();
next(&game, threads);
end = omp_get_wtime();
time_computing_life += ((double) (end - start));
}
if (log_each_step) {
#if VERBOSE == 1
printf("\n===Iteration %i===\n", i);
for (int y = game.padding; y < game.height+game.padding; y++) {
for (int x = game.padding; x < game.width+game.padding; x++) {
printf("%s ", game.grid[y][x] ? "X" : " ");
}
printf("\n");
}
printf("===End iteration %i===\n", i);
#endif
sprintf(iteration_file, "output/iteration-%07d.bin", i);
write_out(iteration_file, &game);
}
}
double total_clock_time = ((double) (omp_get_wtime() - global_start));
printf("\n===Timing===\nTime computing life: %f\nClock time: %f\n", time_computing_life, total_clock_time);
}
int main(int argc, char** argv) {
if (argc >= 2) {
if (strcmp(argv[1], "simulate") == 0) {
simulate(argc, argv);
} else if (strcmp(argv[1], "create-grid") == 0) {
create_grid(argc, argv);
} else {
printf("Unknown input: %s\n", argv[1]);
exit(1);
}
} else {
printf("Usage: ./gol <simulate | create-grid>\n");
exit(1);
}
return 0;
}

54
serial/Makefile Normal file
View File

@ -0,0 +1,54 @@
CC ?= gcc
SRC_PATH = src
BUILD_PATH = build
BIN_PATH = $(BUILD_PATH)/bin
BIN_NAME = gol
SRC_EXT = c
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' | sort -k 1nr | cut -f2-)
OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o)
DEPS = $(OBJECTS:.o=.d)
COMPILE_FLAGS = -Wall -g -std=c99
INCLUDES = -I include/
LIBS =
.PHONY: default_target
default_target: release
.PHONY: release
release: dirs
@$(MAKE) all
.PHONY: dirs
dirs:
@echo "Creating directories"
@mkdir -p $(dir $(OBJECTS))
@mkdir -p $(BIN_PATH)
.PHONY: clean
clean:
@echo "Deleting $(BIN_NAME) symlink"
@$(RM) $(BIN_NAME)
@echo "Deleting directories"
@$(RM) -r $(BUILD_PATH)
@$(RM) -r $(BIN_PATH)
.PHONY: all
all: $(BIN_PATH)/$(BIN_NAME)
@echo "Making symlink: $(BIN_NAME) -> $<"
@$(RM) $(BIN_NAME)
@ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
$(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
@echo "Linking: $@"
$(CC) $(OBJECTS) -o $@ ${LIBS}
-include $(DEPS)
$(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT)
@echo "Compiling: $< -> $@"
$(CC) $(COMPILE_FLAGS) $(INCLUDES) -MP -MMD -c $< -o $@

View File

@ -0,0 +1,12 @@
#include <stdio.h>
#include <stdlib.h>
#include "file.h"
#include "game.h"
#ifndef CREATE_GRID_H
#define CREATE_GRID_H
void print_grid(struct GAME* game);
void create_grid(int argc, char** argv);
#endif // CREATE_GRID_H

11
serial/include/file.h Normal file
View File

@ -0,0 +1,11 @@
#include "game.h"
#include <stdlib.h>
#include <stdio.h>
#ifndef FILE_H
#define FILE_H
void read_in(char* filename, struct GAME* game);
void write_out(char* filename, struct GAME* game);
#endif //FILE_H

20
serial/include/game.h Normal file
View File

@ -0,0 +1,20 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef GAME_H
#define GAME_H
struct GAME {
unsigned char** grid;
int padding;
int width;
int height;
};
int neighbors(struct GAME* game, int x, int y);
void next(struct GAME* game);
void update(struct GAME* game, int x1, int x2, int y1, int y2);
void randomize(struct GAME* game);
#endif // GAME_H

38
serial/src/create_grid.c Normal file
View File

@ -0,0 +1,38 @@
#include "create_grid.h"
void print_grid(struct GAME* game) {
printf("\n===GRID===\n");
for (int y = 0; y < game->height; y++) {
for (int x = 0; x < game->width; x++) {
printf("%i ", game->grid[y][x]);
}
printf("\n");
}
}
void create_grid(int argc, char** argv) {
char* filename;
struct GAME game;
game.padding = 0;
if (argc == 5) {
game.width = atoi(argv[2]);
game.height = atoi(argv[3]);
filename = argv[4];
} else {
printf("Usage: ./gol create-grid <width> <height> <filename>\n");
exit(1);
}
unsigned char** grid = malloc(sizeof(unsigned char*) * game.height);
for (int y = 0; y < game.height; y++) {
grid[y] = malloc(sizeof(unsigned char*) * game.width);
printf("Row %i: ", y);
for (int x = 0; x < game.width; x++) {
char temp;
scanf("%i%c", (unsigned int*)&grid[y][x],&temp);
}
}
game.grid = grid;
write_out(filename, &game);
print_grid(&game);
}

17
serial/src/file.c Normal file
View File

@ -0,0 +1,17 @@
#include "file.h"
void read_in(char* filename, struct GAME* game) {
FILE* file = fopen(filename, "rb");
for (int i = game->padding; i < game->height+game->padding; i++) {
fread(game->grid[i] + game->padding, sizeof(unsigned char), game->width, file);
}
fclose(file);
}
void write_out(char* filename, struct GAME* game) {
FILE* file = fopen(filename, "w+");
for (int i = game->padding; i < game->height+game->padding; i++) {
fwrite(game->grid[i] + game->padding, sizeof(unsigned char), game->width, file);
}
fclose(file);
}

53
serial/src/game.c Normal file
View File

@ -0,0 +1,53 @@
#include "game.h"
int neighbors(struct GAME* game, int x, int y) {
int n = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if (!(dx == 0 && dy == 0) && (x+dx) > 0 && (y+dy) > 0 && (x+dx) < game->width+(game->padding*2) && (y+dy) < game->height+(game->padding*2)) {
if (game->grid[y+dy][x+dx]) {
n++;
}
}
}
}
return n;
}
void next(struct GAME* game) {
unsigned char** newGrid = malloc(sizeof(unsigned char*) * (game->height+(game->padding*2)));
int size = sizeof(unsigned char) * (game->width+(game->padding*2));
for (int y = 0; y < game->height+(game->padding*2); y++) {
newGrid[y] = malloc(size);
memset(newGrid[y], 0, size);
}
for (int y = 0; y < game->height+(game->padding*2); y++) {
for (int x = 0; x < game->width+(game->padding*2); x++) {
int my_neighbors = neighbors(game, x, y);
if (game->grid[y][x]) {
if (my_neighbors < 2 || my_neighbors > 3) {
newGrid[y][x] = 0;
} else {
newGrid[y][x] = 1;
}
} else {
if (my_neighbors == 3) {
newGrid[y][x] = 1;
}
}
}
}
free(game->grid);
game->grid = newGrid;
}
void randomize(struct GAME* game) {
for (int y = game->padding; y < game->height+game->padding; y++) {
for (int x = game->padding; x < game->width+game->padding; x++) {
game->grid[y][x] = rand() & 1;
}
}
}

103
serial/src/main.c Normal file
View File

@ -0,0 +1,103 @@
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include "file.h"
#include "game.h"
#include "create_grid.h"
/*
Rules for life:
Any live cell with fewer than two live neighbors dies (underpopulation).
Any live cell with two or three live neighbors continues to live.
Any live cell with more than three live neighbors dies (overpopulation).
Any dead cell with exactly three live neighbors becomes a live cell (reproduction).
*/
#define PADDING 10
//#define VERBOSE 1
#define SEED 100
void simulate(int argc, char** argv) {
srand(SEED);
char* filename;
struct GAME game;
game.padding = PADDING;
int iterations, log_each_step;
if (argc == 7) {
filename = argv[2];
game.width = atoi(argv[3]);
game.height = atoi(argv[4]);
iterations = atoi(argv[5]);
log_each_step = atoi(argv[6]);
} else {
printf("Usage: ./gol simulate <filename | random> <width> <height> <iterations> <log-each-step?1:0>\n");
filename = "output/out.bin";
game.height = 10;
game.width = 10;
iterations = 5;
log_each_step = 0;
}
clock_t global_start = clock();
// Allocate space for current grid (1 byte per tile)
game.grid = malloc(sizeof(unsigned char*) * (game.height+(2*game.padding)));
for (int i = 0; i < game.height+(2*game.padding); i++) {
game.grid[i] = malloc(sizeof(unsigned char) * (game.width+(2*game.padding)));
memset(game.grid[i], 0, game.width+(2*game.padding));
}
if (strcmp(filename, "random") == 0) {
randomize(&game);
} else {
read_in(filename, &game);
}
char iteration_file[1024];
double time_computing_life = 0;
clock_t start, end;
for (int i = 0; i <= iterations; i++) {
if (i > 0) {
// Iteration 0 is just the input board
start = clock();
next(&game);
end = clock();
time_computing_life += ((double) (end - start)) / CLOCKS_PER_SEC;
}
if (log_each_step) {
#if VERBOSE == 1
printf("\n===Iteration %i===\n", i);
for (int y = game.padding; y < game.height+game.padding; y++) {
for (int x = game.padding; x < game.width+game.padding; x++) {
printf("%s ", game.grid[y][x] ? "X" : " ");
}
printf("\n");
}
printf("===End iteration %i===\n", i);
#endif
sprintf(iteration_file, "output/iteration-%07d.bin", i);
write_out(iteration_file, &game);
}
}
double total_clock_time = ((double) (clock() - global_start)) / CLOCKS_PER_SEC;
printf("\n===Timing===\nTime computing life: %f\nClock time: %f\n", time_computing_life, total_clock_time);
}
int main(int argc, char** argv) {
if (argc >= 2) {
if (strcmp(argv[1], "simulate") == 0) {
simulate(argc, argv);
} else if (strcmp(argv[1], "create-grid") == 0) {
create_grid(argc, argv);
} else {
printf("Unknown input: %s\n", argv[1]);
exit(1);
}
} else {
printf("Usage: ./gol <simulate | create-grid>\n");
exit(1);
}
return 0;
}