numpy在评估表达式时如何管理内存?

时间:2015-08-12 17:42:20

标签: 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 same dimension
  • numpy在评估这个等式时会分配中间内存吗?

[编辑]在@Daniel的回答之后,类似的基准测试显示numpy为此表达式分配了两次内存。

如果您能指出一些文档或相关的编译器技术,那将会很有帮助。谢谢!

2 个答案:

答案 0 :(得分:1)

似乎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

因此,手动优化的版本对于numpy和theano都更快,但与Theano的差异更小。打印出的Theano计算图表显示Theano的优化编译器没有自动改进计算。 Theano的版本总体上更快。

请注意,这些结果可能会因操作的矩阵大小而有所不同。

答案 1 :(得分:1)

不,numpy没有做任何此类优化。

应该怎么做? Numpy在c扩展中实现n维数组对象。剩下的就是纯粹的蟒蛇。 numpy永远不会看到您要评估的实际表达式。它按evaluation order (see python documentation)订单的规定一次执行一个操作。因此,对于每个中间体,numpy将必须分配临时内存。因此:

  • f = (x*y) + (y*z)(为了清晰起见,括号)获得三个内存分配,最后一个用于f。没有表达重写(这甚至是危险的,因为它可能会改变舍入效果)。
  • f = (x+z)*f:两次分配,最后一次分配到f
  • f = (x + y) + z:还有两个分配。

numpy当然不是完全不可能知道你正在评估的是什么表达,但是如果没有解释器的帮助,所有这些都是肮脏的伎俩,必然会混淆用户。 numpy没有做任何事情。