在许多情况下,科学家使用模板模拟系统的动态,这是通过网格卷积数学运算符。通常,此操作消耗大量计算资源。 Here是对这个想法的一个很好的解释。
在numpy中,编程2D 5点模板的规范方法如下:
for i in range(rows):
for j in range(cols):
grid[i, j] = ( grid[i,j] + grid[i-1,j] + grid[i+1,j] + grid[i,j-1] + grid[i,j+1]) / 5
或者,更有效率,使用切片:
grid[1:-1,1:-1] = ( grid[1:-1,1:-1] + grid[0:-2,1:-1] + grid[2:,1:-1] + grid[1:-1,0:-2] + grid[1:-1,2:] ) / 5
但是,如果您的网格非常大,它将无法修复您的内存,或者如果卷积操作非常复杂,则需要很长时间,并行编程技术可用于克服此问题或仅仅是为了获取结果更快。像Dask这样的工具允许科学家以平行 - 几乎透明的方式自己编程这种模拟。目前,Dask不支持项目分配,因此,如何使用Dask对模板进行编程。
答案 0 :(得分:2)
好问题。你是正确的dask.array 做提供并行计算,但不不支持项目分配。我们可以通过使函数一次对一块numpy数据进行操作,然后通过在我们的数组中以稍微重叠的边界映射该函数来解决模板计算。
你应该创建一个带有numpy数组的函数,并返回一个应用了模板的新numpy数组。这不应该修改原始数组。
def apply_stencil(x):
out = np.empty_like(x)
... # do arbitrary computations on out
return out
Dask数组通过将数组分成较小数组的不相交块来并行运行。模板计算等操作需要在相邻块之间进行一些重叠。幸运的是,这可以通过dask.array.ghost模块和特别是dask.array.map_overlap方法来处理。
实际上,map_overlap
docstring中的示例是1d前向有限差分计算
>>> x = np.array([1, 1, 2, 3, 3, 3, 2, 1, 1])
>>> x = from_array(x, chunks=5)
>>> def derivative(x):
... return x - np.roll(x, 1)
>>> y = x.map_overlap(derivative, depth=1, boundary=0)
>>> y.compute()
array([ 1, 0, 1, 1, 0, 0, -1, -1, 0])
答案 1 :(得分:1)
Dask在内部将数组划分为较小的numpy arays,当您使用dask.array创建数组时,必须提供有关如何将其划分为块的一些信息,如下所示:
grid = dask.array.zeros((100,100), chunks=(50,50))
请求一个100 x 100的数组,分为4个块。现在,要对新创建的数组进行卷积操作,必须共享块边界的信息。 Dask ghost cells,管理这样的情况。
常见的工作流程意味着:
例如,
import dask.array as da
grid = da.zeros((100,100), chunks=(50,50))
g = da.ghost.ghost(grid, depth={0:1,1:1}, boundary={0:0,1:1})
g2 = g.map_blocks( some_function )
s = da.ghost.trim_internals(g2, {0:1,1:1})
s.compute()
请记住,Dask创建一个字典来表示任务图,真正的计算由s.compute()
触发。如MRocklin所述,映射函数必须返回一个numpy数组。
默认情况下,dask.array使用dask.theated调度程序来提高性能,但是一旦信息被共享,类似于模板的问题就会令人尴尬并行,这意味着不必共享资源和信息,并且计算可以映射到不同的核心甚至不同的计算为此,可以指示dask使用不同的调度程序,例如dask.multiprocessing:
import dask.multiprocessing
import dask
dask.set_options(get=dask.multiprocessing.get)
当触发compute()
时,Dask将创建多个python实例,如果你的应用程序足够大以支付创建这个新实例的开销,那么dask.multiprocessing将提供更好的性能。
有关Dask调度程序的更多信息,请参见here。