多处理组合会莫名其妙地减慢速度,这取决于随机输入数据的创建方式和重复性

2024-09-28 20:52:55 发布

您现在位置:Python中文网/ 问答频道 /正文

我在Python2.7中编写了代码来创建独特元素列表的独特组合,也使用了多重处理

输入将是包含唯一元素的列表列表,例如:

[['z', 'u', 'w', 'f'], ['k', 'z', 'u', 'o', 'w'], ['z', 'u', 'w', 'f'], 
 ['a', 'k', 'u', 'w', 'x', 'z'], ['e', 'f', 'i', 'k', 'u', 'x', 'z'], ['u', 'f']]

第一个子列表的输出如下所示:

['f_u', 'f_w', 'f_z', 'u_w', 'u_z', 'w_z']

这是我完整代码的简化部分(请忽略LA0),足以说明问题:

import multiprocessing as mpr
import time
import numpy.random as nr
import itertools as it

def combineSets(L0):
    LT = list(it.combinations(sorted(L0),2))
    return ['_'.join(LT[i]) for i in range(len(LT))]

def multiproc(L0,nproc):
    pool = mpr.Pool(processes=nproc)
    LLX = pool.map(combineSets,L0)
    pool.close()
    pool.join()
    return LLX

def main(L0,nproc=1):
    time0 = time.time()
    if nproc == 1:
        LX = []
        for ll in L0:
            LX.append(combineSets(ll))
    else:
        LX = multiproc(L0,nproc)
    print "Combining took "+str(time.time()-time0)+" s"
    return LX

def sampleData(lenSheet,lenList):
    nr.seed()
    choices = list('aeioukzwxfqpgbv')
    pChoices = [0.02,0.03,0.02,0.03,0.2,0.075,0.125,0.15,0.15,0.1,0.02,0.02,0.02,0.02,0.02]
    L0 = [list(set([nr.choice(choices,p=pChoices) for _ in range(nr.randint(2,high=lenSheet+1))])) for _ in range(lenList)]
    L0 = (1000000/lenList)*L0
    print "lenList: "+str(lenList)
    return L0

if __name__ == "__main__":
    lenSheet = 15
    lenList = 100
    L0 = sampleData(lenSheet,lenList)
    nproc = 1
    print "Cores: "+str(nproc)
    res = main(L0,nproc)
    nproc = 10
    print "Cores: "+str(nproc)
    res = main(L0,nproc)
  • combineSets()创建组合,而不是我实际使用的版本,但是对于本例,它会产生相同的结果
  • multiproc()处理所有的多重处理
  • main()当然是主程序
  • sampleData()创建随机测试数据的大型数据集

现在让我们看看sampleData()

参数lenSheet是从choices中选择元素的子列表的最大长度

变量lenList是随机创建的唯一列表的长度

代码L0 = (1000000/lenList)*L0然后确保总是有1000000个子列表。例如,如果我只创建100个唯一的,那么它们只会重复10000次,因此lenList值较低的数据将具有高度重复性

这里我使用numpy.random.choice(),因为它不做唯一的选择,所以我也在结果上使用set()。这改变了随机数据,虽然总体上减少了子列表的数量,增加了元素的数量,并且创建了长度为1的子列表,这些子列表是无用的,但是我不想一开始就用相同的概率来选择元素

然后我尝试了另一种更简单的方法来创建随机数据:

import random as rd
L0 = [rd.sample(choices,nr.randint(2,high=lenSheet+1)) for _ in range(lenList)]

如果lenList足够大,则此代码生成的数据长度从2到lenSheet平均分布

现在请记住,子列表越长,计算所有可能的组合所需的时间就越长,而且时间随着数字的增加而显著增加

因此,我希望算法能够更快地处理使用numpy.random.choice()创建的数据,因为它具有更少的子列表和更高的长度

数据是完全随机的,没有使用固定的种子

实际情况如下:

让我们先看看使用random.sample()进行的计算:

