Многопроцессорность Python не использует все ядра на RHEL6

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

Я заметил, что когда я запускаю свою симуляцию, используется не более 3 из 12 ядер. Фактически, когда я запускаю симуляцию, она сначала использует 3 ядра, а затем, через некоторое время, переходит на 1 ядро. Иногда с самого начала используются только одно или два ядра. Я не мог понять, почему (я в основном ничего не меняю, кроме закрытия нескольких окон терминала (без каких-либо активных процессов)). (ОС - Red Hat Enterprise Linux 6.0, версия Python - 2.6.5.)

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

Я поискал информацию об этой проблеме в Интернете и прочитал большинство связанных вопросов на этом сайте (например, один , два), но не смог найти решения.

(Изменить: я просто попытался запустить код под Windows 7, и он правильно использует все доступные ядра. Я все же хочу исправить это для RHEL.)

Вот мой код (без учета физики):

from multiprocessing import Queue, Process, current_process

def f(q,start,end): #a dummy function to be passed as target to Process
    q.put(mc_sim(start,end))

def mc_sim(start,end): #this is where the 'physics' is 
    p=current_process()
    print "starting", p.name, p.pid        
    sum_=0
    for i in xrange(start,end):
        sum_+=i
    print "exiting", p.name, p.pid
    return sum_

def main():
    NP=0 #number of processes
    total_steps=10**8
    chunk=total_steps/10
    start=0
    queue=Queue()
    subprocesses=[]
    while start<total_steps:
        p=Process(target=f,args=(queue,start,start+chunk))
        NP+=1
        print 'delegated %s:%s to subprocess %s' % (start, start+chunk, NP)
        p.start()
        start+=chunk
        subprocesses.append(p)
    total=0
    for i in xrange(NP):
        total+=queue.get()
    print "total is", total
    #two lines for consistency check:    
    # alt_total=mc_sim(0,total_steps)
    # print "alternative total is", alt_total
    while subprocesses:
        subprocesses.pop().join()

if __name__=='__main__':
    main()

(Фактически код основан на ответе Алекса Мартелли здесь.)

Изменить 2: в конце концов проблема разрешилась сама собой, и я не понял, как это сделать. Я не менял код и не слышал об изменении чего-либо, связанного с ОС. Несмотря на это, теперь при запуске кода используются все ядра. Возможно, проблема возникнет позже, но пока я предпочитаю не исследовать ее дальше, поскольку она работает. Всем спасибо за помощь.


