/* * simulate.c * * Implement your (parallel) simulation here! */ #define WAVE_CONSTANT 0.15 #include "simulate.h" #include #include #include #include #include #include #include #include /* Add any global variables you may need. */ double *current_array; double *next_array; double *old_array; struct worker_data { int i_min; int size; }; struct worker_data *data; int n_done = 0, t, num_threads, t_max; bool running = true; pthread_cond_t rotated = PTHREAD_COND_INITIALIZER; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /* Add any functions you may need (like a worker) here. */ void rotate(void) { /* Rotate the global arrays. */ double *tmp = old_array; old_array = current_array; current_array = next_array; next_array = tmp; } void end_simulation(void) { /* Tell the threads to stop running. */ running = false; pthread_cond_broadcast(&rotated); } void *worker(void *ptr) { struct worker_data *my_data = (struct worker_data *)ptr; while (running) { /* Compute new values for each point in the wave function. */ for (int i = my_data->i_min; i < (my_data->i_min + my_data->size); ++i) { double left = current_array[i - 1]; double right = current_array[i + 1]; double current = current_array[i]; double old = old_array[i]; next_array[i] = 2 * current - old + WAVE_CONSTANT * (left - (2 * current - right)); } /* Synchronise with the other threads and wait until * the arrays are rotated. */ /* Take a lock, which protects both n_done * and the old, current and next pointers. */ pthread_mutex_lock(&mutex); n_done++; if (n_done == num_threads) { // We're the last worker, so we have to rotate // the arrays and go to the next timestep. n_done = 0; rotate(); /* old, current, next = current, next, old */ t++; if (t != t_max) { /* This is not the end of the simulation. * Notify the other threads that they can * continue. */ pthread_cond_broadcast(&rotated); } else { /* We've arrived at the end of the simulation. * Tell the threads to stop running and exit. * The results are returned from simulate() */ end_simulation(); } } else { // If we're not the last worker, wait until // the last worker has completed the rotation. pthread_cond_wait(&rotated, &mutex); } pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } /* * Executes the entire simulation. * * Implement your code here. * * i_max: how many data points are on a single wave * t_max: how many iterations the simulation should run * num_threads: how many threads to use (excluding the main threads) * old_array: array of size i_max filled with data for t-1 * current_array: array of size i_max filled with data for t * next_array: array of size i_max. You should fill this with t+1 */ double *simulate(const int i_max, const int _t_max, const int _num_threads, double *_old_array, double *_current_array, double *_next_array) { /* Assign local vars to the global vars. */ old_array = _old_array; current_array = _current_array; next_array = _next_array; num_threads = _num_threads; t_max = _t_max; // First, slice up the input data into num_threads pieces. int chunk_size = ceil((double)i_max / num_threads); pthread_t thread_ids[num_threads]; struct worker_data d[num_threads]; data = d; int items_left = i_max; for (int i = 0; i < num_threads; ++i) { data[i].i_min = (i * chunk_size) + 1; data[i].size = (i + 1 == num_threads) ? items_left - 2 : chunk_size; items_left -= chunk_size; // Create the worker threads pthread_create(&thread_ids[i], NULL, worker, &data[i]); } // Wait until the worker threads are done computing... for (int i = 0; i < num_threads; ++i) { pthread_join(thread_ids[i], NULL); } // And return a pointer to the final results. return current_array; }