Python:多处理代码非常慢

时间:2019-04-16 09:41:00

标签: python multiprocessing pymongo

我正在使用.8mongodb一次性(这是一次处理)提取pymongo百万条记录,并对其进行一些操作。

我的代码如下所示。

    proc = []
    for rec in cursor: # cursor has .8 million rows 
            print cnt
            cnt = cnt + 1
            url =  rec['urlk']
            mkptid = rec['mkptid']
            cii = rec['cii']

            #self.process_single_layer(url, mkptid, cii)


            proc = Process(target=self.process_single_layer, args=(url, mkptid, cii))
            procs.append(proc)
            proc.start()

             # complete the processes
    for proc in procs:
        proc.join()

process_single_layer是一项基本上从云下载urls。并存储在本地的功能。

现在问题是下载过程很慢,因为它必须命中URL。而且由于记录量巨大,因此要处理1k行,因此需要6分钟。

为了减少我想实现Multiprocessing的时间。但是很难看到上面的代码有什么区别。

请建议我在这种情况下如何提高性能。

2 个答案:

答案 0 :(得分:0)

首先,您需要计算文件中的所有行,然后生成固定数量的进程(最好与处理器核心的数量相匹配),并通过队列(每个进程一个)向其中馈送一定数量的进程。行等于除法total_number_of_rows / number_of_cores。这种方法的思想是将这些行的处理划分为多个进程,从而实现并行性。

一种动态找出内核数的方法是:

import multiprocessing as mp
cores_count = mp.cpu_count()

可以通过避免创建初始队列数来稍作改进,方法是通过创建队列列表来循环添加一行,然后对其应用循环迭代器。

完整示例:

import queue
import multiprocessing as mp
import itertools as itools

cores_count = mp.cpu_count()


def dosomething(q):

    while True:

        try:
            row = q.get(timeout=5)
        except queue.Empty:
            break

    # ..do some processing here with the row

    pass

if __name__ == '__main__':
    processes
    queues = []

    # spawn the processes
    for i in range(cores_count):
        q = mp.Queue()
        queues.append(q)
        proc = Process(target=dosomething, args=(q,))
        processes.append(proc)

    queues_cycle = itools.cycle(queues)
    for row in cursor:
        q = next(queues_cycle)
        q.put(row)

    # do the join after spawning all the processes
    for p in processes:
        p.join()

答案 1 :(得分:0)

在这种情况下使用池更容易。

队列不是必需的,因为您无需在生成的进程之间进行通信。我们可以使用Pool.map来分配工作量。

Pool.imapPool.imap_unordered可能在块大小较大时更快。 (参考:https://docs.python.org/3/library/multiprocessing.html#multiprocessing.pool.Pool.imap)如果需要,可以使用Pool.starmap并消除元组拆包。

from multiprocessing import Pool

def process_single_layer(data):
    # unpack the tuple and do the processing
    url, mkptid, cii = data
    return "downloaded" + url

def get_urls():
    # replace this code: iterate over cursor and yield necessary data as a tuple
    for rec in range(8): 
            url =  "url:" + str(rec)
            mkptid = "mkptid:" + str(rec)
            cii = "cii:" + str(rec)
            yield (url, mkptid, cii)

#  you can come up with suitable process count based on the number of CPUs.
with Pool(processes=4) as pool:
    print(pool.map(process_single_layer, get_urls()))