multiprocessing python_使用Python Multiprocessing从令人尴尬的并行任务中获得预期的加速...

我想你想以不同的方式划分工作.

尽管您的程序在核心之间均匀地划分候选整数的范围,但每个范围内的工作可能不均匀.这意味着一些核心提前完成,无所事事,而其他核心仍在运行.这会快速地失去并行效率.

为了说明问题,想象一下你有1000个核心.第一个核心看到非常小的候选数字并且不需要很长时间来考虑它们,然后空闲.最后一个(千分之一)核心只看到非常大的候选数字,并且需要更长的时间来考虑它们.所以它运行,而第一个核心闲置.浪费了周期.同样适用于4个核心.

当交付给核心的工作量未知时,您想要做的是将所有核心分配给许多适度大小的块,比核心块多得多.然后核心可以以不均匀的速率完成,并且每个核心返回以找到更多工作要做.这本质上是一种工作列表算法.你最终会以不均匀的方式结束,但它只是在小块上,所以不会浪费太多.

我不是Python程序员,所以我用Parlanse编写了一个解决方案.

(includeunique `Console.par')

(includeunique `Timer.par')

(define upper_limit 10000000)

(define candidates_per_segment 10)

(define candidates_per_segment2 (constant (* candidates_per_segment 2)))

(= [prime_count natural] 0)

[prime_finding_team team]

(define primes_in_segment

(action (procedure [lower natural] [upper natural])

(;;

(do [candidate natural] lower upper 2

(block test_primality

(local (= [divisor natural] 3)

(;;

(while (< (* divisor divisor) candidate)

(ifthenelse (== (modulo candidate divisor) 0)

(exitblock test_primality)

(+= divisor 2)

)ifthenelse

)while

(ifthen (~= (* divisor divisor) candidate)

(consume (atomic+= prime_count))

)ifthen

);;

)local

)block

)do

);;

)action

)define

(define main

(action (procedure void)

(local [timer Timer:Timer]

(;;

(Console:Put (. `Number of primes found: '))

(Timer:Reset (. timer))

(do [i natural] 1 upper_limit candidates_per_segment2

(consume (draft prime_finding_team primes_in_segment

`lower':i

`upper':(minimum upper_limit (- (+ i candidates_per_segment2) 2))))

)do

(consume (wait (@ (event prime_finding_team))))

(Timer:Stop (. timer))

(Console:PutNatural prime_count)

(Console:PutNewline)

(Timer:PrintElapsedTime (. timer) (. `Parallel computed in '))

(Console:PutNewline)

);;

)local

)action

)define

Parlanse看起来像LISP,但工作和编译更像C.

工人是primes_in_segment;它需要一系列由其参数lower和upper定义的候选值.它尝试该范围内的每个候选者,并且如果该候选者是素数,则递增(原子地)总prime_count.

全范围被do分成小范围的范围(奇数序列)

在主要循环.并行性发生在draft命令上,它创建了一个并行执行计算粒度(不是Windows线程)并将其添加到prime_finding_team,这是一组代表所有素数因子的工作. (团队的目的是允许所有这些工作作为一个单元进行管理,例如,在必要时销毁,在此程序中不需要). draft的参数是由分叉粒度及其参数运行的函数.这项工作是由Parlanse管理的一组(Windows)线程使用工作窃取算法完成的.如果有太多的工作,Parlanse会限制产生工作的粒子,并将其能量用于处理纯粹计算的粒子.

一个人只能将一个候选值传递给每个粒子,但是每个候选者的分叉开销会更多,总运行时间会相应变差.我们根据经验选择了10个,以确保每个候选人的叉开销很小;将每个段的候选人设置为1000并不会增加额外的加速.

do循环只是尽可能快地制造工作.当有足够的并行性有用时,Parlanse会限制草案步骤.等待团队活动,导致主程序等待所有团队成员完成.

我们在HP六核AMD Phenom II X6 1090T 3.2 GHz上进行了此操作.

执行运行如下;首先为1 CPU:

>run -p1 -v ..\teamprimes

PARLANSE RTS: Version 19.1.53

# Processors = 1

Number of primes found: 664579

Parallel computed in 13.443294 seconds

---- PARLANSE RTS: Performance Statistics

Duration = 13.527557 seconds.

CPU Time Statistics:

Kernel CPU Time: 0.031s

User CPU Time: 13.432s

Memory Statistics:

Peak Memory Usage : 4.02 MBytes

Steals: 0 Attempts: 0 Success rate: 0.0% Work Rediscovered: 0

Exiting with final status 0.

然后是6个CPU(很好地扩展):

>run -p6 -v ..\teamprimes

PARLANSE RTS: Version 19.1.53

# Processors = 6

Number of primes found: 664579

Parallel computed in 2.443123 seconds

---- PARLANSE RTS: Performance Statistics

Duration = 2.538972 seconds.

CPU Time Statistics:

Kernel CPU Time: 0.000s

User CPU Time: 14.102s

Total CPU Time: 14.102s

Memory Statistics:

Peak Memory Usage : 4.28 MBytes

Steals: 459817 Attempts: 487334 Success rate: 94.4% Work Rediscovered: 153

您注意到并行版本的总CPU时间与串行版本大致相同;这是因为他们正在做同样的工作.

鉴于Python的“fork”和“join”操作,我确信有一个可以轻松编写的Python等价物.它可能会耗尽空间或线程,因为同时可能有太多的叉子. (由于candidate_per_segment为10,Parlanse下有多达100万粒活粒).这就是自动限制工作生成是一件好事.作为替代,您可以将candidates_per_segment设置为更大的数字,例如10000,这意味着您只能获得1000个最坏情况的线程. (我认为由于Python的解释性质,你仍然会付出高昂的代价).当您将每个段的候选项设置得越来越接近1e7 / 4时,您将接近使用当前Python代码的确切行为.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值