-2

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);
}
Jordan Ward
  • 29
  • 1
  • 7
  • 3
    What is not working? (I did not downvote although you should read https://stackoverflow.com/help/mcve) –  Feb 26 '19 at 17:35
  • I suspect this is off topic for SO. You will probably get more people more willing/able to offer help improving this code at Stack Exchange. – Tim Randall Feb 26 '19 at 17:53
  • @TimRandall Thank you. I guess I thought SO was for this kind of thing as well. I appreciate the advice without downvoting me. – Jordan Ward Feb 27 '19 at 00:30
  • @peakpeak The code here works. I was looking for advice on how to parallelize it using the c++ thread library. I just don't know how to go about it. – Jordan Ward Feb 27 '19 at 00:40
  • @TimRandall So I changed my question based on the feedback I got, is it going to be taken off hold so I can get help with my error or no? – Jordan Ward Feb 28 '19 at 15:45
  • @JordanWard given the downvotes and the On Hold status _and_ the fact that you now have a different question, I would formalize that fact by writing a new question. If you review the "How To Ask" link above, and the link for [mcve], and take that information on board when writing the new question, I think you'll get a much more positive response. – Tim Randall Feb 28 '19 at 20:11

1 Answers1

1

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.

Michael Surette
  • 701
  • 1
  • 4
  • 12
  • 1
    It will be interesting to see how big the board needs to be for the threaded version to outperform the non-threaded version. At some point, may be better for a thread to be responsible for several rows instead of one thread per one row. – Eljay Feb 26 '19 at 18:09
  • Would you please clarify on what you mean by starting each thread with the calculation of one new row using the current data? Also, how do I declare a thread local row? Thank you! – Jordan Ward Feb 27 '19 at 06:32
  • Write a subroutine with an int argument that indicates the row. This will be what is called by your thread. In that subroutine have a vector. Update this vector using data from the current board as if it was the indicated row. Once done, swap this row with the indicated row in the new board. Vector swap is quick. – Michael Surette Feb 27 '19 at 14:55
  • @MichaelSurette I'm sorry I just don't understand what the subroutine is actually supposed to do. What would the vector hold? – Jordan Ward Feb 27 '19 at 17:49
  • @JordanWard You need a subroutine for each thread to execute. It calculates a new row 1 column at a time. Once done you have a complete row which you swap out to your new board. Perhaps you could benefit from a good tutorial on multithreading in C++. – Michael Surette Feb 27 '19 at 18:24
  • @MichaelSurette I tried to implement what you have suggested but I am stuck on an error. I have updated my question to show the new code and the error that it is giving me. – Jordan Ward Feb 27 '19 at 23:37