Python Conway的人生游戏-并行优化

时间:2020-07-17 23:16:03

标签: python numpy parallel-processing pyglet conways-game-of-life

对于使用Python和pyglet进行Conway的《生命游戏》的简单且可扩展的实现,我有以下代码。

import numpy as np
import pyglet
from pyglet import shapes

COLOURS = [(255, 255, 255), (0, 0, 155)]

class Board(np.ndarray):
    def __new__(cls, box=10, *args, **kwargs):
        return np.ndarray.__new__(cls, (box, box), *args, dtype=int, **kwargs)

    def __init__(self, box, *args, **kwargs):
        self.box = box
        self.conditions = np.array([
            [0, 0, 0, 1, 0, 0, 0, 0, 0], 
            [0, 0, 1, 1, 0, 0, 0, 0, 0]
        ])
        self.PIXEL = kwargs.get('pixel_size', 10)

    def populate(self, array : np.ndarray):
        self[:, :] = array[:, :]

    def neighbours(self, i : int, j : int) -> int:
        return sum([
            self[i-1 % self.box - 1, j-1 % self.box - 1],
            self[i % self.box - 1, j-1 % self.box - 1],
            self[i+1 % self.box - 1, j-1 % self.box - 1],
            self[i-1 % self.box - 1, j % self.box - 1],
            self[i+1 % self.box - 1, j % self.box - 1],
            self[i-1 % self.box - 1, j+1 % self.box - 1],
            self[i % self.box - 1, j+1 % self.box - 1],
            self[i+1 % self.box - 1, j+1 % self.box - 1],
        ])

    def next(self):
        new = Board(self.box)
        for i, row in enumerate(self):
            for j, idx in enumerate(row):
                neighbours = self.neighbours(i, j)
                new[i, j] = self.conditions[self[i, j], neighbours]
        return new

    def run(self, n):
        result = self.next()
        for i in range(n-1): 
            result = result.next()
        return result

    def render(self, dt, window=None):
        if not window:
            window = pyglet.window.Window()

        @window.event
        def on_draw():
            window.clear()
            for i, row in enumerate(self):
                for j, state in enumerate(row):
                    shapes.Rectangle(
                            x=self.PIXEL * i, 
                            y=self.PIXEL * j, 
                            width=self.PIXEL, 
                            height=self.PIXEL, 
                            color=COLOURS[state]
                        ).draw()

        if not window:
            pyglet.app.run()
        
        if window:
            self[:, :] = self.next()

    def animate(self):
        window = pyglet.window.Window(
                width=self.PIXEL*self.shape[0], 
                height=self.PIXEL*self.shape[1]
            )
        pyglet.clock.schedule_interval(self.render, 1./60., window)
        pyglet.app.run()

if __name__ == '__main__':
    import sys
    try:
        size = sys.argv[1]
    except IndexError:
        size = 50
    board = Board(size)
    board.populate(np.random.randint(2, size=board.shape))
    board.animate()

我想知道如何提高性能(因为目前动画对于大尺寸动画来说要慢得多)。

我的主要想法是向Board.next()方法添加并行性,并可能在on_draw()中添加Board.render()函数,以消除double for循环。此功能的主要任务是创建Board的新实例(基本上只是一个新的np.ndarray)并更新每个单元格:

def next(self):
    new = Board(self.box)
    for i, row in enumerate(self):
        for j, idx in enumerate(row):
            neighbours = self.neighbours(i, j)
            new[i, j] = self.conditions[self[i, j], neighbours]
    return new

...

@window.event
def on_draw():
    window.clear()
    for i, row in enumerate(self):
        for j, state in enumerate(row):
            shapes.Rectangle(
                    x=self.PIXEL * i, 
                    y=self.PIXEL * j, 
                    width=self.PIXEL, 
                    height=self.PIXEL, 
                    color=COLOURS[state]
                ).draw()

我完全确定,由于我们创建了一个新数组,因此可以并行完成此操作,因此我有以下问题:

  • 我该怎么办?
  • 有很多不同的方法可以实现这种并行性吗?
  • 是否还有其他基于图形的优化与其余代码相关,从而可以加快速度?

0 个答案:

没有答案