大家遇到Python跑得很慢的时候,会想用多线程优化一下吗?
但是用手把代码改成多线程很麻烦!
好在聪明的莉沫酱发明了「超」,它可以在运行时自动将普通代码改成多线程代码!
首先我们来看这一段代码,功能是计算两个数的和:
def f1(a):
time.sleep(1)
return a
def f2(a):
time.sleep(1)
return a
def f(x, y):
xx = f1(x)
yy = f2(y)
return xx + yy
它显然需要执行2秒,太慢了!
如果我们有办法让f1
和f2
并行执行,那不就只要1秒了?
那接下来我们给f
加上超
,像是这样:
from 超 import 超
@超
def f(x, y):
xx = f1(x)
yy = f2(y)
return xx + yy
t = time.time()
f(1, 2)
print(time.time() - t) # 1.0019426345825195
现在它只需要执行1秒了!
原理其实很简单,拥有超
的函数在执行时会将函数可见的全局变量、函数输入、builtins全部替换成符号对象,然后用这些符号对象假装执行一次函数,直到遇到函数结束或者符号对象不支持的操作。遇到这些操作的时候,就把符号对象展开成普通对象,然后再继续运行,最后拿到结果就好了。
我们用上面的样例来理解一下:
-
首先执行
f(1, 2)
,但是这个时候输入被替换成了符号,所以实际上执行的是f(符号x,符号y)
。 -
进到函数里面,执行
xx = f1(x)
,但是这个时候f1
也被替换成了符号,所以xx
运行时的值是符号f1.__call__(符号x)
。这个符号是复合符号,它跟踪了它是由哪几个符号组成的,因此未来可以把它还原成普通对象。 -
接下来执行
yy = f2(y)
,同理,yy
运行时的值是符号f2.__call__(符号y)
。 -
执行
return xx + yy
,又产生了一个复合符号符号f1.__call__(符号x) + 符号f2.__call__(符号y)
。 -
最终拿到这个复合符号后,它可以等价为一个有向无环图。在这里它更弱,是一棵树,
符号f1.__call__(符号x) + 符号f2.__call__(符号y)
的两个子节点是符号f1.__call__(符号x)
和符号f2.__call__(符号y)
,而符号f1.__call__(符号x)
的子节点是符号f1
和符号x
。 -
在有向无环图上的起点上填入真正的对象,比如
x=1
、y=2
,然后尽可能地并行计算所有的路线,算完再把真正的结果return
出来就好啦。
首先使用pip安装:
pip install git+https://github.com/RimoChan/chao.git
然后在代码里from 超 import 超
,你要加速哪个函数就给它挂上@超
就好了。
其他的样例可以看test.py。
-
函数应当是无状态的。假如两个函数一个更新了全局变量,另一个去读,那最后拿到的结果可能是错误的。
-
只能优化延迟,不能优化CPU。即使平均到每一次执行,占用的CPU时间都是更多的。
-
由于各种各样的原因,实际上有可能反而变慢。
就这样,我要回去睡觉了,大家88!