Медленный дамп рассола в python при использовании многопроцессорности - PullRequest
1 голос
/ 10 июля 2020

Итак, я пытаюсь распараллелить функцию, которая решает экземпляры pyomo с python 3.7, используя модуль многопроцессорности. Код работает, но время запуска абсурдно (~ 25 секунд на процесс). Странно то, что я попробовал тот же код на другом, но гораздо менее мощном компьютере, и он снизился до ~ 2 секунд (тот же код, такое же количество параллельных процессов, те же версии для всего, кроме Python, что составляет 3,6 на этом p c).

Используя cProfile, я обнаружил, что метод дампа сборщика потреблял столько времени, но я не могу понять, почему это займет так много времени. Данных мало, и я проверил с помощью sys.getsizeof (), были ли какие-либо аргументы распараллеленной функции больше, чем ожидалось, но это не так.

Кто-нибудь знает, в чем может быть причина медленного дампа рассола?

Код:

from pyomo.environ import *
from pyomo.opt import SolverFactory, TerminationCondition
from pyomo.opt.parallel import SolverManagerFactory
import sys
import multiprocessing

def worker(init_nodes[i_nodo][j_nodo], data, optsolver, queue, shared_incumbent_data):
    #[pyomo instances solving and constraining]
    return

def foo(model, data, optsolver, processes = multiprocessing.cpu_count()):

    queue = multiprocessing.Queue()
    process_dict = {}

    for i_node in range(len(init_nodes)): #init_nodes is a list containing lists of pyomo instances
        for j_node in range(len(init_nodes[i_node])):
            
            process_name = str(i_node) + str(j_node)
            print(" - Data size:", sys.getsizeof(data)) #same for all of the args
            
            process_dict[process_name] = multiprocessing.Process(target=worker, args=(init_nodes[i_nodo][j_nodo], data, optsolver, queue, shared_incumbent_data))

            pr = cProfile.Profile()
            pr.enable()                 

            process_dict[process_name].start()

            pr.disable()
            ps = pstats.Stats(pr)
            ps.sort_stats('time').print_stats(5)

    for n_nodo in process_dict:
        process_dict[n_nodo].join(timeout=0)

#imports        
#[model definition]
#[data is obtained from 3 .tab files, the biggest one has a 30 x 40 matrix, with 1 to 3 digit integers]     
optsolver = SolverFactory("gurobi")

if __name__ == "__main__":
    foo(model, data, optsolver, 4)

Размер аргументов, полученных sys.getsizeof () и профиль .start () на первом компьютере

 - Data size: 56
 - Init_nodes size: 72
 - Queue size: 56
 - Shared incumbent data size: 56

         7150 function calls (7139 primitive calls) in 25.275 seconds

   Ordered by: internal time
   List reduced from 184 to 5 due to restriction <5>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        2   25.262   12.631   25.267   12.634 {method 'dump' of '_pickle.Pickler' objects}
        1    0.004    0.004    0.004    0.004 {built-in method _winapi.CreateProcess}
     1265    0.002    0.000    0.004    0.000 C:\Users\OLab\AppData\Local\Continuum\anaconda3\lib\site-packages\pyomo\core\expr\numeric_expr.py:186(__getstate__)
        2    0.001    0.001    0.002    0.001 <frozen importlib._bootstrap_external>:914(get_data)
     1338    0.001    0.000    0.002    0.000 C:\Users\OLab\AppData\Local\Continuum\anaconda3\lib\site-packages\pyomo\core\expr\numvalue.py:545(__getstate__)

Размер аргументов, полученных sys.getsizeof () и профиль .start () на втором компьютере

 - Data size: 56
 - Init_nodes size: 72
 - Queue size: 56
 - Shared incumbent data size: 56

         7257 function calls (7247 primitive calls) in 1.742 seconds

   Ordered by: internal time
   List reduced from 184 to 5 due to restriction <5>

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        2    1.722    0.861    1.730    0.865 {method 'dump' of '_pickle.Pickler' objects}
        1    0.009    0.009    0.009    0.009 {built-in method _winapi.CreateProcess}
     1265    0.002    0.000    0.005    0.000 C:\Users\Palbo\Anaconda2\envs\py3\lib\site-packages\pyomo\core\expr\numeric_expr.py:186(__getstate__)
     1339    0.002    0.000    0.003    0.000 C:\Users\Palbo\Anaconda2\envs\py3\lib\site-packages\pyomo\core\expr\numvalue.py:545(__getstate__)
     1523    0.001    0.000    0.001    0.000 {built-in method builtins.hasattr}

Ура!

Технические характеристики первого компьютера это должно быть намного быстрее, но это не так:

  • Windows 10 Pro для рабочих станций
  • Процессор Intel Xeon Silver 4114 @ 2,20 ГГц 2,19 ГГц (по 10 ядер)
  • 64 ГБ ОЗУ

Характеристики второго компьютера:

  • Windows 8,1
  • Процессор Intel Core i3-2348M @ 2,30 ГГц 2,30 ГГц ( По 2 ядра)
  • 6 ГБ ОЗУ

1 Ответ

0 голосов
/ 15 июля 2020

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

Время дампа уменьшено с ~ 24 [с] до ~ 0,005 [с]!

def worker(pickled_file_name, queue, shared_incumbent): 

    with open(pickled_file_name, "rb") as f:
        data_tuple = pickle.load(f, encoding='bytes')
    instance, data, optsolver, int_var_list, process_name, relaxed_incumbent = data_tuple
    return

def foo():
    [...]
    picklefile = open("pickled_vars"+str(i_nodo)+str(j_nodo)+".p", "wb") 
    picklefile.write(pickle.dumps(variables_,-1))
    picklefile.close()
                
    process_dict[process_name] = multiprocessing.Process(target=bnbparallelbranching, args=("pickled_vars"+str(i_nodo)+str(j_nodo)+".p", q, shared_incumbent_data))
    process_dict[process_name].start()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...