python - многопроцессорность медленнее, чем последовательная

это моя первая многопроцессорная реализация, я выполнил свой код в последовательном подходе, и мне потребовалась минута, чтобы обработать около 30 секунд, чтобы обработать 20 записей. Но я создал словарь с каждым ключом, имеющим набор записей, и попытался применить функцию, используя pool.map для каждого ключа. Теперь обработка занимает более 2 минут, хотя я выделяю каждое ядро ​​для каждого процесса. Может ли кто-нибудь помочь мне оптимизировать это.

def f(values):
    data1 = itertools.combinations(values,2)
    tuple_attr =('Age', 'Workclass', 'Fnlwgt', 'Education', 'Education-num', 'marital-status', 'Occupation', 'Relationship', 'Race', 'Sex', 'Capital-gain', 'Capital-loss', 'Hours-per-week', 'Native country', 'Probability', 'Id')
    new = ((tuple_attr[i] for i, t in enumerate(zip(*pair)) if t[0]!=t[1]) for pair in data1)
    skt = set(frozenset(temp) for temp in new)
    newset = set(s for s in skt if not any(p < s for p in skt))

    empty = frozenset(" ")
    tr_x = set(frozenset(i) for i in empty)
    tr = set(frozenset(i) for i in empty)
    for e in newset:
        tr.clear()
        tr = tr.union(tr_x)
        tr_x.clear()
        for x in tr:
            for a in e:
                if x == empty:
                    tmp = frozenset(frozenset([a]))
                    tr_x = tr_x.union([tmp])
                else : 
                    tmp = frozenset(frozenset([a]).union(x))
                    tr_x = tr_x.union([tmp])
        tr.clear()
        tr = tr.union(tr_x)
        tr = set(l for l in tr if not any(m < l for m in tr))

    return tr

def main():
    p = Pool(len(data)) #number of processes = number of CPUs
    keys, values= zip(*data.items()) #ordered keys and values
    processed_values= p.map( f, values )
    result= dict( zip(keys, processed_values ) ) 
    p.close() # no more tasks
    p.join()  # wrap up current tasks
    print(result)


if __name__ == '__main__':
    import csv
    dicchunk = {*****} #my dictionary
    main()

person ds_user    schedule 18.09.2014    source источник
comment
Попробовать больший набор данных? Требуется довольно много работы, прежде чем несколько потоков/процессов станут стоить накладных расходов на переключение контекста, разветвление и т. д.   -  person    schedule 18.09.2014
comment
Это один беспорядочный код, вам действительно следует поработать над именами ваших переменных. И что сказал @Corey, когда я печатал свой комментарий - больший набор данных. Но не слишком много, так как это будет довольно дорого для памяти.   -  person Tymoteusz Paul    schedule 18.09.2014
comment
Можете ли вы предоставить образец набора данных?   -  person dano    schedule 18.09.2014
comment
@Corey, с учетом времени, которое это занимает, я сомневаюсь, что накладные расходы на нерест имеют значение.   -  person Veedrac    schedule 18.09.2014
comment
p = Pool(len(data)) #number of processes = number of CPUs просто не понял. len(data) число процессоров у вас?   -  person starrify    schedule 18.09.2014
comment
Ok. Это мой словарь - dpaste.com/3DHBY9J --› образец набора данных, который я пытаюсь использовать.   -  person ds_user    schedule 18.09.2014
comment
@starrify да. количество ключей в словаре - это количество моих процессоров. Это не имеет значения, поскольку я использую кластер с большим количеством процессоров.   -  person ds_user    schedule 18.09.2014
comment
@Пучек Да. Конечно. Со временем исправлю.. :)   -  person ds_user    schedule 18.09.2014
comment
@Jeeva Вы убедились, что получаете правильный ответ как в многопроцессорной, так и в немногопроцессорной версиях? Я спрашиваю, потому что в моих тестах обработка определенных подсписков очень медленная. Я не думаю, что multiprocessing имеет к этому какое-то отношение.   -  person dano    schedule 18.09.2014
comment
Я не проверял их на самом деле, дайте мне посмотреть сейчас.   -  person ds_user    schedule 18.09.2014
comment
Упс. Я проверил это, и вы правы. Только для некоторых подсписков требуется время.   -  person ds_user    schedule 18.09.2014


Ответы (1)


Я создал тестовую программу, чтобы запустить ее один раз с multiprocessing и один раз без:

def main(data):
    p = Pool(len(data)) #number of processes = number of CPUs
    keys, values= zip(*data.items()) #ordered keys and values
    start = time.time()
    processed_values= p.map( f, values )
    result= dict( zip(keys, processed_values ) ) 
    print("multi: {}".format(time.time() - start))
    p.close() # no more tasks
    p.join()  # wrap up current tasks

    start = time.time()
    processed_values = map(f, values)
    result2 = dict( zip(keys, processed_values ) ) 
    print("non-multi: {}".format(time.time() - start))
    assert(result == result2)

Вот результат:

multi: 191.249588966
non-multi: 225.774535179

multiprocessing быстрее, но не так сильно, как можно было бы ожидать. Причина этого в том, что некоторые подсписки занимают намного (несколько минут) больше времени, чем другие. Вы никогда не будете быстрее, чем сколько бы времени ни потребовалось для обработки самого большого подсписка.

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

<Process(PoolWorker-4, started daemon)> is done. Took 0.940237998962 seconds
<Process(PoolWorker-2, started daemon)> is done. Took 1.28068685532 seconds
<Process(PoolWorker-1, started daemon)> is done. Took 42.9250118732 seconds
<Process(PoolWorker-3, started daemon)> is done. Took 193.635578156 seconds

Как видите, работники выполняют очень неравные объемы работы, поэтому вы экономите всего около 44 секунд по сравнению с последовательностью.

person dano    schedule 18.09.2014
comment
Большой. Я понял, то же самое я вижу на своем компьютере. Есть ли способ сбалансировать это? Есть ли способ сбалансировать его таким образом, чтобы рабочий, который завершает работу быстрее, брал на себя следующую доступную задачу? И любая другая возможность оптимизировать мой код, потому что я не знаю, почему это занимает много времени только для определенных подсписков. Это всего 20 записей, дальше я собираюсь обработать 50 тысяч записей.. :( - person ds_user; 18.09.2014
comment
Если вы в конечном итоге передадите более cpu_count() элементов в map, то рабочие возьмут следующий элемент из итерируемого объекта, пока они еще остались. Попытка сбалансировать проблему с неравными подсписками, вероятно, будет сложной. Вам нужно найти способ разделить работу, которую выполняет f, чтобы вы могли распараллелить ее части, а затем объединить их вместе. Я действительно не понимаю, что вы делаете в этом методе, достаточно хорошо, чтобы сказать вам, как это сделать лучше всего (или если это вообще возможно). - person dano; 18.09.2014
comment
Ok. Но я не могу разделить работу, которую я делаю внутри функции, она должна выполняться последовательно. В любом случае, спасибо - person ds_user; 18.09.2014