Почему идут многопроцессорные процессы.общие типы заданий такие медленные?


Вот небольшой код стендовой маркировки, чтобы проиллюстрировать мой вопрос:

import numpy as np
import multiprocessing as mp
# allocate memory
%time temp = mp.RawArray(np.ctypeslib.ctypes.c_uint16, int(1e8))
Wall time: 46.8 ms
# assign memory, very slow
%time temp[:] = np.arange(1e8, dtype = np.uint16)
Wall time: 10.3 s
# equivalent numpy assignment, 100X faster
%time a = np.arange(1e8, dtype = np.uint16)
Wall time: 111 ms

В основном я хочу, чтобы массив numpy был разделен между несколькими процессами, потому что он большой и доступен только для чтения. Этот метод отлично работает, никаких дополнительных копий не производится, и фактическое время вычислений по процессам хорошее. Но накладные расходы на Создание общего массива огромны.

Этот пост предложил некоторое глубокое понимание того, почему некоторые способы инициализации массива являются медленными (обратите внимание, что в приведенном выше примере я использую более быстрый метод). Но пост на самом деле не описывает, как действительно улучшить скорость до numpy, как производительность.

Есть ли у кого-нибудь предложения о том, как улучшить скорость? Будет ли какой-то код цитона иметь смысл для выделения массива?

Я работаю в системе Windows 7 x64.

3 5

3 ответа:

Это медленно по причинам, указанным в вашей второй ссылке, и решение на самом деле довольно простое: обойти (медленный) RawArray код назначения среза, который в этом случае неэффективно считывает одно необработанное значение C за один раз из исходного массива, чтобы создать объект Python, затем преобразует его обратно в необработанный C для хранения в общем массиве, затем отбрасывает временный объект Python и повторяет 1e8 раз.

Но вам не нужно делать это таким образом; например большинство вещей уровня C, RawArray реализует буферный протокол, что означает, что вы можете преобразовать его в memoryview, представление базовой необработанной памяти, которая реализует большинство операций C-подобными способами , используя операции необработанной памяти, если это возможно. Поэтому вместо того, чтобы делать:

# assign memory, very slow
%time temp[:] = np.arange(1e8, dtype = np.uint16)
Wall time: 9.75 s  # Updated to what my machine took, for valid comparison

Используйте memoryview, чтобы управлять им как необработанным байтоподобным объектом и назначать таким образом (np.arange уже реализует буферный протокол, и оператор назначения среза memoryview плавно использует его):

# C-like memcpy effectively, very fast
%time memoryview(temp)[:] = np.arange(1e8, dtype = np.uint16)
Wall time: 74.4 ms  # Takes 0.76% of original time!!!

ПРИМЕЧАНИЕ, время для последнее составляет миллисекунды, а не секунды; копирование с использованием memoryview обертывания для выполнения передачи необработанной памяти занимает менее 1% времени, чтобы сделать это трудным способом RawArray делает это по умолчанию!

Просто поместите массив numpy вокруг общего массива:

import numpy as np
import multiprocessing as mp

sh = mp.RawArray('i', int(1e8))
x = np.arange(1e8, dtype=np.int32)
sh_np = np.ctypeslib.as_array(sh)

Время:

%time sh[:] = x
CPU times: user 10.1 s, sys: 132 ms, total: 10.3 s
Wall time: 10.2 s

%time memoryview(sh).cast('B').cast('i')[:] = x
CPU times: user 64 ms, sys: 132 ms, total: 196 ms
Wall time: 196 ms

%time sh_np[:] = x
CPU times: user 92 ms, sys: 104 ms, total: 196 ms
Wall time: 196 ms

Нет необходимости выяснять, как отлить memoryview (как это было в python3 Ubuntu 16) и возиться с переформированием (если x имеет больше измерений, так как cast() уплощается). И используйте sh_np.dtype.name для двойной проверки типов данных, как и любой массив numpy. :)

В ms-windows при создании Process будет создан новый интерпретатор Python, который затем импортирует вашу программу в виде модуля. (Вот почему в ms-windows вы должны создавать только Process и Pool из блока if __name__ is "__main__".) Это позволит воссоздать Ваш массив, что займет примерно столько же времени, сколько и его первоначальное создание. Смотрите руководство по программированию , особенно касающееся метода spawn start, который должен использоваться в ms-windows.

Так что, вероятно, лучше способ состоит в том, чтобы создать сопоставленный памяти массив numpy с помощью numpy.memmap. Запишите массив на диск в Родительском процессе. (В ms-windows Этот должен быть выполнен в блоке if __name__ is "__main__", поэтому он вызывается только один раз). Затем в функции target Используйте функцию numpy.memmap только для чтения данных.