Использование 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()
5 ответов
Чтобы использовать 100% всех ядер, не создавайте и не разрушайте новые процессы.
Создайте несколько процессов на ядро и свяжите их с конвейером.
На уровне ОС все конвейерные процессы выполняются одновременно.
Чем меньше вы пишете (и тем больше вы делегируете ОС), тем больше у вас шансов использовать как можно больше ресурсов.
python p1.py | python p2.py | python p3.py | python p4.py ...
Будет максимально использовать ваш процессор.
Ты можешь использовать 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.
Минимальный пример:
def f(x):
while 1:
if (x * x) ^ 1 + 1 >= 1: # run all parts
pass # but it is just pointless
import multiprocessing as mp
n_thread = mp.cpu_count()
with mp.Pool(n_thread) as p:
p.map(f, range(n_thread))
Вариант использования: чтобы нагреть машину в холодный день (смените внутреннюю часть цикла "время" на что-то менее бессмысленное.)
Относительно фрагмента кода 1. Сколько ядер / процессоров у вас на тестовой машине? Бесполезно запускать 50 из этих процессов, если у вас только 2 ядра процессора. Фактически вы заставляете ОС тратить больше времени на переключение контекста, чтобы перевести процессы на процессор и выключать его, чем на фактическую работу.
Попробуйте уменьшить количество порождаемых процессов до количества ядер. Так что "для меня в диапазоне (50):" должно стать что-то вроде:
import os;
# assuming you're on windows:
for i in range(int(os.environ["NUMBER_OF_PROCESSORS"])):
...
Относительно фрагмента кода 2: Вы используете многопроцессорную блокировку, которая может удерживаться только одним процессом за раз, поэтому вы полностью ограничиваете параллелизм в этой версии программы. Вы сериализовали вещи так, что процесс с 1 по 50 запускается, случайный процесс (скажем, процесс 7) получает блокировку. Процессы 1-6 и 8-50 все сидят на линии:
l.acquire()
Пока они сидят там, они просто ждут, пока замок будет снят. В зависимости от реализации примитива Lock они, вероятно, не используют какой-либо ЦП, они просто сидят там, используя системные ресурсы, такие как ОЗУ, но не выполняют никакой полезной работы с ЦП. Процесс 7 считает и печатает до 1000, а затем снимает блокировку. Затем ОС может произвольно планировать запуск одного из оставшихся 49 процессов. Какой бы он ни проснулся первым, он получит замок следующим и побежит, пока оставшиеся 48 ждут на замке. Это будет продолжаться для всей программы.
По сути, фрагмент кода 2 является примером того, что затрудняет параллелизм. Вы должны управлять доступом большого количества процессов или потоков к некоторому общему ресурсу. В этом конкретном случае действительно нет причин, чтобы эти процессы должны были ждать друг друга.
Таким образом, Snippet 1 ближе к более эффективному использованию процессора. Я думаю, что правильная настройка количества процессов для соответствия количеству ядер даст намного улучшенный результат.
Я бы рекомендовал использовать библиотеку Joblib, это хорошая библиотека для многопроцессорной обработки, используемая во многих приложениях ML, в sklearn и т. Д.
from joblib import Parallel, delayed
Parallel(n_jobs=-1, prefer="processes", verbose=6)(
delayed(function_name)(parameter1, parameter2, ...)
for parameter1, parameter2, ... in object
)
куда n_jobs
количество одновременных работ. Задаватьn=-1
если вы хотите использовать все доступные ядра на машине, на которой выполняется ваш код.
Подробнее о параметрах здесь: https://joblib.readthedocs.io/en/latest/generated/joblib.Parallel.html
В вашем случае возможная реализация:
def worker(i):
print('worker ', i)
x = 0
while x < 1000:
print(x)
x += 1
Parallel(n_jobs=-1, prefer="processes", verbose=6)(
delayed(worker)(num)
for num in range(50)
)
Чтобы ответить на ваш вопрос (ы):
Есть ли какой-либо способ заставить "питона" использовать все 100%?
Не то чтобы я слышал о
ОС (windows 7, 64bit) ограничивает доступ Python к процессорам?
Да и Нет, да: если это питон взял 100%, окна будут зависать. Нет, вы можете предоставить привилегии администратора Python, что приведет к блокировке.
Как эти процессы связаны с процессорами?
На техническом уровне ОС они не являются теми "процессами" python, которые являются потоками, которые обрабатываются обработчиком ОС, так как он требует обработки.
Вместо этого, какие процессы используют? Они разделяют все ядра? И если так, то ОС заставляет процессы делить ядра?
Они совместно используют все ядра, если только вы не запустите один экземпляр Python, для которого привязка установлена к определенному ядру (в многоядерной системе), ваши процессы будут разделены на обработку, которая когда-либо свободна от ядра. Так что да, ОС по умолчанию форсирует совместное использование ядра (или технически Python)
если вас интересует сходство с ядром python, посмотрите пакет affinity для python.