Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在计算表达式时,numpy如何管理内存?_Python_Numpy - Fatal编程技术网

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
  • 在计算
    f
    时,numpy如何执行并存储中间结果的内存
  • 优化是否应用于将
    f
    转换为
    f=(x+z)*y
  • 如果我们改为计算
    f=(x+z)*y
    ,numpy是否在
    f
    的内存之外分配任何临时内存

或考虑另一个例子:

f = a + b + c # all of them of same dimension
  • 当计算这个等式时,numpy是否分配了中间内存
[edit]在@Daniel的回答之后,一个类似的基准测试显示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维数组对象。其余部分是纯python
numpy
永远看不到您试图计算的实际表达式。它按照命令一次执行一个操作。因此,对于每个中间进程,
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
,只能分配一次,对吗@burnpanck
numpy
无法放弃第二次分配,因为它不知道是否还需要创建临时文件。对于
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
是最有效的,但我确信自动临时表的分配方式是相同的(即操作前没有不必要的调零)。