Я работаю с довольно большими плотными массивами с плавающей запятой, которые в настоящее время находятся на диске в PyTables CArray
s. Мне нужно иметь возможность выполнять эффективные точечные продукты с использованием этих массивов, например C = A.dot(B)
, где A
- это огромный (~ 1E4 x 3E5 float32) массив с памятью, а B
и C
меньше numpy массивы, которые находятся в основной памяти.
То, что я делаю в данный момент, - это копирование данных в массивы numpy с отображением памяти с использованием np.memmap
, а затем вызов np.dot
непосредственно на массивы с отображением памяти. Это работает, но я подозреваю, что стандартный np.dot
(или, скорее, базовые функции BLAS, которые он вызывает), вероятно, не очень эффективен с точки зрения количества операций ввода-вывода, необходимых для вычисления результата.
Я нашел интересный пример в этой обзорной статье. Наивный точечный продукт, рассчитанный с использованием 3х вложенных циклов, например:
def naive_dot(A, B, C):
for ii in xrange(n):
for jj in xrange(n):
C[ii,jj] = 0
for kk in xrange(n):
C[ii,jj] += A[ii,kk]*B[kk,jj]
return C
требуется выполнить операции ввода/вывода O (n ^ 3).
Однако, обрабатывая массивы в блоках соответствующего размера:
def block_dot(A, B, C, M):
b = sqrt(M / 3)
for ii in xrange(0, n, b):
for jj in xrange(0, n, b):
C[ii:ii+b,jj:jj+b] = 0
for kk in xrange(0, n, b):
C[ii:ii+b,jj:jj+b] += naive_dot(A[ii:ii+b,kk:kk+b],
B[kk:kk+b,jj:jj+b],
C[ii:ii+b,jj:jj+b])
return C
где M
- максимальное количество элементов, которые будут вписываться в память ядра, количество операций ввода-вывода сводится к O (n ^ 3/sqrt (M)).
Насколько разумным является np.dot
и/или np.memmap
? Вызывает ли вызов np.dot
эффективный продукт с блочной точкой? Имеет ли np.memmap
какое-либо причудливое кэширование, которое улучшило бы эффективность такого типа операций?
Если нет, существует ли какая-то ранее существовавшая функция библиотеки, которая выполняет эффективные точечные продукты ввода-вывода, или я должен сам попытаться реализовать ее?
Update
Я провела сравнительный анализ с ручным внедрением np.dot
, который работает с блоками входного массива, которые явно считываются в память ядра. Эти данные по крайней мере частично касаются моего первоначального вопроса, поэтому я отправляю его как ответ.