我将lenList从100增加到1000000,这意味着我将从高度重复的数据变成完全唯一的数据集,并对1个和10个核分别进行计算。我使用的是打印输出,虽然我知道它不如timeit准确,但这并不重要:

lenList: 100
Cores: 1
Combining took 11.5520000458 s
Cores: 10
Combining took 17.3930001259 s

lenList: 10000
Cores: 1
Combining took 12.1019999981 s
Cores: 10
Combining took 18.8040001392 s

lenList: 1000000
Cores: 1
Combining took 11.5900001526 s
Cores: 10
Combining took 18.5590000153 s

来自multiprocessing的开销很大,但是单处理和多处理之间的比率是稳定的,不依赖于lenList,也就是说数据的重复程度

现在使用numpy.random.choice()-随机数据的随机数据:

lenList: 100
Cores: 1
Combining took 5.53099989891 s
Cores: 10
Combining took 8.14699983597 s

lenList: 10000
Cores: 1
Combining took 5.32299995422 s
Cores: 10
Combining took 9.90599989891 s

lenList: 20000
Cores: 1
Combining took 5.92899990082 s
Cores: 10
Combining took 14.0799999237 s

lenList: 50000
Cores: 1
Combining took 5.69400000572 s
Cores: 10
Combining took 28.9730000496 s

lenList: 100000
Cores: 1
Combining took 5.88800001144 s
Cores: 10
Combining took 50.2009999752 s

lenList: 1000000
Cores: 1
Combining took 5.75199985504 s
Cores: 10
Combining took 50.7860000134 s

正如预期的那样,单个数据集的处理速度比其他数据集快,因为数据具有较少的大长度子列表

然而,multiprocessing的计算时间在10000到100000之间大幅增加,然后保持在单次处理的10倍左右。这表明了对数据重复性降低的敏感性

处理十分之一数据的十个进程的时间是处理所有数据的单个进程的十倍

实际上,这两种情况之间的唯一区别是随机数据的创建方式,也就是子列表长度的分布,这种区别是这样的,对于numpy.random.choice()-数据,它应该工作得更快,只不过对于随机数据具有较高的唯一性和较少的重复性的情况下进行多次处理。假设单个进程的时间保持不变,数据的重复性对性能没有影响

我真的不知道发生了什么,有人能告诉我是什么导致了这一切吗

我的代码中有一些非常奇怪的bug吗

多重处理对不同的处理不同的工作量

编辑1:简化了代码,结果基本相同

编辑2:我用2个核心而不是10个核心运行了最后一个案例,时间是54.34 s,所以核心的数量似乎并不重要

编辑3:更多结果,在简化代码后,我尝试使用2个核心而不是10个核心进行相同的计算,问题仍然是一样的:

使用random.sample()数据计算:

lenList: 100
Cores: 1
Combining took 10.8669998646 s
Cores: 2
Combining took 17.8090000153 s

lenList: 10000
Cores: 1
Combining took 10.9370000362 s
Cores: 2
Combining took 18.8830001354 s

lenList: 1000000
Cores: 1
Combining took 11.3680000305 s
Cores: 2
Combining took 19.1840000153 s

使用numpy.random.choice()数据计算:

lenList: 100
Cores: 1
Combining took 5.1210000515 s
Cores: 2
Combining took 11.4690001011 s

lenList: 10000
Cores: 1
Combining took 5.06900000572 s
Cores: 2
Combining took 13.2760000229 s

lenList: 1000000
Cores: 1
Combining took 4.63499999046 s
Cores: 2
Combining took 54.754999876 s

编辑4:

我使用cProfilenumpy.random.choice()运行来分析multiproc()函数

主要区别似乎在于以下几行

对于lenList = 100

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
35   11.386    0.325   11.386    0.325 {method 'acquire' of 'thread.lock' objects}

对于lenList = 1000000

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
35   49.839    1.424   49.839    1.424 {method 'acquire' of 'thread.lock' objects}

我对锁不太了解,所以我现在可能得好好研究一下。也许有人能给我一些启示


Tags: 数据代码numpy元素列表时间randomcores