Использование 100% всех ядер с многопроцессорным модулем
У меня есть две части кода, которые я использую, чтобы узнать о многопроцессорности в Python 3.1. Моя цель - использовать 100% всех доступных процессоров. Тем не менее, фрагменты кода здесь достигают только 30% - 50% на всех процессорах.
Есть ли какой-либо способ заставить "питона" использовать все 100%? ОС (Windows 7, 64bit) ограничивает доступ Python к процессорам? Пока выполняются приведенные ниже фрагменты кода, я открываю диспетчер задач и наблюдаю всплеск процессора, но никогда не достигаю и не поддерживаю 100%. В дополнение к этому, я вижу несколько процессов python.exe, созданных и уничтоженных по пути. Как эти процессы связаны с процессорами? Например, если я порождаю 4 процесса, каждый процесс не использует свое собственное ядро. Вместо этого, какие процессы используют? Они разделяют все ядра? И если так, то ОС заставляет процессы делить ядра?
фрагмент кода 1
import multiprocessing
def worker():
#worker function
print ('Worker')
x = 0
while x < 1000:
print(x)
x += 1
return
if __name__ == '__main__':
jobs = []
for i in range(50):
p = multiprocessing.Process(target=worker)
jobs.append(p)
p.start()
фрагмент кода 2
from multiprocessing import Process, Lock
def f(l, i):
l.acquire()
print('worker ', i)
x = 0
while x < 1000:
print(x)
x += 1
l.release()
if __name__ == '__main__':
lock = Lock()
for num in range(50):
Process(target=f, args=(lock, num)).start()
Ответы
Ответ 1
Чтобы использовать 100% всех ядер, не создавайте и не уничтожайте новые процессы.
Создайте несколько процессов на ядро и привяжите их к конвейеру.
На уровне ОС все конвейерные процессы запускаются одновременно.
Чем меньше вы пишете (и чем больше вы делегируете ОС), тем более вероятно, что вы должны использовать как можно больше ресурсов.
python p1.py | python p2.py | python p3.py | python p4.py ...
Максимально использовать ваш процессор.
Ответ 2
Вы можете использовать psutil
для psutil
каждого процесса, порожденного multiprocessing
на конкретном процессоре:
import multiprocessing as mp
import psutil
def spawn():
procs = list()
n_cpus = psutil.cpu_count()
for cpu in range(n_cpus):
affinity = [cpu]
d = dict(affinity=affinity)
p = mp.Process(target=run_child, kwargs=d)
p.start()
procs.append(p)
for p in procs:
p.join()
print('joined')
def run_child(affinity):
proc = psutil.Process() # get self pid
print('PID: {pid}'.format(pid=proc.pid))
aff = proc.cpu_affinity()
print('Affinity before: {aff}'.format(aff=aff))
proc.cpu_affinity(affinity)
aff = proc.cpu_affinity()
print('Affinity after: {aff}'.format(aff=aff))
if __name__ == '__main__':
spawn()
Примечание. Как прокомментировано, psutil.Process.cpu_affinity
недоступен в macOS.
Ответ 3
Что касается фрагмента кода 1: Сколько ядер/процессоров у вас есть на тестовой машине? Это не поможет вам запустить 50 из этих процессов, если у вас есть только 2 ядра процессора. Фактически вы вынуждаете ОС тратить больше времени на переключение контекста, чтобы переместить процессы в CPU и вне его, чем выполнять фактическую работу.
Попробуйте уменьшить количество порожденных процессов до количества ядер. Итак, "для я в диапазоне (50):" должно стать чем-то вроде:
import os;
# assuming you're on windows:
for i in range(int(os.environ["NUMBER_OF_PROCESSORS"])):
...
Что касается фрагмента кода 2: вы используете многопроцессорную систему. Блокировка, которая может поддерживаться только одним процессом за раз, поэтому вы полностью ограничиваете все parallelism в этой версии программы. Вы сериализовали все, чтобы начать процесс с 1 по 50, случайный процесс (например, процесс 7) получает блокировку. Процессы 1-6 и 8-50 все сидят на линии:
l.acquire()
Пока они сидят, они просто ждут выхода блокировки. В зависимости от реализации примитива Lock они, вероятно, не используют какой-либо процессор, они просто сидят там, используя системные ресурсы, такие как RAM, но не выполняют полезную работу с процессором. Процесс 7 подсчитывает и печатает до 1000, а затем освобождает блокировку. Затем ОС может планировать случайным образом один из оставшихся 49 процессов для запуска. Независимо от того, что он просыпается первым, он получит следующий замок и запустится, а оставшиеся 48 ждут блокировки. Это будет продолжаться для всей программы.
В принципе, фрагмент кода 2 является примером того, что делает concurrency трудным. Вы должны управлять доступом несколькими процессами или потоками к некоторому общему ресурсу. В этом конкретном случае действительно нет причин, по которым эти процессы должны ждать друг друга.
Итак, из этих двух, Snippet 1 ближе к более эффективному использованию CPU. Я думаю, что правильная настройка количества процессов в соответствии с количеством ядер приведет к значительному улучшению результата.
Ответ 4
Минимальный пример на чистом Python:
def f(x):
while 1:
# ---bonus: gradually use up RAM---
x += 10000 # linear growth; use exponential for faster ending: x *= 1.01
y = list(range(int(x)))
# ---------------------------------
pass # infinite loop, use up CPU
if __name__ == '__main__': # name guard to avoid recursive fork on Windows
import multiprocessing as mp
n = mp.cpu_count() * 32 # multiply guard against counting only active cores
with mp.Pool(n) as p:
p.map(f, range(n))
Использование: чтобы согреться в холодный день (но не стесняйтесь менять цикл на что-то менее бессмысленное.)
Предупреждение: чтобы выйти, не тяните за вилку и не держите кнопку питания, вместо этого нажмите Ctrl-C.
Ответ 5
Чтобы ответить на ваши вопросы:
В любом случае, чтобы "заставить" python использовать все 100%?
Не то, чтобы я слышал о
Является ли ОС (Windows 7, 64 бит) ограничивающим доступ к процессорам Python?
Да и Нет, Да: если python занимает 100%, окна замерзнут. Нет, вы можете предоставить python Admin Priviledges, что приведет к блокировке.
Как эти процессы относятся к процессорам?
Они не являются технически на уровне ОС, эти "процессы" python - это потоки, которые обрабатываются обработчиком ОС, поскольку он нуждается в обработке.
Вместо этого, какие процессы используют? Разделяют ли они все ядра? И если да, то это ОС, которая вынуждает процессы делиться ядрами?
Они делятся всеми ядрами, если только вы не запустите один экземпляр python с привязкой к определенному ядру (в многоядерной системе) ваши процессы будут разделены на обработку без ведома без обработки. Итак, да, ОС заставляет основной доступ по умолчанию (или python технически)
если вы заинтересованы в сродстве к ядру python, ознакомьтесь с аффилированным пакетом для python.