How to implement the Game of Life in parallel using C++ threads?

685 views Asked by At

I have implemented a sequential version of the Game of Life but now I want to parallelize it. I have tried using the advice I got from the one answer on here but now I am getting an error when trying to compile. The error says "invalid use of non-static member function CalcRow" and then the line it gives right after is: threads.push_back(thread(CalcRow, board, row)); Not sure how to fix it. I have tried declaring it as static but that doesn't work and it still gives the same error.

Here is my try for the parallel implementation:

GameOfLife.h:

#include <vector>
#include <thread>
using namespace std;


class GameOfLife {

    public:

        vector<vector<int> > SimulateLife(vector<vector<int> > &board, int life_cycles);

    private:

        vector<vector<int> > board;
        vector<vector<int> > nextBoard;

        int CheckNeighbors(vector<vector<int> > &board, int row, int col);
        void CalcRow(vector<vector<int> > &board, int row);
};


//Checks all 8 neighbors of the current cell to see if they are alive
//If they are alive add one to liveNeighbors

int GameOfLife::CheckNeighbors(vector<vector<int> > &board, int row, int col) {
    int liveNeighbors = 0;

    if(board[(board.size()+row-1)%board.size()][(board.size()+col-1)%board.size()] == 1)
        liveNeighbors++;

    if(board[(board.size()+row-1)%board.size()][col] == 1)
        liveNeighbors++;

    if(board[(board.size()+row-1)%board.size()][(board.size()+col+1)%board.size()] == 1)
        liveNeighbors++;

    if(board[row][(board.size()+col+1)%board.size()] == 1)
        liveNeighbors++;

    if(board[(board.size()+row+1)%board.size()][(board.size()+col+1)%board.size()] == 1)
        liveNeighbors++;

    if(board[(board.size()+row+1)%board.size()][col] == 1)
        liveNeighbors++;

    if(board[(board.size()+row+1)%board.size()][(board.size()+col-1)%board.size()] == 1)
        liveNeighbors++;

    if(board[row][(board.size()+col-1)%board.size()] == 1)
        liveNeighbors++;

    return liveNeighbors;
}


void GameOfLife::CalcRow(vector<vector<int> > &board, int row) {
    vector<int> x;

    for(int col = 0; col < board.size(); col++) {
        int aliveNeighbors = 0;

        if(board[row][col] == 2) 
            x.push_back(2);

        else if(board[row][col] == 1) {
            aliveNeighbors = CheckNeighbors(board, row, col);
            if(aliveNeighbors < 2 || aliveNeighbors > 3)
                x.push_back(0);
            else
                x.push_back(1);

        }

        else if(board[row][col] == 0) {
            aliveNeighbors = CheckNeighbors(board, row, col);
            if(aliveNeighbors == 3)
                x.push_back(1);
            else
                x.push_back(0);
        }
    }    

    nextBoard.swap(board);
}


vector<vector<int> > GameOfLife::SimulateLife(vector<vector<int> > &board, int life_cycles) {
    vector<thread> threads;

    for(int cycles = life_cycles; cycles > 0; cycles--) {
        for(int row = 0; row < board.size(); row++)
            threads.push_back(thread(CalcRow, board, row);

        for(int i = 0; i < threads.size(); i++)
            threads[i].join();
    }

    return board;
}    

Main.cc:

#include <iostream>
#include <vector>
#include <thread>
#include "GameOfLife.h"
using namespace std;


void print_board(vector<vector<int> > &board) {
    int n = board.size();

    for (int i=0;i<n;i++) {
        for (int j=0;j<n;j++) {
            cout << board[i][j] << " ";
        }
        cout << endl;
    }
}


int main() {
    int n;
    cin >> n;

    vector<vector<int> > board;
    board.resize(n);

    for (int i=0;i<n;i++) {
        board[i].resize(n);
        for (int j=0;j<n;j++) {
            cin >> board[i][j];
        }
    }

    int k;
    cin >> k;

    GameOfLife obj;
    vector<vector<int> > result;
    result = obj.SimulateLife(board,k);
    print_board(result);
}
1

There are 1 answers

6
Michael Surette On

Here's an overview of how I would do it.

I would make two boards, the current board and the next.

I would create a vector of threads the for the number of rows.

Start each thread with the calculation of one new row using the current data. You will want to create a row local to the thread which gets copied after its done to prevent ghost sharing.

Join all threads to make sure the board is fully calculated.

Swap the boards, repeat.