使用Python的多处理来计算一条长输入行的整数之和

时间:2016-08-31 14:39:26

标签: python python-2.7 python-multiprocessing

我想将Python的多处理模块用于以下方面: 将输入行映射到整数列表并计算此列表的总和。

输入行最初是一个字符串,其中要求和的项目用空格分隔。

我试过的是:

from itertools import imap

my_input = '1000000000 ' * int(1e6)
print sum(imap(int, my_input.split()))

我的机器上需要大约600毫秒,但我希望通过多处理使其更快。

似乎瓶颈在于映射部分,因为sum-method在应用于整数的就绪列表时非常快:

>>> int_list = [int(1e9)] * int(1e6)
>>> %time sum(int_list)
CPU times: user 7.38 ms, sys: 5 µs, total: 7.38 ms
Wall time: 7.4 ms
>>> 1000000000000000

我尝试应用来自this question的说明,但由于我对使用多处理很陌生,我无法满足此问题的说明。

2 个答案:

答案 0 :(得分:0)

所以,这似乎大致归结为三个步骤:

  1. 制作pool
  2. 在该池中的列表中映射int()
  3. 总结结果。
  4. 所以:

    if __name__ == '__main__':
        import multiprocessing
        my_input = '1000000000 ' * int(1e6)
        string_list = my_input.split()
        # Pool over all CPUs
        int_list = multiprocessing.Pool().map(int, string_list)
        print sum(int_list)
    

    在可能的情况下使用发电机的时间可能更有效:

    if __name__ == '__main__':
        import multiprocessing
        import re
        my_input = '1000000000 ' * int(1e6)
        # use a regex iterator matching whitespace
        string_list = (x.group(0) for x in re.finditer(r'[^\s]+\s', my_input))
        # Pool over all CPUs
        int_list = multiprocessing.Pool().imap(int, string_list)
        print sum(int_list)
    

    正则表达式可能会慢于split,但使用re.finditer应该允许Pool以单个拆分的速度开始映射,并使用imap而不是mapsum re.finditer应该做的类似{允许它在可用时开始添加数字)。感谢Uncaught TypeError: Cannot read property 'getMonth' of null mm @ form_Validation.js:11(anonymous function) @ form_Validation.js:11_formatDate @ form_Validation.js:11validate @ form_Validation.js:11validateField @ form_Validation.js:10revalidateField @ form_Validation.js:11(anonymous function) @ form_Validation.js:11each @ jquery.min.js:2each @ jquery.min.js:2a.fn.formValidation @ form_Validation.js:11(anonymous function) @ index1.php:658dispatch @ jquery.min.js:3r.handle @ jquery.min.js:3trigger @ jquery.min.js:3(anonymous function) @ jquery.min.js:3each @ jquery.min.js:2each @ jquery.min.js:2trigger @ jquery.min.js:3_trigger @ bootstrap-datepicker.min.js:1_setDate @ bootstrap-datepicker.min.js:1click @ bootstrap-datepicker.min.js:1d @ jquery.min.js:2dispatch @ jquery.min.js:3r.handle @ jquery.min.js:3 想法的this answer

    多进程可能比在单个进程中执行更高效。您可能最终会失去更多时间来制作新流程并将结果从他们那里传回,而不是一次性完成所有工作。如果您尝试将添加添加到池中也是如此。

    在系统上我正在测试它,它有两个CPU,我得到单进程解决方案在大约半秒内运行,非生成器多进程解决方案在大约1秒内,以及生成器解决方案在12-13秒内。

答案 1 :(得分:0)

使用名为forking的Unix系统的一个功能,您可以从零进程中读取(不写入)来自父进程的数据。通常,您必须复制数据,但在Unix中分支进程可以避免这种情况。

使用它,池中的作业可以访问整个输入字符串并提取它将处理的部分。然后它可以自己拆分并解析字符串的这一部分,并返回其部分中的整数之和。

from multiprocessing import Pool, cpu_count
from time import time


def serial(data):
    return sum(map(int, data.split()))


def parallel(data):
    processes = cpu_count()

    with Pool(processes) as pool:
        args = zip(
            ["input_"] * processes, # name of global to access
            range(processes), # job number
            [processes] * processes # total number of jobs 
        )
        return sum(pool.map(job, args, chunksize=1))


def job(args):
    global_name, job_number, total_jobs = args
    data = globals()[global_name]
    chunk = get_chunk(data, job_number, total_jobs)

    return serial(chunk)


def get_chunk(string, job_number, total_jobs):
    """This function may mess up if the number of integers in each chunk is low (1-2).
    It also assumes there is only 1 space separating integers."""
    approx_chunk_size = len(string) // total_jobs

    # initial estimates
    start = approx_chunk_size * job_number
    end = start + approx_chunk_size

    if start and not string.startswith(" ", start - 1):
        # if string[start] is not beginning of a number, advance to start of next number
        start = string.index(" ", start) + 1

    if job_number == total_jobs:
        # last job
        end = None
    elif not string.startswith(" ", end - 1):
        # if string[end] is part of a number, then advance to end of number
        end = string.index(" ", end - 1)

    return string[start:end]


def timeit(func, *args, **kwargs):
    "Simple timing function"
    start = time()
    result = func(*args, **kwargs)
    end = time()
    print("{} took {} seconds".format(func.__name__, end - start))
    return result


if __name__ == "__main__":
#    from multiprocessing.dummy import Pool # uncomment this for testing

    input_ = "1000000000 " * int(1e6)

    actual = timeit(parallel, input_)
    expected = timeit(serial, input_)
    assert actual == expected