Как избежать огромного дополнительного потребления памяти при использовании векторизации numpy?

Этот код ниже лучше всего иллюстрирует мою проблему:

Вывод на консоль (обратите внимание, запуск даже первого теста занимает около 8 минут) показывает, что выделения массива 512x512x512x16-бит потребляют не больше, чем ожидалось (256 МБ для каждого), и, глядя на «сверху», процесс обычно остается суб- 600MB, как и ожидалось.

Однако при вызове векторизованной версии функции процесс расширяется до огромного размера (более 7 ГБ!). Даже самое очевидное объяснение, которое я могу придумать для объяснения этого — векторизация преобразует входные и выходные данные в число с плавающей запятой 64 внутренне — может объяснить только пару гигабайт, хотя векторизованная функция возвращает int16, а возвращаемый массив, безусловно, int16. Есть ли способ избежать этого? Я неправильно использую/понимаю аргумент векторизации otypes?

import numpy as np
import subprocess

def logmem():
    subprocess.call('cat /proc/meminfo | grep MemFree',shell=True)

def fn(x):
    return np.int16(x*x)

def test_plain(v):
    print "Explicit looping:"
    logmem()
    r=np.zeros(v.shape,dtype=np.int16)
    for z in xrange(v.shape[0]):
        for y in xrange(v.shape[1]):
            for x in xrange(v.shape[2]):
                r[z,y,x]=fn(x)
    print type(r[0,0,0])
    logmem()
    return r

vecfn=np.vectorize(fn,otypes=[np.int16])

def test_vectorize(v):
    print "Vectorize:"
    logmem()
    r=vecfn(v)
    print type(r[0,0,0])
    logmem()
    return r

logmem()    
s=(512,512,512)
v=np.ones(s,dtype=np.int16)
logmem()
test_plain(v)
test_vectorize(v)
v=None
logmem()

Я использую те версии Python/numpy, которые актуальны в системе AMD64 Debian Squeeze (Python 2.6.6, numpy 1.4.1).


person timday    schedule 16.08.2011    source источник
comment
Вы пытались запустить cProfile поверх него?   -  person Jakob Bowyer    schedule 16.08.2011
comment
Нет, я никогда не пробовал профилировать код Python; У меня сложилось впечатление, что он просто расскажет мне о времени звонков. Может ли он также сказать мне что-нибудь полезное о том, где выделяется память?   -  person timday    schedule 16.08.2011
comment
Профилирование кода покажет вам, что поглощает больше всего времени, это может помочь вам изолировать то, что вызывает замедление вашего кода. Heapy также предоставит вам точные результаты использования памяти.   -  person Jakob Bowyer    schedule 16.08.2011


Ответы (2)


вы можете прочитать исходный код vectorize(). Он преобразует dtype массива в объект и вызывает np.frompyfunc() для создания ufunc из вашей функции python, ufunc возвращает массив объектов и, наконец, vectorize() преобразует массив объектов в массив int16.

Он будет использовать много памяти, когда dtype массива является объектом.

Использование функции python для поэлементного вычисления выполняется медленно, даже если оно преобразуется в ufunc с помощью frompyfunc().

person HYRY    schedule 16.08.2011

Основная проблема векторизации заключается в том, что все промежуточные значения также являются векторами. Хотя это удобный способ получить приличное повышение скорости, он может быть очень неэффективным с использованием памяти и будет постоянно перегружать кеш вашего процессора. Чтобы преодолеть эту проблему, вам нужно использовать подход, который имеет явные циклы, работающие на скорости компиляции, а не на скорости Python. Лучший способ сделать это — использовать cython, код на Фортране, заключенный в f2py или числоэкспр. Вы можете найти сравнение этих подходов здесь, хотя это больше фокусируется на скорости, чем на использовании памяти.

person DaveP    schedule 16.08.2011