2015-05-08 2 views
1

Я пытаюсь использовать многопроцессорность в python (2.7.8) на Mac OSX. Прочитав ответ Велимира Млакера на этот question, я смог использовать multiprocessing.Pool() для многопроцессорности простой функции, но она не работает с моей фактической функцией. Я получаю правильные результаты, но он выполняется последовательно. Я считаю, что проблема в том, что моя функция петли поверх music21.stream(), которая похожа на список, но имеет специальную функциональность для музыкальных данных. Я считаю, что потоки music21 не могут быть маринованными, так есть ли какая-то многопроцессорная альтернатива пулу, которую я могу использовать? Я не возражаю, если результаты возвращаются не по порядку, и при необходимости я могу перейти на другую версию python. Я включил свой код для многопроцессорной задачи, но не для функции stream_indexer(), которую он вызывает. Спасибо!Многопроцессорные задачи на питоне, которые нельзя мариновать?

import multiprocessing as mp 
def basik(test_piece, part_numbers): 
    jobs = [] 
    for i in part_numbers: 
     # Each 2-tuple in jobs has an index <i> and a music21 stream that 
     # corresponds to an individual part in a musical score. 
     jobs.append((i, test_piece.parts[i])) 
    pool = mp.Pool(processes=4) 
    results = pool.map(stream_indexer, jobs) 
    pool.close() 
    pool.join() 

    return results 
+0

Какая операционная система (mp работает по-разному на окнах)? Это не проблема рассола или вы получите исключение. Является ли поток в памяти? mp может сериализоваться и передавать весь поток работнику. – tdelaney

+0

Спасибо за ваш вклад, я работаю в Mac OSX. Программа определенно работает серийно, когда ей предоставляются потоки, и каждый кортеж в списке имеет поток, поскольку это второй элемент, но почему это приведет к его серийному запуску? Также как я мог избежать передачи всего потока каждому работнику? – Alex

+1

Можете ли вы отложить создание потока для рабочего? Это поможет распараллелить. Вы также можете попробовать поместить поток в глобальный список, прежде чем создавать пул и передавать указатель списка работнику. Поскольку дочерний элемент получает копию в виде записи родительской памяти, она уже будет там. Если потоки находятся на диске, вы в конечном счете зависите от скорости хранения. – tdelaney

ответ

1

Новейшие Git фиксаций music21 имеют функции, чтобы помочь с некоторыми из более хитрых частей многопроцессорной, на основе joblib. Например, если вы хотите, чтобы сосчитать все ноты в части, вы можете нормально делать в сериале:

import music21 
def countNotes(s): 
    return len(s.recurse().notes) 
    # using recurse() instead of .flat to avoid certain caches... 

bach = music21.corpus.parse('bach/bwv66.6') 
[countNotes(p) for p in bach.parts] 

параллельно работает следующим образом:

music21.common.runParallel(list(bach.parts), countNotes) 

НО! вот огромное предостережение. Давайте время эти:

In [5]: %timeit music21.common.runParallel(list(b.parts), countNotes) 
10 loops, best of 3: 152 ms per loop 

In [6]: %timeit [countNotes(p) for p in b.parts] 
100 loops, best of 3: 2.19 ms per loop 

На моем компьютере (2 ядра, 4 нити), работает параллельно почти 100x медленнее, чем работает в сериале. Зачем? Потому что есть значительные накладные расходы на подготовку потока для многопроцессорности. Если выполняемая подпрограмма выполняется очень медленно (около 1 мс/нота, деленная на количество процессоров), тогда стоит потратить поток в многопроцессорной обработке. В противном случае, посмотреть, если есть способы передавать только назад и вперед маленькие биты информации, например, на пути к процессу:

def parseCountNotes(fn): 
    s = corpus.parse(fn) 
    return len(s.recurse().notes) 

bach40 = [b.sourcePath for b in music21.corpus.search('bwv')[0:40]] 

In [32]: %timeit [parseCountNotes(b) for b in bach40] 
1 loops, best of 3: 2.39 s per loop 

In [33]: %timeit music21.common.runParallel(bach40, parseCountNotes) 
1 loops, best of 3: 1.83 s per loop 

Здесь мы начинаем получать ускорения даже на MacBook Air. В моем офисе Mac Pro ускорение становится огромным для звонков, таких как это. В этом случае звонок до parse массово доминирует в течение времени до recurse().

+0

btw - common.runParallel не имеет ничего общего с w/music21, поэтому он должен быть способен превращаться в отдельный пакет, если кто захочет его. –

Смежные вопросы