如何解决并行化第n个阶乘python程序的更好方法?
我有这个python代码来连续计算n个“ 1”的第n个阶乘。我已经能够很好地对其进行优化,包括使用多处理模块将其调整为在所有内核上运行。但是我注意到第7个进程(因为从上到下,这是值的下限)明显快于其余线程。线程0-6平均耗时32秒,n = 11,而线程7仅耗时12秒。我希望数字本身越大,差异就越大,但我不希望有如此明显的差异。
我的代码中是否存在除了导致这堵大墙的计算之外的其他遗漏?我已经验证了输出,并且每个段的长度几乎相同(线程7经过几十次计算而稍长一些,但是从总体上看,这没什么,线程7还是运行最短的)
是否有更好的方法来并行化此方法以提高效率?使线程的增量不相同会有所帮助吗?
编辑:添加python版本信息
Win32上的Python 3.8.5(tags / v3.8.5:580fbb0,2020年7月20日,15:57:54)[MSC v.1924 64位(AMD64)]
(我做了25次n = 11的测试,所有测试都与此类似)
import multiprocessing
import argparse
from datetime import datetime
from math import log10
parser = argparse.ArgumentParser(
formatter_class=argparse.HelpFormatter,description="Calcs n factorial",usage=""
)
parser.add_argument("-n","--number",type=int,default=2)
args = parser.parse_args()
def getlog(send_end,i,threads,num,n,inc):
begin = datetime.now()
start = num-inc*i
end = num-inc*(i+1) if i < threads-1 else 0
output = sum(map(log10,range(start,end,-n)))
send_end.send(output)
final = datetime.now()
duration = final-begin
print("{},{},{}".format(i,duration,start,end))
def main():
n = args.number
num = int('1'*n)
threads = multiprocessing.cpu_count() if num/multiprocessing.cpu_count() > multiprocessing.cpu_count() else 1
inc = int(num/threads)
inc -= inc%n
jobs = []
pipe_list = []
for i in range(threads):
recv_end,send_end = multiprocessing.Pipe(False)
p = multiprocessing.Process(target=getlog,args=(send_end,inc))
jobs.append(p)
pipe_list.append(recv_end)
p.start()
for proc in jobs:
proc.join()
e = sum([output.recv() for output in pipe_list])
print('%.2fe%d' % (10**(e % 1),e // 1))
if __name__ == '__main__':
start = datetime.now()
main()
end = datetime.now()
print(end-start)
解决方法
@echo off
pushd "D:\Jaye\Programming\Web\Project\scss"
for /f "delims=" %%i in ('dir /b /s /a-d *.scss ^| findstr /V /IRC:"^_"') do echo Non of these starts with "_": "%%~i"
popd
如果需要使用超出C range
范围之外的值,请使用较慢的实现-请参见source。
您在Windows上,其中C long
是32位(即使在64位Python版本上)。进程7是唯一将范围元素放在C long
范围内的程序。
以不同的幅度迭代一百万个数字的时间:
from timeit import repeat
from collections import deque
for e in range(26,36):
n = 2**e
t = min(repeat(lambda: deque(range(n,n+10**6),0),number=1))
print(e,t)
在64位Windows上的32位Python上为我提供的输出,请注意,从2 30 增至2 31 时的巨大增长:
26 0.020830399999999916
27 0.020713199999999987
28 0.02067260000000004
29 0.021565000000000056
30 0.021966000000000152
31 0.16404839999999998
32 0.16630840000000013
33 0.16394810000000026
34 0.16302989999999973
35 0.1655395999999998
在范围上映射log10
仍显示大约相同(绝对)的增加:
26 0.14502039999999994
27 0.1435571
28 0.14378349999999962
29 0.14398270000000002
30 0.14687919999999988
31 0.29700239999999933
32 0.29499730000000035
33 0.2949491999999996
34 0.2964432000000006
35 0.2918921999999995
代码:
from timeit import repeat
from collections import deque
from math import log10
for e in range(26,36):
n = 2**e
t = min(repeat(lambda: deque(map(log10,range(n,n+10**6)),t)
您在线程7中的数字都是快幅度,而其他线程中的大多数/所有数字都是慢幅度。
您可以更改范围,以使它们都经过所有幅度。更简单的示例:使用范围 range(0,10)
和range(10,20)
代替范围range(0,20,2)
和range(1,2)
。
顺便说一句,当从2 30 变为2 31 时,我看到64位Windows上的64位Python也有类似的增长。但是在Linux上的64位Python上,从2 30 到2 31 并没有增加,但从2 62 62>时也有类似的显着增加。 sup>到2 63 。
更新:
以上删除线段落不正确。正如所示,并不是“数字”很慢(我曾想过),而是有两个完全独立的range
实现。而且只有您的线程7使用快速线程(少量线程)。因此,我上面提出的使所有线程/范围遍历所有幅度的建议实际上会适得其反。它不会使速度较慢的速度更快,而只会使速度较慢的速度和其他速度一样慢。闷闷不乐。
因此,另一种建议是:不要像给每个线程一个范围那样,而是给每个线程一个long
范围的一部分和一个非long
范围的一部分。那应该使所有线程都同样快,并减少整体时间。但是效果会很小,对于较大的n甚至会更小,我怀疑是否值得这样做。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。