2

有没有一种舒适的方法可以在 Julia 中以某种方式获取 map/pmap 的“状态”?

如果我有一个数组 a = [1:10] 我想:

1:枚举数组并使用if-conditional添加打印命令

((index,value) -> 5*value ......, enumerate(a)

并且“.......”在哪里,有一种方法可以将匿名函数“链接”到类似的东西

"5*value and then print index/length(a) if index%200 == 0"

2:知道是否已经有一个现有的选项,因为 pmap 用于并行任务,这些任务通常用于大型进程,所以它是否已经存在?

此外,有没有办法让匿名函数一个接一个地做两个“独立”的事情?

例子

如果我有

a = [1:1000]
function f(n) #something that takes a huge ammount of time
end 

我执行

map(x -> f(x), a)

REPL 会打印出状态

"0.1 completed"
.
.
.
"0.9 completed"

解决方案

Chris Rackauckas 回答

ProgressMeter 包默认不包含这个有点奇怪

Pkg.add("ProgressMeter")
Pkg.clone("https://github.com/slundberg/PmapProgressMeter.jl")
@everywhere using ProgressMeter
@everywhere using PmapProgressmeter
pmap(x->begin sleep(1); x end, Progress(10), 1:10)

github上的PmapProgressMeter

4

4 回答 4

3

ProgressMeter.jl有一个 pmap 分支。

您还可以使 Juno 进度条在 pmap 内工作。这是一种使用未记录的东西,所以你应该在Gitter中询问你是否需要更多信息,因为如果/当它发生变化时,发布这个公众只会让人们感到困惑。

于 2016-08-04T14:01:13.420 回答
1

为什么不把它包含在你的函数定义中来打印这些信息呢?例如

function f(n) #something that takes a huge amount of time
    ...
    do stuff.
    ...
    println("completed $n")
end 

而且,如果需要,您可以在函数中添加一个额外的参数,该参数将包含0.1, ... ,0.9在您的示例中(我不太确定那些是什么,但无论它们是什么,它们都可以只是一个参数在您的功能中)。

如果你看一下下面的例子pmap@parallel你会发现一个提供给pmap打印输出的函数的例子。

另请参阅thisthis SO post on info,以将多个参数提供给与mapand一起使用的函数pmap



Julia文档建议

pmap() 是为每个函数调用执行大量工作的情况而设计的。相比之下,@parallel for 可以处理每次迭代很小的情况,也许只是将两个数字相加。

有几个原因。首先,pmap启动工人的工作会产生更高的启动成本。因此,如果工作非常小,这些启动成本可能会变得低效。然而,相反,pmap在工人之间分配工作方面做得“更聪明”。特别是,它构建了一个作业队列,并在每个工人可用时向每个工人发送一个新作业。 @parallel相比之下,当它被调用时,它会在工人之间分配所有要完成的工作。因此,如果某些员工的工作时间比其他员工更长,您最终可能会遇到这样一种情况,即您的大多数员工都已完成工作并处于空闲状态,而少数员工则长时间保持活动状态,完成工作。然而,这种情况不太可能发生在非常小而简单的工作中。

下面说明了这一点:假设我们有两个工人,一个很慢,另一个快两倍。理想情况下,我们希望快速工作人员的工作量是慢工作人员的两倍。(或者,我们可以有快速和慢速的工作,但校长是完全相同的)。 pmap会做到这一点,但@parallel不会。

对于每个测试,我们初始化以下内容:

addprocs(2)

@everywhere begin
    function parallel_func(idx)
        workernum = myid() - 1 
        sleep(workernum)
        println("job $idx")
    end
end

现在,对于@parallel测试,我们运行以下命令:

@parallel for idx = 1:12
    parallel_func(idx)
end

并返回打印输出:

julia>     From worker 2:    job 1
    From worker 3:    job 7
    From worker 2:    job 2
    From worker 2:    job 3
    From worker 3:    job 8
    From worker 2:    job 4
    From worker 2:    job 5
    From worker 3:    job 9
    From worker 2:    job 6
    From worker 3:    job 10
    From worker 3:    job 11
    From worker 3:    job 12

这几乎是甜的。工人们平均地“分担”了工作。请注意,每个工人完成了 6 份工作,尽管工人 2 的速度是工人 3 的两倍。这可能很感人,但效率低下。

对于pmap测试,我运行以下命令:

pmap(parallel_func, 1:12)

并获得输出:

From worker 2:    job 1
From worker 3:    job 2
From worker 2:    job 3
From worker 2:    job 5
From worker 3:    job 4
From worker 2:    job 6
From worker 2:    job 8
From worker 3:    job 7
From worker 2:    job 9
From worker 2:    job 11
From worker 3:    job 10
From worker 2:    job 12

现在,请注意,工人 2 已经完成了 8 个工作,而工人 3 已经完成了 4 个工作。这与他们的速度完全成正比,也是我们想要获得最佳效率的结果。 pmap是一个艰巨的任务高手 - 从每个人的能力。

于 2016-08-04T13:28:06.080 回答
1

您可以按照您的要求通过实现“闭包”来创建具有“状态”的函数。例如

julia> F = function ()
  ClosedVar = 5
  return (x) -> x + ClosedVar
end;
julia> f = F();
julia> f(5)
10
julia> ClosedVar = 1000;
julia> f(5)
10

正如您所看到的,该函数f维护“状态”(即内部变量ClosedVar是本地的F,并且f即使它F本身在技术上早已超出范围,也保持对它的访问。

注意与正常的非封闭函数定义的区别:

julia> MyVar = 5;
julia> g(x) = 5 + MyVar;
julia> g(5)
10
julia> MyVar = 1000;
julia> g(5)
1005

您可以创建自己的闭包,在运行时询问/更新其封闭变量,并根据其状态每次执行不同的操作。

话虽如此,从您的示例来看,您似乎期望pmap它将按顺序运行。这不能保证。所以不要依赖“哪个索引是这个线程处理”的方法来打印每 200 次操作。您可能必须在闭包内维护一个封闭的“计数器”变量,并依赖它。这大概也意味着您的关闭需要可访问@everywhere

于 2016-08-04T14:00:38.757 回答
0

另一种可能性是使用 aSharedArray作为工人之间共享的计数器。例如

addprocs(2)

Counter = convert(SharedArray, zeros(Int64, nworkers()))

## Make sure each worker has the SharedArray declared on it, so that it need not be fed as an explicit argument
function sendto(p::Int; args...)
  for (nm, val) in args
    @spawnat(p, eval(Main, Expr(:(=), nm, val)))
  end
end

for (idx, pid) in enumerate(workers())
  sendto(pid, Counter = Counter)
end
@everywhere global Counter


@everywhere begin
    function do_stuff(n)
        sleep(rand())
        Counter[(myid()-1)] += 1
        TotalJobs = sum(Counter)
        println("Jobs Completed = $TotalJobs")
    end
end

pmap(do_stuff, 1:10)
于 2016-08-04T15:14:09.227 回答