person the.real.gruycho    schedule 03.10.2012    source источник
comment
Я бы попробовал сначала подключиться к процессу, а потом подсчитать сумму. Мне ваше решение кажется странным. Если это не помогает, предоставьте дополнительные выходные данные отладки, чтобы мы могли видеть, где ваши процессы блокируются. Вы проверили, сколько процессов запущено? Если используется только 3 ядра, у вас может быть только 3 процесса или намного больше, кроме спящих. Это различие, о котором было бы полезно знать.   -  person Achim    schedule 03.10.2012
comment
если mc_sim реализован на чистом питоне, а не в коде C, отличном от Python, то вы, вероятно, столкнетесь с GIL (глобальная блокировка интерпретатора). Если вы все же зашли на какой-нибудь C или Cython, который не требует GIL, вам необходимо убедиться, что подпрограмма правильно помечена как, например, cdef void func(int a) nogil (во всяком случае, именно так вы это делаете для Cython.)   -  person tehwalrus    schedule 03.10.2012
comment
@Achim, вы имеете в виду, что вы сначала присоединитесь к процессам (две строки, начинающиеся с while subprocesses: ...), прежде чем вычислять окончательную сумму (две строки, начинающиеся с for i in range(NP):)? Я просто попробовал это, и, похоже, это не имеет значения.   -  person the.real.gruycho    schedule 03.10.2012
comment
@tehwalrus Я просто попытался запустить свой код без каких-либо изменений на машине с Windows 7, и он использовал все 8 ядер. Разве это не означает, что проблема не в GIL Python?   -  person the.real.gruycho    schedule 03.10.2012
comment
@tehwalrus GIL имеет значение только при работе с потоками одного и того же процесса. Многопроцессорность позволяет избежать использования потоков специально по этой причине.   -  person tylerl    schedule 03.10.2012
comment
Проблема не может быть multiprocessing только потому, что в документации сказано, что это не так. не использую GIL. Я не могу найти проблему в вашем коде, поэтому, если вы тоже не найдете ее, я предлагаю вам взглянуть на параллельный модуль Python. Это легко, быстро, не использует GIL и, вероятно, может вам помочь (опять же, если вы не найдете проблемы).   -  person aga    schedule 03.10.2012
comment
Проблема может быть в O / S. Модуль multiprocessing гарантирует только то, что вы будете работать с несколькими процессами. Операционная служба должна распределять эти процессы между доступными ядрами, AFAIK.   -  person mpenkov    schedule 03.10.2012
comment
@tylerl спасибо за разъяснения - извиняюсь за затуманивание вопроса.   -  person tehwalrus    schedule 03.10.2012
comment
@Tropcho: Да, это то, что я имел в виду. Пожалуйста, проверьте макс. количество процессов, запущенных одновременно. Вы также можете записать текущее время в обоих циклах в конце. Это покажет вам, происходит ли какая-то (в настоящее время необъяснимая) блокировка.   -  person Achim    schedule 03.10.2012
comment
В качестве общего ответа, предполагая, что вы не используете блокировки, вы можете проверить свои требования к памяти и дискам и их доступность, особенно емкость, скорость и задержку. Вы не сказали, пробовали ли вы использовать RHEL 6 и Windows 7 на одном или разных компьютерах. Уместно только сравнение на той же машине.   -  person Acumenus    schedule 04.10.2012
comment
@Achim Наконец, я думаю, что это где-то ошибка. Я использовал multiprocessing.current_process(), чтобы проверить, какие процессы запускаются (я отредактирую код выше, чтобы показать это). Все процессы запускались немедленно, но, тем не менее, только несколько ядер (максимум 3, но иногда 1 или 2) были активными. Через несколько часов я запустил тот же код, и все 12 ядер были использованы. Затем я переключил компьютер в спящий режим и сразу же снова включил, а затем снова запустил код. На этот раз снова использовалось лишь несколько ядер. Сбивает с толку.   -  person the.real.gruycho    schedule 04.10.2012
comment
@ ABB Я пробовал Windows 7 и RHEL 6 на разных машинах.   -  person the.real.gruycho    schedule 04.10.2012
comment
@ A-B-B Память составляет около 62 ГБ, и ~ 75% этой памяти используется при total_steps = 10 ** 9. Список range(start,end) может использовать много памяти, если end-start - большое число. Чтобы исправить это, можно использовать while (start<end): ... start+=1. Я пробовал это, но все же использовалось лишь несколько ядер.   -  person the.real.gruycho    schedule 04.10.2012
comment
@Tropcho: Измерьте необходимое время от начала до конца в каждом подпроцессе. Цифры, вероятно, подтвердят, что некоторые процессы заблокированы / находятся в спящем режиме и будут длиться намного дольше, чем в среднем. Если подтвердится, попробуйте убрать все возможности для входа в систему. Я бы удалил очередь, жестко закодировал значения и проверил бы, как ведет себя код. Я предполагаю, что доступ к очереди каким-то образом блокируется.   -  person Achim    schedule 04.10.2012
comment
попробуйте запустить его на python 2.7. Добавьте mp.log_to_stderr (). SetLevel (logging.DEBUG), используйте mp.Pool (), чтобы не управлять процессами вручную, попробуйте mp.Manager (). Queue () (если это ошибка в mp.Queue () )   -  person jfs    schedule 04.10.2012
comment
@ J.F. Себастьян: Я не обнаружил значительных ошибок в multiprocessing.Queue в Python 2.6 на RHEL 5 или 6. Я отлично использовал его в нескольких проектах. @Tropcho: Конечно, вы не должны использовать range в Python 2.x - вместо этого используйте xrange. При использовании многих процессов используйте ps или top для проверки их типичного состояния выполнения.   -  person Acumenus    schedule 04.10.2012
comment
@Achim Да, я забыл об этом упомянуть, я рассчитал процессы, и когда использовалось только одно ядро, все они занимали примерно одинаковое количество времени. Так что, вероятно, они одновременно работают на одном ядре.   -  person the.real.gruycho    schedule 05.10.2012
comment
@ J.F.Sebastian: Я пробовал python 2.7, то же самое, что и с python 2.6. Остальное, что вы предлагаете, я попробую немного позже, сейчас есть кое-что более срочное.   -  person the.real.gruycho    schedule 05.10.2012
comment
Привет всем, извините за то, что не предоставил обновление раньше: в конце концов проблема разрешилась сама собой, и я не понял, как это сделать. Я не менял код и не слышал об изменении чего-либо, связанного с ОС. Несмотря на это, теперь при запуске кода используются все ядра. Возможно, проблема возникнет позже, но пока я предпочитаю не исследовать ее дальше, поскольку она работает. Всем спасибо за помощь.   -  person the.real.gruycho    schedule 07.02.2013


Ответы (1)


Я запустил Ваш пример на Ubuntu 12.04 x64 (kernel 3.2.0-32-generic) с версией Python 2.7.3 x64 на i7 процессоре, и все 8 ядер, о которых сообщила система, были полностью перегружены (на основе наблюдения htop), поэтому Ваша проблема, сэр, основана на реализации ОС, и код хороший.

person WBAR    schedule 06.10.2012
comment
Верно. Похоже, что ошибка исправлена. См. Комментарий оригинального постера: Tropcho от 7 февраля 2013 г. в 9:33, что он начал работать и на RHEL. - person nealmcb; 30.10.2015