Почему идут многопроцессорные процессы.общие типы заданий такие медленные?
Вот небольшой код стендовой маркировки, чтобы проиллюстрировать мой вопрос:
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 ответа:
Это медленно по причинам, указанным в вашей второй ссылке, и решение на самом деле довольно простое: обойти (медленный)
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
только для чтения данных.