- Оригинальный адрес:Intro to Threads and Processes in Python
- Оригинальный автор:Brendan Fortuner
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:lsvih
- Корректор:yqian1991
Руководство для начинающих по параллельному программированию
Участие в KaggleUnderstanding the Amazon from SpaceВо время гонок я пытаюсь ускорить различные части своего кода. Скорость имеет решающее значение в соревнованиях Kaggle. Для получения высокого рейтинга часто требуется попробовать сотни комбинаций структур модели и гиперпараметров, а экономия 10 секунд в эпоху, которая длится одну минуту, — это огромная победа.
Что меня удивляет, так это то, что обработка данных является самым большим узким местом. Я использовал операции поворота матрицы, переворота матрицы, масштабирования и обрезки Numpy для выполнения операций на ЦП. В некоторых случаях Numpy и Pytorch DataLoader используют параллельную обработку. Я провожу от 3 до 5 экспериментов одновременно, каждый со своей обработкой данных. Но этот способ обработки кажется неэффективным, и я хотел бы знать, могу ли я использовать параллельную обработку для ускорения всех экспериментов.
Что такое параллельная обработка?
Проще говоря, это делать две вещи одновременно, или это может быть запуск кода на разных ЦП по отдельности, или полное использование «затраченных впустую» циклов ЦП, когда программа ожидает внешних ресурсов (загрузка файла, Вызовы API и т. д.) Повышение эффективности.
Следующий пример представляет собой «нормальную» программу. Он использует один поток для последовательной загрузки содержимого списка URL-адресов.
Ниже представлена та же программа, но с использованием 2 потоков. Он разбивает список URL-адресов на разные потоки, почти удваивая скорость обработки.
Если вам интересно, как нарисовать приведенную выше диаграмму, вы можете обратиться кисходный коди краткое введение ниже:
- Добавьте таймер внутри вашей функции и верните время начала и окончания выполнения функции.
URLS = [url1, url2, url3, ...]
def download(url, base):
start = time.time() - base
resp = urlopen(url)
stop = time.time() - base
return start,stop
- Визуализация однопоточной программы выглядит следующим образом: выполните свою функцию несколько раз и сохраните несколько значений времени начала и окончания.
results = [download(url, 1) for url in URLS]
- Транспонируйте массив результатов [start, stop], чтобы нарисовать гистограмму
def visualize_runtimes(results):
start,stop = np.array(results).T
plt.barh(range(len(start)), stop-start, left=start)
plt.grid(axis=’x’)
plt.ylabel("Tasks")
plt.xlabel("Seconds")
Аналогичным образом работает многопоточное построение диаграмм. Библиотека параллелизма Python также может возвращать массив результатов.
процесс против потока
Одинпроцессявляется экземпляром программы (такой как блокнот Jupyter или интерпретатор Python). запуск процессанить(подпроцесс) для обработки некоторых подзадач (таких как нажатия клавиш, загрузка HTML-страниц, сохранение файлов и т. д.). Потоки живут внутри процесса, и потоки используют одно и то же пространство памяти.
Пример: Microsoft Word
Когда вы открываете Word, вы фактически создаете процесс. Когда вы начинаете печатать, процесс запускает несколько потоков: один поток предназначен для получения ввода с клавиатуры, один поток для отображения текста, один поток для автоматического сохранения файлов и один поток для проверки орфографии. После запуска этих потоков Word может более эффективно использовать время простоя процессора (время ожидания ввода с клавиатуры или загрузки файла), чтобы повысить вашу производительность.
процесс
- Создается операционной системой для запуска программ
- Процесс может содержать несколько потоков
- Два процесса могут одновременно выполнять код в программе Python.
- Запуск и завершение процессов занимает больше времени, поэтому использование процессов обходится дороже, чем использование потоков.
- Поскольку процессы не используют совместное пространство памяти, обмен информацией между процессами происходит намного медленнее, чем обмен информацией между потоками. В Python обмен информацией с помощью сериализованных структур данных, таких как массивы, занимает время на уровне обработки ввода-вывода.
нить
- Поток — это что-то вроде мини-процесса внутри процесса.
- Различные потоки используют одно и то же пространство памяти и могут эффективно читать и записывать одни и те же переменные.
- Два потока не могут выполнять код в одной и той же программе Python (для этого есть обходной путь).
*
)
ЦП против ядра
CPU, или процессор, управляет самой базовой вычислительной работой компьютера. ЦП имеет один или несколькоядерный, что позволяет ЦП выполнять код одновременно.
Если есть только одно ядро, не будет ускорения для ресурсоемких задач (таких как циклы, операции и т. д.). Операционная система должна переключать планирование туда и обратно между различными задачами за небольшие промежутки времени. Таким образом, при выполнении некоторых тривиальных операций (таких как загрузка некоторых изображений) многозадачность фактически снижает производительность обработки. Причина этого явления заключается в том, что при запуске и обслуживании нескольких задач также возникает дополнительная нагрузка на производительность.
Проблема блокировки GIL в Python
CPython (стандартная реализация Python) имеет метод, называемыйGIL(Global Interpretation Lock) вещь, которая предотвращает одновременное выполнение двух потоков в программе. Кому-то это очень не нравится, а кому-то нравится. Для этого есть несколько обходных путей, но такие библиотеки, как Numpy, в основном обходят это ограничение, выполняя внешний код C.
Когда использовать потоки, а когда процессы?
- Благодаря нескольким ядрам и отсутствию GIL,мультипрогрессМожет ускорить программы Python, интенсивно использующие ЦП.
- МногопоточностьОн может очень хорошо справляться с задачами ввода-вывода или задачами, связанными с внешними системами, поскольку потоки могут эффективно комбинировать различные задачи. Процесс должен сериализовать результаты для агрегирования нескольких результатов, что требует дополнительного времени.
- Благодаря существованию GIL,МногопоточностьНе очень полезно для программ Python, интенсивно использующих процессор.
*
Для определенных операций, таких как скалярные произведения, Numpy обходит блокировку GIL Python и может выполнять код параллельно.
Пример параллельной обработки
питонбиблиотека concurrent.futuresЕго легко и приятно использовать. Вы просто передаете ему функцию, список объектов для обработки и количество параллелизма. В следующих нескольких разделах я проведу несколько экспериментов, чтобы продемонстрировать, когда использовать потоки, а когда — процессы.
def multithreading(func, args,
workers):
with ThreadPoolExecutor(workers) as ex:
res = ex.map(func, args)
return list(res)
def multiprocessing(func, args,
workers):
with ProcessPoolExecutor(work) as ex:
res = ex.map(func, args)
return list(res)
вызов API
Для вызовов API многопоточность значительно быстрее, чем последовательная обработка и многопроцессорность.
def download(url):
try:
resp = urlopen(url)
except Exception as e:
print ('ERROR: %s' % e)
2 темы
4 темы
2 процесса
4 процесса
Интенсивные задачи ввода-вывода
Я передаю огромный текст, чтобы наблюдать за производительностью записи потоков и процессов. Многопоточность работает лучше, но многопроцессорность также ускоряет работу.
def io_heavy(text):
f = open('output.txt', 'wt', encoding='utf-8')
f.write(text)
f.close()
сериал
%timeit -n 1 [io_heavy(TEXT,1) for i in range(N)]
>> 1 loop, best of 3: 1.37 s per loop
4 темы
4 процесса
Задачи, интенсивно использующие процессор
Поскольку GIL отсутствует, код может выполняться на нескольких ядрах одновременно, и многопроцессорность всегда выигрывает.
def cpu_heavy(n):
count = 0
for i in range(n):
count += i
Серийный номер:4,2 секунды
4 потока:6,5 секунды
4 процесса:1,9 секунды
Точечный продукт в Numpy
Неудивительно, что ни многопоточность, ни многопроцессорность этому коду не помогут. Numpy выполняет внешний код C за кулисами, минуя GIL.
def dot_product(i, base):
start = time.time() - base
res = np.dot(a,b)
stop = time.time() - base
return start,stop
Серийный номер:2,8 секунды
2 темы:3,4 секунды
2 процесса:3,3 секунды
Тетрадь для вышеуказанного эксперимента пожалуйстаобратитесь сюда, вы можете сами воспроизвести эти эксперименты.
связанные ресурсы
Вот несколько справочных статей, которые я использовал при изучении этой темы. Очень рекомендую Натана Григгаэтот блогвдохновил меня на метод визуализации.
Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллекти другие поля, если вы хотите видеть больше качественных переводов, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.