在python中,如何将itertools.product的lage结果分成组并并行迭代

时间:2016-06-07 08:44:40

标签: python python-3.x parallel-processing itertools

在python中,我使用itertools.product来迭代产生非常大的结果的字符列表的所有可能组合。 但是,当我查看Windows 10任务管理器时,执行此任务的python进程仅占用13.5%的CPU。我在python中研究了多处理,并发现使用pool.map我可以将一个函数的实例映射到池,并且有多个函数实例并行运行。这很好,但是当我在一个(非常大的)列表上进行迭代时,这是在占用大量时间的函数的一个实例中完成的,这对我没有帮助。

所以我认为加速这种方式的唯一方法是将itertools.product的结果分成组并并行地迭代组。如果我可以获得结果的长度itertools.product,我可以根据我可用的处理器核心数将它分成组,然后使用多处理我可以并行迭代所有这些组。

所以我的问题是可以做到这一点,最好的方法是什么?

也许这里存在一个模块?

这个概念是这样的。 (以下实际上有效但在我尝试将其扩展到注释掉的完整字符集时会产生MemoryError)

#!/usr/bin/env python3.5
import sys, itertools, multiprocessing, functools

def process_group(iIterationNumber, iGroupSize, sCharacters, iCombinationLength, iCombintationsListLength, iTotalIterations):
    iStartIndex = 0
    if iIterationNumber > 1: iStartIndex = (iIterationNumber - 1) * iGroupSize
    iStopIndex = iGroupSize * iIterationNumber
    if iIterationNumber == iTotalIterations: iStopIndex = iCombintationsListLength
    aCombinations = itertools.product(sCharacters, repeat=iCombinationLength)
    lstCombinations = list(aCombinations)
    print("Iteration#", iIterationNumber, "StartIndex:", iStartIndex, iStopIndex)
    for iIndex in range(iStartIndex, iStopIndex):
        aCombination = lstCombinations[iIndex];
        print("Iteration#", iIterationNumber, ''.join(aCombination))

if __name__ == '__main__':
    #_sCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()_-+={[}]|\"""':;?/>.<,"
    _sCharacters = "123"
    _iCombinationLength = 4
    aCombinations = itertools.product(_sCharacters, repeat=_iCombinationLength)
    lstCombinations = list(aCombinations)
    _iCombintationsListLength = len(lstCombinations)
    iCPUCores = 4
    _iGroupSize = round(_iCombintationsListLength / iCPUCores)
    print("Length", _iCombintationsListLength)
    pool = multiprocessing.Pool()
    pool.map(functools.partial(process_group, iGroupSize = _iGroupSize, sCharacters = _sCharacters, iCombinationLength = _iCombinationLength, iCombintationsListLength = _iCombintationsListLength, iTotalIterations = iCPUCores), range(1,iCPUCores+1))

感谢您的时间。

1 个答案:

答案 0 :(得分:0)

您不能在子流程之间共享product()输出;没有好的方法可以将每个进程分解成块。相反,让每个子进程生成新值,但为它们提供一个前缀来开始。

product()调用中删除外部循环并从中创建组。例如,您可以通过将len(sCharacters)减少一个并将iCombinationLength中的每个元素作为前缀传递来创建sCharacters组:

for prefix in sCharacters:
    # create group for iCombinationLength - 1 results.
    # pass in the prefix

然后,每个组都可以循环遍历product(sCharacters, repeat=iCombinationLength - 1)并将其与前缀相结合。因此,组1以'0'开头,组2以'1'开头,等等。

您可以使用2个或3个或更多字符的组合来扩展它。对于10个输入字符,分别创建100或1000个组。通用版本是:

prefix_length = 3
for prefix in product(sCharacters, repeat=prefix_length):
    # create group for iCombinationLength - prefix_length
    # pass in the prefix