Почему идут многопроцессорные процессы.общие типы заданий такие медленные?
Вот небольшой код стендовой маркировки, чтобы проиллюстрировать мой вопрос:
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__".) Это позволит воссоздать Ваш массив, что займет примерно столько же времени, сколько и его первоначальное создание. Смотрите руководство по программированию , особенно касающееся методаspawnstart, который должен использоваться в ms-windows.Так что, вероятно, лучше способ состоит в том, чтобы создать сопоставленный памяти массив numpy с помощью
numpy.memmap. Запишите массив на диск в Родительском процессе. (В ms-windows Этот должен быть выполнен в блокеif __name__ is "__main__", поэтому он вызывается только один раз). Затем в функцииtargetИспользуйте функциюnumpy.memmapтолько для чтения данных.