并行化第n个阶乘python程序的更好方法?

并行化第n个阶乘python程序的更好方法?,python,python-3.x,python-multithreading,Python,Python 3.x,Python Multithreading,我有这个python代码来计算一行中n“1”的第n个阶乘。我已经能够很好地优化它,包括使用多处理模块将其调整为在所有内核上运行。然而,我注意到第7个进程(这是值的低端,因为我从上到下)比其他线程要快得多。线程0-6平均耗时32秒,其中线程7仅耗时12秒。我本以为数字越大,差距就越大,但我不希望马上出现如此明显的差距 在我的代码中,除了导致这堵巨大的墙的计算之外,还有什么我遗漏了的吗?我已经验证了输出,每个段的长度几乎相同(线程7稍微长了几十次计算,但在总体方案中,这算不了什么,线程7是运行时间最

我有这个python代码来计算一行中n“1”的第n个阶乘。我已经能够很好地优化它,包括使用多处理模块将其调整为在所有内核上运行。然而,我注意到第7个进程(这是值的低端,因为我从上到下)比其他线程要快得多。线程0-6平均耗时32秒,其中线程7仅耗时12秒。我本以为数字越大,差距就越大,但我不希望马上出现如此明显的差距

在我的代码中,除了导致这堵巨大的墙的计算之外,还有什么我遗漏了的吗?我已经验证了输出,每个段的长度几乎相同(线程7稍微长了几十次计算,但在总体方案中,这算不了什么,线程7是运行时间最短的)

有没有更好的方法来并行处理这个问题以提高效率?使线程的增量不完全相同会有帮助吗

编辑:添加python版本信息

win32上的Python 3.8.5(tags/v3.8.5:580FBB02020年7月20日15:57:54)[MSC v.1924 64位(AMD64)]

(我做了25次n=11的测试,所有测试都与这次运行非常相似)

导入多处理
导入argparse
从日期时间导入日期时间
从数学导入日志10
parser=argparse.ArgumentParser(
formatter_class=argparse.HelpFormatter,
description=“Calcs n factorial”,
用法=“”
)
add_参数(“-n”,“--number”,type=int,default=2)
args=parser.parse_args()
def getlog(发送端,i,线程,num,n,inc):
begin=datetime.now()
start=num inc*i
如果imultiprocessing.cpu\u count()否则1
inc=int(num/线程)
inc-=inc%n
工作=[]
管道列表=[]
对于范围内的i(线程):
recv_end,send_end=multiprocessing.Pipe(False)
进程(target=getlog,args=(send_end,i,threads,num,n,inc))
jobs.append(p)
管道列表。追加(recv\u end)
p、 开始()
对于作业中的proc:
proc.join()
e=总和([output.recv()用于管道列表中的输出])
打印(“%.2fe%d%”(10**(e%1),e//1))
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
start=datetime.now()
main()
end=datetime.now()
打印(结束-开始)

以不同幅度重复一百万个数字的次数:

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上的输出,请注意从230到231时的巨大增长:

在这些范围内映射
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
代码:

线程7中的数字都是快量级,而其他线程中的大多数/所有数字都是慢量级

你可以改变你的范围,使他们都通过所有的量级。更简单的示例:使用范围
范围(0,20,2)
范围(1,20,2)
代替范围
范围(0,10)
范围(1,20,2)

顺便说一句,当64位Windows上的64位Python从230增加到231时,我看到了类似的增长。但在Linux上的64位Python上,从230增加到231时,我看不到任何增加,但从262增加到263时,也有类似的显著增加

更新:

上面删去的段落不正确。如图所示,并不是“数字”太慢(我原以为如此),而是有两种完全不同的
range
实现。只有线程7使用快速线程(用于小数字的线程)。所以我上面的建议是让所有的线程/范围都通过所有的量级,实际上会适得其反。它不会让慢的更快,它只会让快的和其他的一样慢。糟糕透了


因此,另一种建议是:不要像你那样给每个线程一个范围,而是给每个线程一个
long
-范围的一部分和一个非
long
范围的一部分。这将使所有线程速度相等,并将总时间减少一点。但是影响很小,对于较大的n,甚至更小,我怀疑这是否值得复杂化。

以不同的量级重复一百万个数字的次数:

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上的输出,请注意从230到231时的巨大增长:

在这些范围内映射
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
代码:

线程7中的数字都是快量级,而其他线程中的大多数/所有数字都是慢量级

你可以改变你的范围,使他们都通过所有的量级。更简单的示例:使用范围
范围(0,20,2)
范围(1,20,2)
代替范围
范围(0,10)
范围(1,20,2)

顺便说一句,当64位Windows上的64位Python从230增加到231时,我看到了类似的增长。但在Linux上的64位Python上,从230增加到231时,我看不到任何增加,但从262增加到263时,也有类似的显著增加

更新:

上面删去的段落不正确。如图所示,并不是“数字”太慢(我原以为如此),而是有两种完全不同的
range
实现。只有线程7使用快速线程(用于小数字的线程)。所以我上面的建议是让所有的线程/范围都通过所有的量级,实际上会适得其反。它不会让慢的更快,它只会让快的和其他的一样慢。糟糕透了

因此,另一个建议是:Inst
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)), 0), number=1))
    print(e, t)