【问题标题】:Dask opportunistic caching in custom graphs自定义图中的 Dask 机会性缓存
【发布时间】:2019-07-09 19:50:18
【问题描述】:

我有一个自定义 DAG,例如:

dag = {'load': (load, 'myfile.txt'),
       'heavy_comp': (heavy_comp, 'load'),
       'simple_comp_1': (sc_1, 'heavy_comp'),
       'simple_comp_2': (sc_2, 'heavy_comp'),
       'simple_comp_3': (sc_3, 'heavy_comp')}

我正在寻找计算键 simple_comp_1simple_comp_2simple_comp_3,我执行如下操作,

import dask
from dask.distributed import Client
from dask_yarn import YarnCluster

task_1 = dask.get(dag, 'simple_comp_1')
task_2 = dask.get(dag, 'simple_comp_2')
task_3 = dask.get(dag, 'simple_comp_3')
tasks = [task_1, task_2, task_3]

cluster = YarnCluster()
cluster.scale(3)
client = Client(cluster)
dask.compute(tasks)
cluster.shutdown()

看来,如果没有缓存,这 3 个键的计算将导致 heavy_comp 的计算也 3 次。由于这是一个繁重的计算,我尝试从here 实现机会缓存,如下所示:

from dask.cache import Cache
cache = Cache(2e9)
cache.register()

但是,当我尝试打印缓存的结果时,我什么也没得到:

>>> cache.cache.data
[]
>>> cache.cache.heap.heap
{}
>>> cache.cache.nbytes
{}

我什至尝试将缓存大小增加到 6GB,但没有效果。难道我做错了什么?如何让 Dask 缓存密钥 heavy_comp 的结果?

【问题讨论】:

  • 您如何评估您的图表?原则上,heavy_comp 应该只计算一次,如果您使用类似 dask.get(dag, [simple_comp1,simple_comp2,simple_comp3]) 的方式计算密钥
  • @malbert,对不起,我应该添加有关如何评估我的图表的详细信息,我现在在对上述问题的编辑中。 heavy_compload 这两个键的计算是否只会按照我上面显示的方式进行,还是必须像您显示的那样将键格式化为列表?
  • 没有缓存,每次调用dask.get,所有必要的计算都会从头开始执行。事实上,缓存应该负责存储中间结果,例如heavy_comp。不幸的是,我没有使用内置缓存的经验。但是,您的问题可以通过调用 dask.get(dag, [simple_comp1,simple_comp2,simple_comp3]) 来解决(如您所说,将关键字作为列表给出),因为 heavy_comp 只会被计算一次。还有一件事:在你的例子中,你应该只调用dask.get 而不是get 然后compute
  • @malbert,所以你是说dask.get(dag, simple_comp1,simple_comp2,simple_comp3]) 会缓存而dask.get(dag, simple_compx) 3 次不会?你对此有多大把握?你有参考吗?另外,不知道你用dask.get 代替get 是什么意思——不是我上面显示的吗?
  • 关于确定性,说服自己:考虑这张图import time; dag={'1':'heavy','2':'heavy','heavy':(time.sleep,1)}[dask.get(dag,i) for i in ['1','2']] 需要两秒钟,而dask.get(dag,['1','2']) 只需一秒钟。

标签: dask


【解决方案1】:

扩展 MRocklin 的答案并在问题下方的 cmets 中格式化代码。

一次计算整个图表可以按照您的预期进行。 heavy_comp 只会执行一次,这就是你想要的。考虑您在由空函数定义完成的 cmets 中提供的以下代码:

def load(fn):
    print('load')
    return fn

def sc_1(i):
    print('sc_1')
    return i

def sc_2(i):
    print('sc_2')
    return i

def sc_3(i):
    print('sc_3')
    return i

def heavy_comp(i):
    print('heavy_comp')
    return i

def merge(*args):
    print('merge')
    return args

dag = {'load': (load, 'myfile.txt'), 'heavy_comp': (heavy_comp, 'load'), 'simple_comp_1': (sc_1, 'heavy_comp'), 'simple_comp_2': (sc_2, 'heavy_comp'), 'simple_comp_3': (sc_3, 'heavy_comp'), 'merger_comp': (merge, 'sc_1', 'sc_2', 'sc_3')}

import dask
result = dask.get(dag, 'merger_comp')
print('result:', result)

它输出:

load
heavy_comp
sc_1
sc_2
sc_3
merge
result: ('sc_1', 'sc_2', 'sc_3')

如您所见,“heavy_comp”只打印了一次,表明函数heavy_comp只执行了一次。

【讨论】:

  • 感谢@malbert,已验证!
【解决方案2】:

核心 Dask 库中的机会缓存仅适用于单机调度器,不适用于分布式调度器。

但是,如果您一次只计算整个图形,Dask 将智能地保留中间值。如果您想保留某些值,也可以查看persist 函数。

【讨论】:

  • 如果我错了请纠正我,但persist 不只适用于数组/数据帧等 Numpy/Dask 数据结构吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多