装饰并发
一个简化的Python并行运算模型。仅需最少的现有串行程序修改,DECO就能自动并行化Python程序,
使用pip安装
pip install deco
通常用法
使用DECO简单得就像是在你的Python程序里查找或者创建两个函数一样简单。第一个函数是我们想要并发运行的,并且使用@concurrent
装饰。第二个函数是调用@concurrent
并且使用@synchronized
装饰的。第二个函数的装饰是可选的,但提供了一些非常酷的好处。让我们来看一个例子。
@concurrent # We add this for the concurrent function
def process_lat_lon(lat, lon, data):
#Does some work which takes a while
return result
@synchronized # And we add this for the function which calls the concurrent function
def process_data_set(data):
results = defaultdict(dict)
for lat in range(...):
for lon in range(...):
results[lat][lon] = process_lat_lon(lat, lon, data)
return results
就这样,为了并行化这个程序只有两行代码的修改。现在这个程序运行时将会使用机器上的所有cpu核心,使它运行地明显变快。
它做了什么
@concurrent
装饰器使用multiprocessing.pool来并发调用目标函数。- 索引是基于自动处理的函数参数的变种,这是进程池做不到的
@synchronized
装饰器自动插入同步性事件- 在同步性事件期间,它也自动重构了
@concurrent
函数调用的结果赋值。
局限性
@concurrent
只能给那些运行时间长于~1ms的函数提速- 如果他们需要的时间更少,你的代码将会运行地更慢!
@synchronized
装饰器只能在"简单"函数上生效,确保函数符合下面的条件- 仅仅调用,或者分配
@concurrent
函数的结果到像下面这样的可索引对象:- concurrent(...)
- result[key] = concurrent(...)
- 没有间接读取由调用
@concurrent
函数分配的对象
- 仅仅调用,或者分配
它是如何工作的
对工作机制的深入探讨,我们写了一篇论文。你可以在这里找到。
作为一个概述,DECO只主是给Python的multiprocessing.pool做了智能封装。当@concurrent
被 应用在一个函数时,它使用pool.apply_async调用来替代这个函数。此外,当参数被传给pool.apply_async时,DECO使用代 理来替换索引可变对象,便它能检测这些对象的变化。这些调用的结果之后可以通过在并发函数上调用wait()和唤起一个同步事件来获取。这些事件可以在调 用@concurrent
函数的函数上使用@synchronized
装饰器来自动放置在你的代码里。此外,当使用@synchronized
,你可以直接赋值并发函数的调用结果到索引可变对象。DECO重构这些赋值自动发生在下一个同步事件期间。所有这一切意味着在许多情况下,使用DECO的并行编程完全像串行编程一样简单。
原文地址:https://github.com/alex-sherman/deco
简单的示例:
#!/usr/bin/env python
# encoding: utf-8
"""
@version: ??
@author: Licamla
@license:
@contact: licamla@live.cn
@project: test
@software: PyCharm Community Edition
@file: test.py
@time: 16-5-15 下午8:23
"""
import os
import time
from deco import concurrent, synchronized
temp_dict = {}
for i in range(20):
temp_dict[i] = '{}pid'.format(i)
@concurrent
def test_concurrent(input_):
print(input_, os.getpid())
time.sleep(5)
return input_, os.getpid()
@synchronized
def test_synchronized():
result = {}
for k, v in temp_dict.items():
result[k] = test_concurrent(v)
return result
if __name__ == '__main__':
print(test_synchronized())
运行结果:
0pid 18689
1pid 18690
2pid 18692
3pid 18691
4pid 18693
5pid 18694
6pid 18695
7pid 18696
8pid 18693
9pid 18689
10pid 18690
11pid 18696
12pid 18691
13pid 18692
14pid 18695
15pid 18694
16pid 18689
17pid 18690
18pid 18693
19pid 18692
{0: ('0pid', 18689), 1: ('1pid', 18690), 2: ('2pid', 18692), 3: ('3pid', 18691), 4: ('4pid', 18693), 5: ('5pid', 18694), 6: ('6pid', 18695), 7: ('7pid', 18696), 8: ('8pid', 18693), 9: ('9pid', 18689), 10: ('10pid', 18690), 11: ('11pid', 18696), 12: ('12pid', 18691), 13: ('13pid', 18692), 14: ('14pid', 18695), 15: ('15pid', 18694), 16: ('16pid', 18689), 17: ('17pid', 18690), 18: ('18pid', 18693), 19: ('19pid', 18692)}
本文地址:http://my.oschina.net/soarwilldo/blog/675441