2015-12-15 1 views
2

В системе Linux, которую я использую, планировщик не очень щедр, давая время процессора для подпроцессов, порожденных из многопроцессорного модуля python. При использовании 4 субпроцессов на 4-ядерном компьютере я получаю около 22% процессора в соответствии с ps. Однако, если подпроцессы являются дочерними процессами оболочки, а не программой python, она достигает почти 100% CPU. Но многопроцессорность - гораздо более приятный интерфейс, чем ручное разделение моих данных, и запуск отдельных программ python для каждого раскола, и было бы неплохо получить лучшее из обоих миров (организация кода и высокая загрузка процессора). Я попробовал установить привлекательность процессов на -20, но это не помогло.Изменения уровня операционной системы для ускорения многопроцессорности Python?

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

Точная версия я использую:

$ uname -a 
Linux <hostname> 3.19.0-39-generiC#44~14.04.1-Ubuntu SMP Wed Dec 2 10:00:35 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux 

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

with Pool(4) as p: 
    p.map(function,data) 

Update: Это не воспроизводимая проблема. Результаты, представленные здесь, были получены несколько дней назад, и я снова проверил тест, и процессы многопроцессорности были такими быстрыми, как я надеялся. Возможно, этот вопрос должен быть удален, было бы неплохо ввести в заблуждение людей о производительности, ожидаемой от multiprocessing.

+2

Вы уверены, что это ядро ​​в вине? Эта проблема больше связана с тем, что Python не может выполнять задания по-настоящему параллельно. – 5gon12eder

+0

Многопроцессорные рабочие являются собственными процессами с их собственными PID.Я понимаю, что делает процессы отличными друг от друга, как, например, от моего веб-браузера. – seewalker

+1

Вы можете [легко] насыщать свою шину памяти (т. Е.), Вы связаны с памятью, а не вычисляете границы. Возможно, вы могли бы оценить или проверить, чтобы подтвердить. Посмотрите на/sys/devices/system/cpu/cpu */cpufreq и установите «производительность» –

ответ

2

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

Я написал следующий ориентир. Если я запускаю скрипт либо с параметром --fork, либо с параметром --mp, я всегда получаю 400% загрузки процессора (на моей четырехъядерной машине) и сопоставимое общее время выполнения около 18 секунд. Однако, если вызывается с опцией --threads, программа эффективно работает последовательно, достигая только около 100% загрузки процессора, и занимает минуту для завершения по причине mentioned по dave.

import multiprocessing 
import os 
import random 
import sys 
import threading 


def find_lucky_number(x): 
    prng = random.Random() 
    prng.seed(x) 
    for i in range(100000000): 
     prng.random() 
    return prng.randint(0, 100) 

def with_threading(inputs): 
    callback = lambda x : print(find_lucky_number(x)) 
    threads = [threading.Thread(target=callback, args=(x,)) for x in inputs] 
    for t in threads: 
     t.start() 
    for t in threads: 
     t.join() 

def with_multiprocessing(inputs): 
    with multiprocessing.Pool(len(inputs)) as pool: 
     for y in pool.map(find_lucky_number, inputs): 
      print(y) 

def with_forking(inputs): 
    pids = list() 
    for x in inputs: 
     pid = os.fork() 
     if pid == 0: 
      print(find_lucky_number(x)) 
      sys.exit(0) 
     else: 
      pids.append(pid) 
    for pid in pids: 
     os.waitpid(pid, 0) 

if __name__ == '__main__': 
    inputs = [1, 2, 3, 4] 
    if sys.argv[1] == '--threads': 
     with_threading(inputs) 
    if sys.argv[1] == '--mp': 
     with_multiprocessing(inputs) 
    elif sys.argv[1] == '--fork': 
     with_forking(inputs) 
    else: 
     print("What should I do?", file=sys.stderr) 
     sys.exit(1) 
+0

То, что я сделал, очень похоже на вашу функцию with_multiprocessing: запуск процессов в контексте 'with Pool' и отсутствие синхронизации или разделяемой памяти. Результаты, о которых я сообщал, произошли несколько дней назад, и я попытался запустить его снова, и результаты больше похожи на то, что вы нашли. Я думаю об удалении вопроса, потому что это не воспроизводимая проблема. Может быть, модератор знает, стоит ли это делать, учитывая, что вы сделали несколько хороших моментов. – seewalker

+0

@seewalker Я не знаю, какое лучшее решение в этом случае. Никто из нас не может удалить вопрос, как сейчас. Я не знаю, подходит ли флажок, но это вариант. С другой стороны, вы можете попытаться переформулировать свой вопрос, чтобы спросить: «Как заставить Python использовать все процессоры?». Но будьте осторожны, чтобы не спрашивать то, что немедленно закрывается как дубликат. – 5gon12eder

+0

Я оставил бы вопрос, частично из-за ответа @ 5gon12eder. Его тестовая программа делает это того стоит. И, я давний программист, но новичок python, поэтому я получил что-то от этого. Но у меня были схожие проблемы с расщеплением perl WRT с использованием потоков или процессов. Даже если он не воспроизводится, и вопрос и ответ ясны [и подтверждены]. Я видел много вопросов, оставшихся до того, что ОП повторяет и получает разные результаты. Вы отредактировали вопрос с отказом от ответственности - вы охвачены [юридически и морально]. ИМО, модераторы имеют большую рыбу, чтобы жарить и не повредить, здесь нет фола. –

1

Добро пожаловать в CPython Global Interpreter Lock. Ваши потоки отображаются в виде отдельных процессов в ядре linux (так как потоки реализованы в Linux в целом: каждый поток получает свой собственный процесс, поэтому ядро ​​может их планировать).

Так почему же Linux не планирует запуск более одного из них за один раз (поэтому ваша 4-ядерная машина усредняет около 25% минус немного накладных расходов)? Интерпретатор python удерживает блокировку при интерпретации каждого потока, тем самым блокируя запуск других потоков (поэтому они не могут быть запланированы).

Чтобы обойти эту проблему вы можете:

  1. Используйте процессы, а не потоки (как вы упоминаете в своем вопросе)
  2. Используйте другой питон переводчика, который не имеет глобального Interpreter Lock.
+0

Во всех отношениях я не думаю, что это правильный ответ. В первом абзаце документации говорится: * 'multiprocessing' - это пакет, который поддерживает процессы нереста с использованием API, аналогичного модулю' threading'. Пакет «multiprocessing» предлагает как локальный, так и удаленный параллелизм, эффективно перешагивая блокировку Global Interpreter Lock, используя подпроцессы вместо потоков. * – 5gon12eder

+0

Да, возможно, преждевременно было отметить, что это правильный ответ. В конкретном случае, с которым я столкнулся, вероятно, был связан с Global Interpreter Lock (спасибо dave!), Но спасибо, что заметили, что многопроцессорность не привязана к этой блокировке. – seewalker

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