Как обрабатывать действительно большие объекты, возвращаемые из joblib.Parallel ()? - PullRequest
0 голосов
/ 15 марта 2020

У меня есть следующий код, где я пытаюсь распараллелить:

import numpy as np
from joblib import Parallel, delayed

lst = [[0.0, 1, 2], [3, 4, 5], [6, 7, 8]]
arr = np.array(lst)
w, v = np.linalg.eigh(arr)

def proj_func(i):
    return np.dot(v[:,i].reshape(-1, 1), v[:,i].reshape(1, -1))

proj = Parallel(n_jobs=-1)(delayed(proj_func)(i) for i in range(len(w)))

proj возвращает действительно большой список, и это вызывает проблемы с памятью.

Есть ли способ, которым я мог бы обойти это?

Я думал о возвращении генератора, а не списка, но я не знаю, как это сделать. Любые другие способы тоже приветствуются.

1 Ответ

1 голос
/ 15 марта 2020

Q : "Есть ли способ, которым я мог бы обойти это ?"

Это, черт возьми, зависит на что такое this означает.


Является ли ПАМЯТЬ Размер узким местом?
Пространство для стати c - размер данных:

Учитывая ваш MCVE, как было опубликовано выше, размер ПАМЯТИ зависит от N = arr.size, и ваша система имеет как минимум:
- N * 3 * 8 [B] ОЗУ для удержания lst, arr, w
- N * N * 8 [B] ОЗУ для удержания v

Положите в целом, должно быть путь больше чем <_nCPUs_> * 8 * N * ( 3 + N ) [B] RAM-пространства, просто для представления n_jobs == -1 полных копий процесса интерпретатора python (определенно, что для MacOS / WinOS и, скорее всего, также для linux, как fork- метод был задокументирован в 2019/2020 гг. для получения нестабильных / небезопасных результатов) до код пытался выполнить даже первый вызов proj_func( i )

Если это не так емкость вашей системы, вы можете прямая остановка чтения.


Далее?
Пространство для динамических c данных:

Любой вызов следующего N -вызывает на proj_func( i ), каждый добавляет дополнительное распределение ОЗУ - N * N * 8 [B] ОЗУ для хранения np.dot() -результатов

В целом более k * N * N * N * 8 [B] RAM для хранения np.dot() -результатов, где k >> 2, так как каждый из этих N -результатов должен получить SER -пакетирован (снова выделяя для этого место в ОЗУ), затем каждая такая SER -ed-полезная нагрузка должна передаваться с пульта- joblib.Parallel()(delayed()(...)) - executor перейти к основному процессу (здесь снова выделяется некоторое пространство ОЗУ для полезной нагрузки SER), а затем эта промежуточная двоичная полезная нагрузка, хранимая в ОЗУ, должна получить DES - erialized (таким образом, снова выделяя некоторое дополнительное пространство RAM для хранения DES -данных данных оригинального размера N * N * 8 [B]), чтобы наконец получить этот конвейерный продукт SER / DES N раз добавляется к начальному proj == [], так как указанный выше синтаксис использования
joblib.Parallel(…)( delayed( proj_func )( i ) for i in range( len( w ) ) ) -класса настаивает и настоятельно предписывает.

<_nCPUs_> * 8 * N * ( 3 + N ) //     static storage: data + all python process-replicas
+
<_nCPUs_> * 8 * N * N * k     //    dynamic storage: SER/DES on joblib.Parallel()(delayed…)
+
            8 * N * N * N     // collective storage: proj-collected N-( np.dot() )-results
~
=           8 * N * ( N * N + <_nCPUs_> * ( 3 + N * ( k + 1 ) ) )

РЕЗЮМЕ:

Это скоро масштабируется (даже если мы не предполагали никаких других python -процессов import -s и stati c данных) значительно выше объема оперативной памяти "обычного" хост-компьютера для любого N == arr.size >= 1E3:

>>> nCPUs =  4; k = 2.1; [ ( 8 * N * ( N * N + nCPUs * (3+N*(k+1)))/1E9 ) for N in ( 1E3, 1E4, 1E5, 1E6 ) ]
[8.099296, 8009.92096, 8000992.0096, 8000099200.096]
>>> nCPUs =  8; k = 2.1; [ ( 8 * N * ( N * N + nCPUs * (3+N*(k+1)))/1E9 ) for N in ( 1E3, 1E4, 1E5, 1E6 ) ]
[8.198592, 8019.84192, 8001984.0192, 8000198400.192]
>>> nCPUs = 16; k = 2.1; [ ( 8 * N * ( N * N + nCPUs * (3+N*(k+1)))/1E9 ) for N in ( 1E3, 1E4, 1E5, 1E6 ) ]
[8.397184, 8039.68384, 8003968.0384, 8000396800.384]
 8[GB]     |...[GB]    |  |...[GB]   |  |  |...[GB]
           8   [TB]    |...   [TB]   |  |...   [TB]
                       8      [PB]   |...      [PB]
                                     8         [EB]

EPILOGUE:

Таким образом, простой SLO C, использующий такой же простой синтаксис, как и синтаксис joblib.Parallel()(delayed()()), может сразу опустошать все выполненные до сих пор усилия вычислительного графа одним, необратимым образом, если не были затрачены надлежащие усилия по разработке хотя бы для количественной оценки необработанной обработки данных.

...