Including OpenMP

Download Pandemic-OMP.tgz

It is really easy to include OpenMP features into existing code we have. All we need to do is to identify all the functions that could use OpenMP. There are in total 5 functions that could use OpenMP to increase performance. The first function is the init_array() function in Initialize.h file. The next four functions are all the core functions inside Core.h file.

In Initialize.h

init_array()

This function can be divided into four parts: the first part sets the states of the initially infected people and sets the count of infected people. The second part sets states of the rest of the people and sets the of susceptible people. The third part sets random x and y locations for each people. The last part initilize the number of days infected of each people to 0.

Normally, to include OpenMP, all we need is to put #pragma omp parallel in front of each of the for loops. However, our case is a little tricky. The problem is that we are reducing two counters in the first two parts of the function. Different from most parallel structure, reduction in OpenMP is pretty easy to implement. We just need to add a reduction literal,

    #pragma omp parallel for private(current_person_id) \
        reduction(+:num_infected_local)

The problem lies on that the counters we are reducing is inside a structure, namely, the global structure. OpenMP does not support reduction to structures. Therefore, we solve this problem by first create local instance such as num_infected_local that equals to counter num_infected in global struct

    int num_infected_local = global->num_infected;

we can then, reduce to local instance,

        num_infected_local++;

Finally, we put local instance back to struct.

    global->num_infected = num_infected_local;

We then use the same reduction method for the second part of the function. The third and Fourth part of the function does not reduce any counters, which means we don’t need worry about reduction at all.

In Core.h

There are four core functions inside Core.h file, and all of them can be parallelized using OpenMP.

move()

This function is easy to parallelize because it does not perform any reduction. However, we need to specify the variables that is private to each OpenMP threads. current_person_id is iterator that is clearly private. x_move_direction and y_move_direction are different for every thread, which means they are private as well.

    #ifdef _OPENMP
    #pragma omp parallel for private(current_person_id, x_move_direction, \
        y_move_direction)
    #endif 

susceptible()

This function is relatively hard to parallelize because it has four counters to reduce. Luckily, we already developed our way of reducing counters in init_array() function, which means we can use same method in here.

Creating local instances

    // OMP does not support reduction to struct, create local instance
    // and then put local instance back to struct
    int num_infection_attempts_local = stats->num_infection_attempts;
    int num_infections_local = stats->num_infections;
    int num_infected_local = global->num_infected;
    int num_susceptible_local = global->num_susceptible;

OpenMP initialization

    #ifdef _OPENMP
    #pragma omp parallel for private(current_person_id, num_infected_nearby, \
        my_person) reduction(+:num_infection_attempts_local) \
        reduction(+:num_infected_local) reduction(+:num_susceptible_local) \
        reduction(+:num_infections_local)
    #endif 

Put local instances back to global struct

    // update struct data with local instances
    stats->num_infection_attempts = num_infection_attempts_local;
    stats->num_infections = num_infections_local;
    global->num_infected = num_infected_local;
    global->num_susceptible = num_susceptible_local;

infected()

Similar to susceptible() function, we have five counters to reduce in this function.

Creating local instances

    // OMP does not support reduction to struct, create local instance
    // and then put local instance back to struct
    int num_recovery_attempts_local = stats->num_recovery_attempts;
    int num_deaths_local = stats->num_deaths;
    int num_dead_local = global->num_dead;
    int num_infected_local = global->num_infected;
    int num_immune_local = global->num_immune;

OpenMP initialization

    #ifdef _OPENMP
    #pragma omp parallel for private(current_person_id) \
        reduction(+:num_recovery_attempts_local) reduction(+:num_dead_local) \
        reduction(+:num_infected_local) reduction(+:num_deaths_local) \
        reduction(+:num_immune_local)
    #endif 

Put local instances back to global struct

    // update struct data with local instances
    stats->num_recovery_attempts = num_recovery_attempts_local;
    stats->num_deaths = num_deaths_local;
    global->num_dead = num_dead_local;
    global->num_infected = num_infected_local;
    global->num_immune = num_immune_local;

update_days_infected()

We don’t have any reduction in this function, which means that the parallelization is relatively easy.

    #ifdef _OPENMP 
        #pragma omp parallel for private(current_person_id)
    #endif