Python 在计算表达式时,numpy如何管理内存?
举个例子:Python 在计算表达式时,numpy如何管理内存?,python,numpy,Python,Numpy,举个例子: import numpy as np x = np.ones((100, 100)) y = np.ones((100, 100)) z = np.ones((100, 100)) f = x * y + y * z 在计算f时,numpy如何执行并存储中间结果的内存 优化是否应用于将f转换为f=(x+z)*y 如果我们改为计算f=(x+z)*y,numpy是否在f的内存之外分配任何临时内存 或考虑另一个例子: f = a + b + c # all of them of sa
import numpy as np
x = np.ones((100, 100))
y = np.ones((100, 100))
z = np.ones((100, 100))
f = x * y + y * z
- 在计算
时,numpy如何执行并存储中间结果的内存f
- 优化是否应用于将
转换为f
f=(x+z)*y
- 如果我们改为计算
,numpy是否在f=(x+z)*y
的内存之外分配任何临时内存f
或考虑另一个例子:
f = a + b + c # all of them of same dimension
- 当计算这个等式时,numpy是否分配了中间内存李>
如果你能给我介绍一些文档或相关的编译技术,我会很有帮助的。谢谢 numpy似乎没有做任何优化,但也许更令人惊讶的是,Theano也没有 下面是一个脚本,用于比较每个实现的两个变体。输出如下
import timeit
import numpy
import theano
import theano.tensor as tt
def main():
size = 1000
iterations = 1000
a = tt.matrix()
b = tt.matrix()
c = tt.matrix()
f1 = theano.function(inputs=[a, b, c], outputs=a * b + b * c)
f2 = theano.function(inputs=[a, b, c], outputs=(a + c) * b)
theano.printing.debugprint(f1)
theano.printing.debugprint(f2)
x = numpy.ones((size, size))
y = numpy.ones((size, size))
z = numpy.ones((size, size))
result = x * y + y * z
start = timeit.default_timer()
for _ in xrange(iterations):
result1 = x * y + y * z
assert numpy.all(result1 == result)
print timeit.default_timer() - start
start = timeit.default_timer()
for _ in xrange(iterations):
result2 = (x + z) * y
assert numpy.all(result2 == result)
print timeit.default_timer() - start
start = timeit.default_timer()
for _ in xrange(iterations):
result3 = f1(x, y, z)
assert numpy.all(result3 == result)
print timeit.default_timer() - start
start = timeit.default_timer()
for _ in xrange(iterations):
result4 = f2(x, y, z)
assert numpy.all(result4 == result)
print timeit.default_timer() - start
main()
我得到以下输出:
Elemwise{Composite{((i0 * i1) + (i1 * i2))}} [@A] '' 0
|<TensorType(float64, matrix)> [@B]
|<TensorType(float64, matrix)> [@C]
|<TensorType(float64, matrix)> [@D]
Elemwise{Composite{((i0 + i1) * i2)}} [@A] '' 0
|<TensorType(float64, matrix)> [@B]
|<TensorType(float64, matrix)> [@C]
|<TensorType(float64, matrix)> [@D]
9.19932313948
6.43367212255
4.15276831469
4.07725744595
Elemwise{Composite{((i0*i1)+(i1*i2))}[@A]'0
|[@B]
|[@C]
|[@D]
Elemwise{Composite{((i0+i1)*i2)}[@A]''0
|[@B]
|[@C]
|[@D]
9.19932313948
6.43367212255
4.15276831469
4.07725744595
因此,手动优化版本对于numpy和theano都更快,但与theano的差异更小。打印出来的Theano计算图表明Theano的优化编译器并没有自动改进计算。不过,Theano的版本总体来说速度更快
请注意,这些结果可能因所操作矩阵的大小而异。否,
numpy
不会进行任何此类优化
应该怎么做?Numpy在c扩展中实现n维数组对象。其余部分是纯pythonnumpy
永远看不到您试图计算的实际表达式。它按照命令一次执行一个操作。因此,对于每个中间进程,numpy
必须分配临时内存。因此:
(大括号表示清楚)获取三个内存分配,最后一个用于f=(x*y)+(y*z)
。没有进行表达式重写(这甚至是危险的,因为它可能会改变舍入效果)f
:两次分配,最后一次再次绑定到f=(x+z)*f
f
:也是两个分配f=(x+y)+z
当然,
numpy
要了解您要计算的表达式并非完全不可能,但如果没有解释器的帮助,所有这些都将是肮脏的把戏,必然会让用户感到困惑numpy
不做任何事情。一个简单的timeit测试显示(x+z)*y
快了30%——所以我拒绝这种优化。x*y+y*z
在处理浮点数时不一定等于(x+z)*y
。甚至C编译器也不会进行这种优化。例如,参见SO的另一个问题:这很有趣,谢谢。没想到西亚诺没有这个。优化是一部分,而真正的问题是在评估期间如何处理中间缓冲区。对于您的最后一个示例,f=(x+y)+z
,只能分配一次,对吗@burnpancknumpy
无法放弃第二次分配,因为它不知道是否还需要创建临时文件。对于numpy
,它看起来就像t=x+y;f=t+z
。以后可能需要t
。但是您可以手动重新排列表达式以保存一个分配:f=x+y;f+=z
。没错,刚刚做了一个基准测试来确认这一点。ufunc
的out
参数让您可以控制中间产物。e、 g.f=np.empty_-like(x);np.add(x,z,out=f);np.乘(y,f,out=f)
@hpaulj;说得好!但是,对于标准操作,如np.add
,就地操作是等效的。您无法绕过f
的分配。当然np.empty
和np.empty\u like
是最有效的,但我确信自动临时表的分配方式是相同的(即操作前没有不必要的调零)。