作业1
作业描述
实现了一个简单的生命游戏。使用几种不同的模式进行初始化。
按照像素画出的细胞太小了,按比例放大显示每个细胞。
效果展示
Random
Oscillators
Glider
遇到的问题
使用gpu时,可以得到以上图形,但使用cpu时得到的图形并不一致,且不符合预期,不清楚哪里可能导致了这样的问题。对比Random初始化后分别使用gpu和cpu得到的图形。
实现了一个简单的生命游戏。使用几种不同的模式进行初始化。
按照像素画出的细胞太小了,按比例放大显示每个细胞。
使用gpu时,可以得到以上图形,但使用cpu时得到的图形并不一致,且不符合预期,不清楚哪里可能导致了这样的问题。对比Random初始化后分别使用gpu和cpu得到的图形。
Taichi 里不同 backend 实现 ti.random()
的方法确实不同,但是 cpu 的运行效果应该是由bug导致的。
在 Python-scope 里如果想把一个 field 的数据拷贝给另一个 field,应该使用 next_gen.copy_from(cells)
。如果单纯写 next_gen = cells
,实际上是使得 next_gen
和 cells
指向了同一个 field。
把你代码里的 85 行和 89 行改成 copy_from
,cpu 和 gpu 上的结果应该就一致了。
如果把evolve函数换成下面这个(把inner loop变成串行),也会得到和cpu一样类似的错误结果:
@ti.kernel
def evolve():
neighbors = 0
for i in range(cells.shape[0]):
for j in range(cells.shape[1]):
neighbors = int(cells[(i-1) % N, (j-1) % N]+cells[i, (j-1) % N] +
cells[(i+1) % N, (j-1) % N] + cells[(i-1) % N, j] +
cells[(i+1) % N, j] + cells[(i-1) % N, (j+1) % N] +
cells[i, (j+1) % N]+cells[(i+1) % N, (j+1) % N])
if cells[i, j] == 1:
if (neighbors < 2) or (neighbors > 3):
next_gen[i, j] = 0
else:
if neighbors == 3:
next_gen[i, j] = 1
所以猜测,目前GPU结果看起来正确,大概率是因为线程够多,避免了data race
这个解释是正确的,原因是Python本身是弱类型的,当你把两个field在Python Scope中赋值了之后,他们两个实际指向了同一个field,并没有进行数据拷贝。
所以你的代码实际上是在做这件事:
@ti.kernel
def evolve():
neighbors = 0
for i, j in cells:
neighbors = int(cells[(i-1) % N, (j-1) % N]+cells[i, (j-1) % N] +
cells[(i+1) % N, (j-1) % N] + cells[(i-1) % N, j] +
cells[(i+1) % N, j] + cells[(i-1) % N, (j+1) % N] +
cells[i, (j+1) % N]+cells[(i+1) % N, (j+1) % N])
if cells[i, j] == 1:
if (neighbors < 2) or (neighbors > 3):
cells[i, j] = 0
else:
if neighbors == 3:
cells[i, j] = 1
同一个kernel中同时在读写cells这个field。当你运气好并行度高的时候不会发生数据争用,运行结果是正确的。当你需要串行执行,或者当你并发数少于cells的大小的时候,就会出问题。
给余畅同学点赞!
感谢几位老师帮忙找bug
同时也解决了glider图形移动一段时间丢失的问题 : )
改写了一下,把临时field的拷贝写在kernel函数里了。
@ti.kernel
def evolve():
for i, j in cells:
new_cells[i, j] = cells[i, j]
neighbors = 0
for i, j in cells:
neighbors = (cells[(i-1) % N, (j-1) % N]+cells[i, (j-1) % N]
+ cells[(i+1) % N, (j-1) % N] + cells[(i-1) % N, j]
+ cells[(i+1) % N, j] + cells[(i-1) % N, (j+1) % N]
+ cells[i, (j+1) % N]+cells[(i+1) % N, (j+1) % N])
if cells[i, j] == 1:
if (neighbors < 2) or (neighbors > 3):
new_cells[i, j] = 0
else:
if neighbors == 3:
new_cells[i, j] = 1
for i, j in cells:
cells[i, j] = new_cells[i, j]