如何以及何时在Julia中使用@async和@sync

迈克尔·奥罗格(Michael Ohlrogge):

我已经阅读了文档但仍然无法弄清楚如何以及何时使用它们,也无法在Internet上的其他地方找到许多关于它们的资源或示例。@async@sync

我的近期目标是找到一种方法,让多个工作人员并行进行工作,然后等到他们全部完成我的代码后再进行工作。这篇文章:等待在Julia中的远程处理器上完成任务包含一种成功的方法。我曾以为可以使用@async@sync宏,但最初的失败使我想知道自己是否正确理解了如何以及何时使用这些宏。

迈克尔·奥罗格(Michael Ohlrogge):

根据下的文档?@async,“ @async将表达式包装在Task中”。这意味着,对于处于其范围内的任何内容,Julia都会开始运行此任务,然后继续执行脚本中的后续操作,而无需等待任务完成。因此,例如,没有宏,您将得到:

julia> @time sleep(2)
  2.005766 seconds (13 allocations: 624 bytes)

但是使用宏,您将获得:

julia> @time @async sleep(2)
  0.000021 seconds (7 allocations: 657 bytes)
Task (waiting) @0x0000000112a65ba0

julia> 

因此,Julia允许脚本继续执行(并使@time宏完全执行),而无需等待任务(在本例中为休眠状态,等待两秒钟)完成。

@sync宏,相比之下,将“等到所有动态封闭使用@async@spawn@spawnat@parallel是完整的。” (根据的文档?@sync)。因此,我们看到:

julia> @time @sync @async sleep(2)
  2.002899 seconds (47 allocations: 2.986 KB)
Task (done) @0x0000000112bd2e00

那么,在这个简单的示例中,没有必要将@asyncand 的单个实例包括在内@sync但是,@sync有用的地方@async是您希望将多个操作应用于所有操作,这些操作可以一次全部开始,而不必等待每个操作完成。

例如,假设我们有多个工作人员,并且我们想开始让他们每个人同时处理一个任务,然后从这些任务中获取结果。最初(但不正确)的尝试可能是:

using Distributed
cell(N) = Vector{Any}(undef, N)

addprocs(2)
@time begin
    a = cell(nworkers())
    for (idx, pid) in enumerate(workers())
        a[idx] = remotecall_fetch(sleep, pid, 2)
    end
end
## 4.011576 seconds (177 allocations: 9.734 KB)

这里的问题是循环remotecall_fetch()在继续开始下一个remotecall_fetch()操作之前等待每个操作完成,即等待每个进程完成其工作(在这种情况下为2秒)在实际情况中,由于我们的流程无法同时完成其工作(即睡眠),因此我们在这里没有获得并行的好处。

但是,我们可以使用@async@sync的组合来纠正此问题

@time begin
    a = cell(nworkers())
    @sync for (idx, pid) in enumerate(workers())
        @async a[idx] = remotecall_fetch(sleep, pid, 2)
    end
end
## 2.009416 seconds (274 allocations: 25.592 KB)

现在,如果将循环的每个步骤都算作一个单独的操作,我们会看到在@async之前有两个单独的操作宏允许它们中的每一个启动,并且代码在每个完成之前继续(在这种情况下,进入循环的下一步)。但是,使用@sync范围涵盖整个循环宏意味着在@async完成所有前面的操作之前,我们不允许脚本继续执行该循环

通过进一步调整上面的示例以查看在某些修改下它如何变化,可以对这些宏的操作有一个更清晰的了解。例如,假设我们只有@async不带的@sync

@time begin
    a = cell(nworkers())
    for (idx, pid) in enumerate(workers())
        println("sending work to $pid")
        @async a[idx] = remotecall_fetch(sleep, pid, 2)
    end
end
## 0.001429 seconds (27 allocations: 2.234 KB)

在这里,@async宏允许我们甚至在每个remotecall_fetch()操作完成执行之前就继续循环但是,无论好坏,我们都没有@sync宏来阻止代码继续经过此循环,直到所有remotecall_fetch()操作完成为止

尽管如此,remotecall_fetch()即使我们继续进行,每个操作仍在并行运行。我们可以看到,因为如果我们等待两秒钟,那么包含结果的数组a将包含:

sleep(2)
julia> a
2-element Array{Any,1}:
 nothing
 nothing

(“ nothing”元素是成功获取sleep函数的结果的结果,该函数不返回任何值)

我们还可以看到这两个remotecall_fetch()操作基本上在同一时间开始,因为在它们之前的打印命令也快速连续执行(这些命令的输出未在此处显示)。与下面的示例相反,在下一个示例中,打印命令彼此之间的延迟为2秒:

如果我们将@async宏放在整个循环中(而不是仅仅放在其内部),那么我们的脚本将再次立即继续remotecall_fetch()运行,而无需等待操作完成。但是,现在,我们只允许脚本从整体上继续循环。我们不允许循环的每个步骤在上一个步骤结束之前开始。因此,与上面的示例不同,脚本在循环后继续执行两秒钟后,结果数组仍具有一个元素,如#undef,表明第二项remotecall_fetch()操作尚未完成。

@time begin
    a = cell(nworkers())
    @async for (idx, pid) in enumerate(workers())
        println("sending work to $pid")
        a[idx] = remotecall_fetch(sleep, pid, 2)
    end
end
# 0.001279 seconds (328 allocations: 21.354 KB)
# Task (waiting) @0x0000000115ec9120
## This also allows us to continue to

sleep(2)

a
2-element Array{Any,1}:
    nothing
 #undef    

而且,毫不奇怪,如果我们将@sync@async紧挨着,我们会发现每个remotecall_fetch()依次运行(而不是同时运行),但是直到每个完成为止我们都不会继续执行代码。换句话说,我认为这基本上等同于如果我们都没有宏,就像sleep(2)行为与@sync @async sleep(2)

@time begin
    a = cell(nworkers())
    @sync @async for (idx, pid) in enumerate(workers())
        a[idx] = remotecall_fetch(sleep, pid, 2)
    end
end
# 4.019500 seconds (4.20 k allocations: 216.964 KB)
# Task (done) @0x0000000115e52a10

还要注意,在@async范围内可能有更复杂的操作文档提供了一个示例,其中包含范围为的整个循环@async

更新:回想一下,对于同步宏国的帮助下,它会“等到所有动态封闭使用@async@spawn@spawnat@parallel。完成” 就算是“完成”而言,如何在@sync@async范围内定义任务很重要考虑下面的示例,它与上面给出的示例之一略有不同:

@time begin
    a = cell(nworkers())
    @sync for (idx, pid) in enumerate(workers())
        @async a[idx] = remotecall(sleep, pid, 2)
    end
end
## 0.172479 seconds (93.42 k allocations: 3.900 MB)

julia> a
2-element Array{Any,1}:
 RemoteRef{Channel{Any}}(2,1,3)
 RemoteRef{Channel{Any}}(3,1,4)

前面的示例大约花费了2秒钟的时间来执行,这表明这两个任务是并行运行的,并且脚本在继续执行之前等待每个任务完成其功能。但是,此示例的评估时间要短得多。原因是,就其目的而言,一旦将工人发送给工作@syncremotecall()操作就“完成”了。(请注意,结果数组a在这里仅包含RemoteRef对象类型,这仅表明特定进程正在发生某些事情,理论上可以在将来的某个时间获取)。相比之下,该remotecall_fetch()操作仅在从工作人员处收到其任务已完成的消息时才“完成”。

因此,如果您正在寻找方法来确保对工作人员的某些操作在脚本中继续进行之前已经完成(例如本博文中所讨论的:等待在Julia中的远程处理器上完成任务),则有必要仔细考虑什么才算是“完整”,以及如何在脚本中对其进行衡量和实施。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何以及何时在Akka中使用ActorIdentity

如何以及何时在应用程序中使用片段?

如何以及何时使用“异步”和“等待”

如何以及何时使用刷新令牌?

如何以及何时使用State functor和State应用程序?

Tk(),Toplevel()和winfo_toplevel()。它们之间的区别以及如何以及何时有效使用?

如何以及何时在Python中适当使用weakref

如何以及何时使用createForm vs createFormBuilder

ArrayList <String> vs String [],如何以及何时使用?

如何以及何时使用通过AcquisitionUnstableContentProviderClient获得的ContentProviderClient?

我将如何以及何时使用Rake文件?

如何以及何时使用Q_DECLARE_METATYPE

如何以及何时使用getline函数执行计算?

有人可以给我解释一下如何以及何时在oracle中使用syscursor吗?

我很困惑。如何以及何时使用 Object 作为参数和参数在 Java OOP 中有用?

kotlin 如何以及何时让运行?

“:ab”映射如何以及何时触发?

在Julia中使用@sync @async进行并行处理

如何以及何时在顶点和片段着色器中选择 highp、lowp 和 mediump?

何时以及如何在PostgreSQL中使用窗口函数?

何时以及如何在Java中使用“返回此”

何时以及如何在python中使用内置函数property()

Rscript和软件包:如何以及何时确定要加载哪些软件包?

何时以及如何使用 Laravel Factory 和 Seeding

如何以及在哪里使用“?” 和 ”!” 在斯威夫特

如何以及何时使用Google Cloud Functions中的Promise正确返回值?

如何以及何时使用/ dev / shm来提高效率?

将redux-saga与setInterval一起使用-如何以及何时产生

如何以及何时使用cmake 3.18选项Boost_NO_BOOST_CMAKE?