Как сделать многопроцессорность для циклов в python, где каждый расчет независим? - PullRequest
0 голосов
/ 21 февраля 2020

Я пытаюсь узнать что-то немного новое в каждом мини-проекте, который я делаю. Я создал программу Game of Life (https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life).

Это включает массив numpy, где каждая точка в массиве ("ячейка") имеет целочисленное значение. Чтобы развить состояние игры, вы должны вычислить для каждой ячейки сумму всех значений соседей (8 соседей).

Соответствующий класс в моем коде выглядит следующим образом, где evolve() занимает одно xxx_method методов. Он отлично работает для conv_method и loop_method, но я хочу использовать многопроцессорность (которая, как я определил, должна работать, в отличие от многопоточности?) На loop_method, чтобы увидеть какое-либо увеличение производительности. Я чувствую, что это должно работать, так как каждый расчет независим. Я попробовал наивный подход, но на самом деле недостаточно хорошо понимаю многопроцессорный модуль. Могу ли я также использовать его в методе evolve(), так как я снова чувствую, что каждый расчет в двойных циклах for независим.

Любая помощь приветствуется, включая комментарии общего кода.

Редактировать - я получаю RuntimeError, которую я почти ожидаю, так как мое понимание многопроцессорности недостаточно хорошее. Что нужно сделать с кодом, чтобы он заработал?

class GoL:
    """ Game Engine """
    def __init__(self, size):
        self.size = size
        self.grid = Grid(size) # Grid is another class ive defined

    def evolve(self, neigbour_sum_func):
        new_grid = np.zeros_like(self.grid.cells) # start with everything dead, only need to test for keeping/turning alive
        neighbour_sum_array = neigbour_sum_func()
        for i in range(self.size):
            for j in range(self.size):
                cell_sum = neighbour_sum_array[i,j]
                if self.grid.cells[i,j]: # already alive
                    if cell_sum == 2 or cell_sum == 3:
                        new_grid[i,j] = 1
                else: # test for dead coming alive
                    if cell_sum == 3:
                        new_grid[i,j] = 1

        self.grid.cells = new_grid

    def conv_method(self):
        """ Uses 2D convolution across the entire grid to work out the neighbour sum at each cell """
        kernel = np.array([
                            [1,1,1],
                            [1,0,1],
                            [1,1,1]],
                            dtype=int)
        neighbour_sum_grid = correlate2d(self.grid.cells, kernel, mode='same')
        return neighbour_sum_grid

    def loop_method(self, partition=None):
        """ Also works out neighbour sum for each cell, using a more naive loop method """
        if partition is None:
            cells = self.grid.cells # no multithreading, just work on entire grid
        else:
            cells = partition # just work on a set section of the grid

        neighbour_sum_grid = np.zeros_like(cells) # copy
        for i, row in enumerate(cells):
            for j, cell_val in enumerate(row):
                neighbours = cells[i-1:i+2, j-1:j+2]
                neighbour_sum = np.sum(neighbours) - cell_val
                neighbour_sum_grid[i,j] = neighbour_sum
        return neighbour_sum_grid

    def multi_loop_method(self):
        cores = cpu_count()
        procs = []
        slices = []
        if cores == 2: # for my VM, need to impliment generalised method for more cores
            half_grid_point = int(SQUARES / 2)
            slices.append(self.grid.cells[0:half_grid_point])
            slices.append(self.grid.cells[half_grid_point:])
        else:
            Exception

        for sl in slices:
            proc = Process(target=self.loop_method, args=(sl,))
            proc.start()
            procs.append(proc)

        for proc in procs:
            proc.join()

1 Ответ

2 голосов
/ 21 февраля 2020

Я хочу использовать многопроцессорность (которая, как я определил, должна работать, в отличие от многопоточности?)

Многопоточность не будет работать, потому что она будет работать на одном процессоре, который является вашим текущим узким местом , Многопоточность предназначена для вещей, где вы ждете ответа API. В то же время вы можете сделать другие расчеты. Но в игре Conway's Game of Life ваша программа постоянно работает.


Правильно настроить многопроцессорность сложно. Если у вас есть 4 процессора, вы можете определить квадрант для каждого вашего процессора. Но вам нужно поделиться результатом между вашими процессорами. И с этим вы получаете удар производительности. Они должны быть синхронизированы / работать на той же тактовой частоте / иметь одинаковую частоту тиков для обновления, и результат должен быть разделен.

Многопроцессорная обработка становится возможной, когда ваша сетка очень большая / есть тонна для посчитайте.
Поскольку вопрос очень широкий и сложный, я не могу дать вам лучший ответ. Есть статья о том, как получить параллельную обработку в игре Конвея о жизни: http://www.shodor.org/media/content/petascale/materials/UPModules/GameOfLife/Life_Module_Document_pdf.pdf

...