Почему лучше использовать синхронное программирование для операций в памяти? - PullRequest
0 голосов
/ 03 марта 2019

У меня сложная вложенная структура данных.Я перебираю его и выполняю некоторые вычисления для каждой возможной уникальной пары элементов.Это все математические функции в памяти.Я не читаю из файлов и не работаю по сети.

Требуется несколько часов, чтобы do_work() вызывали 25 000 раз.Я ищу способы ускорить его.

Хотя Pool.map() кажется полезным для моих списков, это оказывается сложным, потому что мне нужно передать дополнительные аргументы в отображаемую функцию.

Я подумал, что использование библиотеки Python multitasking поможет, но когда я использую Pool.apply_async() для вызова do_work(), это на самом деле занимает больше времени.

Я немного погуглил и блогер говорит «Использовать синхронизацию для операций в памяти - асинхронность - это пустая трата времени, когда вы не производите блокировку вызовов». Это правда?Может кто-нибудь объяснить, почему?Операции чтения и записи в оперативной памяти мешают друг другу?Почему мой код занимает больше времени с асинхронными вызовами?do_work() записывает результаты вычислений в базу данных, но это не меняет мою структуру данных.

Конечно, есть способ использовать мои ядра процессора вместо простой линейной итерации по моим спискам.


Моя отправная точка, делающая это синхронно:

     main_list   = [ [ [a,b,c,[x,y,z], ... ], ... ], ... ] # list of identical structures
     helper_list = [1,2,3]
     z = 2
     for i_1 in range(0, len(main_list)):
         for i_2 in range(0, len(main_list)):
             if i_1 < i_2: # only unique combinations
                 for m in range(0, len(main_list[i_1])):
                     for h, helper in enumerate(helper_list):
                         do_work( 
                             main_list[i_1][m][0], main_list[i_2][m][0], # unique combo
                             main_list[i_1][m][1], main_list[i_1][m][2], 
                             main_list[i_1][m][3][z], main_list[i_2][m][3][h],
                             helper_list[h]
                         )

Имена переменных были изменены, чтобы сделать его более читабельным.

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Это просто общий ответ, но слишком длинный для комментария ...

Прежде всего, я думаю, что вашим самым большим узким местом в данный момент является сам Python.Я не знаю, что делает do_work(), но если он интенсивно использует процессор, у вас есть GIL, который полностью предотвращает эффективное распараллеливание внутри одного процесса.Независимо от того, что вы делаете, потоки будут бороться за GIL, и это в конечном итоге сделает ваш код еще медленнее.Помните: Python имеет реальную многопоточность, но центральный процессор совместно используется внутри одного процесса.Я рекомендую проверить страницу Дэвида М Бизли: http://dabeaz.com/GIL/gilvis, который приложил много усилий для визуализации поведения GIL в Python.

С другой стороны, модуль multiprocessing позволяет вамзапустить несколько процессов и «обойти» недостатки GIL, но будет сложно получить доступ к тем же ячейкам памяти без больших штрафов или компромиссов.

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

Подводя итог, я думаю, что код, который вы запускаете, может быть порядкомна порядок быстрее с numba и numpy структурами.

В качестве альтернативы, вы можете попробовать переписать код на языке, подобном Julia (очень похож синтаксис на Python и сообщество очень полезно), и быстро проверитьнасколько это быстро, чтобы изучить пределы производительности.Всегда полезно почувствовать, насколько быстрым может быть что-то (или части кода) в языке, который не имеет таких сложных критических аспектов производительности, как Python.

0 голосов
/ 03 марта 2019

Ваша задача больше связана с процессором, чем с операциями ввода / вывода.Асинхронное выполнение имеет смысл, когда у вас есть длинные операции ввода-вывода, т.е. отправка / получение чего-либо из сети и т. Д.

Что вы можете сделать, это разделить задачу на куски и использовать потоки и многопроцессорность (работать на разных ядрах ЦП).

...