基本上,我正在寻找一种使用python3协程而不是线程或进程提供并行映射的东西。我认为执行高度并行的IO工作时应该减少开销。

肯定已经存在类似的东西,无论是在标准库中还是在广泛使用的软件包中?

最佳答案

免责声明 PEP 0492仅定义协程的语法和用法。它们需要运行事件循环,最有可能是 asyncio 's event loop

异步映射

我不知道基于协程的map的任何实现。但是,使用 map 实现基本的asyncio.gather()功能很简单:

def async_map(coroutine_func, iterable):
    loop = asyncio.get_event_loop()
    future = asyncio.gather(*(coroutine_func(param) for param in iterable))
    return loop.run_until_complete(future)

此实现非常简单。它为iterable中的每个项目创建一个协程,将它们加入单个协程,并在事件循环上执行已加入的协程。

提供的实现方案涵盖部分案例。但是它有一个问题。如果长期迭代,您可能希望限制并行运行的协程数量。我无法提出简单的实现方式,该实现方式高效并且可以同时保留顺序,因此我将其作为练习留给读者。

表现

您声称:



它需要证明,因此这里比较了multiprocessing实现,a pgevent实现和我基于协程的实现。所有测试均在Python 3.5上执行。

使用multiprocessing的实现:
from multiprocessing import Pool
import time


def async_map(f, iterable):
    with Pool(len(iterable)) as p:  # run one process per item to measure overhead only
        return p.map(f, iterable)

def func(val):
    time.sleep(1)
    return val * val

使用gevent的实现:
import gevent
from gevent.pool import Group


def async_map(f, iterable):
    group = Group()
    return group.map(f, iterable)

def func(val):
    gevent.sleep(1)
    return val * val

使用asyncio的实现:
import asyncio


def async_map(f, iterable):
    loop = asyncio.get_event_loop()
    future = asyncio.gather(*(f(param) for param in iterable))
    return loop.run_until_complete(future)

async def func(val):
    await asyncio.sleep(1)
    return val * val

测试程序是通常的timeit:
$ python3 -m timeit -s 'from perf.map_mp import async_map, func' -n 1 'async_map(func, list(range(10)))'

结果:
  • 可以迭代10项:
  • multiprocessing-1.05秒
  • gevent-1秒
  • asyncio-1秒
  • 可以迭代100项:
  • multiprocessing-1.16秒
  • gevent-1.01秒
  • asyncio-1.01秒
  • 可以迭代500项:
  • multiprocessing-2.31秒
  • gevent-1.02秒
  • asyncio-1.03秒
  • 可以迭代5000项:
  • multiprocessing-失败(生成5k进程不是一个好主意!)
  • gevent-1.12秒
  • asyncio-1.22秒
  • 可以迭代50000项:
  • gevent-2.2秒
  • asyncio-3.25秒

  • 结论

    当程序主要执行I/O而非计算时,基于事件循环的并发工作速度更快。请记住,当I/O更少且涉及更多计算时,差异将更小。

    与基于事件循环的并发引入的开销相比,生成过程引入的开销要大得多。这意味着您的假设是正确的。

    比较asynciogevent可以说,asyncio的开销大33-45%。这意味着,创建绿色小子比创建协程便宜。

    最后的结论是:gevent具有更好的性能,但是asyncio是标准库的一部分。性能差异(绝对数字)不是很明显。 gevent是相当成熟的库,而asyncio相对较新,但是发展很快。

    关于python - 在Python中,是否有一个异步于multiprocessing或current.futures?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32955846/

    10-12 17:51