Главная / О компании / Новости / Многопоточность и многопроцессорность – выбор правильного подхода для вашей разработки

Многопоточность и многопроцессорность – выбор правильного подхода для вашей разработки

« Назад

Многопоточность и многопроцессорность – выбор правильного подхода для вашей разработки  16.12.2020 14:05

Дори Экстерман / 6 октября 2020 г.

Прежде чем мы погрузимся в различные размышления по выбору стратегии параллельных вычислений, которая соответствует вашим потребностям (многопоточность или многопроцессорность и разница между ними), я хочу начать с обсуждения закона Мура. Как вы наверняка знаете, закон Мура гласит, что тактовая частота процессора удваивается каждые два года. Это утверждение было верно в течение многих лет, но в последнее время стало неактуальным, так как тактовая частота не увеличивается, как раньше. На самом деле, закон Мура довольно скоро окончательно перестанет быть актуальным (я знаю, это ужасающе).

Производители компьютеров, достигая своих пределов, «обходили» закон Мура, вводя мультипроцессоры, ведь многоядерная архитектура – это настоящий и будущий способ справиться с ограничениями. Современные рабочие станции имеют 4, 8, 16, 32, 64 ядра; ожидается, что  для повышения производительности и быстродействия приложений, будут использоваться несколько ядер, особенно при высокопроизводительных рабочих нагрузках, таких как рендеринг, моделирование, машинное обучение и другие трудоемкие вычислительные задачи.

Когда вы хотите написать программное обеспечение, которое должно использовать существующую аппаратную многопроцессорную архитектуру, крайне важно продумать правильную архитектуру для этой задачи. По большей части у вас будет выбор между многопоточностью и многопроцессорностью или и тем, и другим. Ваш выбор будет влиять на производительность вашего программного обеспечения, будущее обслуживание, масштабируемость, использование памяти и другие факторы. У выбора любой из этих альтернатив есть свои преимущества и недостатки - вам просто нужно ознакомиться с ними, чтобы принять правильное решение. В этой статье мы рассмотрим различные критерии по выбору правильной стратегии с учетом требований вашего приложения. Другими словами, рассмотрим преимущества и недостатки многопоточной и многопроцессорной разработки в соответствии с различными сценариями. Итак, без лишних слов, давайте сразу перейдем к делу.

Многопоточная разработка: преимущества

Наиболее очевидным преимуществом многопоточности является легкость, с которой вы можете обмениваться данными между потоками (с помощью переменных, объектов и т.д). Кроме того, очень легко взаимодействовать с родительским процессом потока.

Если вы работаете с большими наборами данных, которые не могут быть разделены на подмножества, многопоточность будет полезна, потому что дублирование ваших наборов данных, как это происходит при многопроцессорной обработке, может занять много времени и памяти, а работа с данными, находящими в общей памяти, вносит сложности в разработку программного обеспечения.

Еще одно хорошо известное преимущество многопоточности заключается в том, что она поддерживается многими сторонними библиотеками (открытыми и коммерческими). Многие доступные библиотеки сегодня поддерживают многопоточные приложения, предоставляя "потокобезопасный" интерфейс. Компоненты, классы, функции и другое со встроенной поддержкой многопоточности позволяют разработчикам легко разрабатывать многопоточный код.

Но не может быть все так хорошо…

Многопоточная разработка: недостатки

Одним из основных недостатков многопоточного кода является ситуация, когда один из потоков завершает работу, а вместе с ним и все приложение. Это не относится к многопроцессорной обработке, где сбой одного процесса не обязательно влияет на другие процессы.

Еще один недостаток - сложность отладки многопоточных приложений. Это представляет проблему, так как вы, несомненно, столкнетесь с ошибками. Обычно отладчик не является лучшим инструментом для обработки многопоточных ошибок, и вам нужно будет использовать журналы для отслеживания ошибок и выяснения того, какой поток вызывает их (или связь между потоками). Проще говоря, отладка займет у вас довольно много времени. Это также требует более опытного разработчика для правильной разработки и отладки многопоточного приложения, поэтому, если вы заранее знаете, что ваша команда состоит из новичков, вам нужно будет принять это во внимание.

Также проблема возникает, когда одновременно выполняется слишком много потоков. Процессор может тратить много времени на переключение контекста за счет фактической обработки, сбрасывая блоки памяти в файловую систему и увеличивая количество операций ввода/вывода, что в конечном итоге замедляет работу всего приложения и засоряет хост-машину.

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

Теперь давайте рассмотрим преимущества многопроцессорности.

Многопроцессорная разработка: Преимущества

Как я уже упоминалось ранее, если один из ваших процессов выходит из строя, это не означает, что все приложение выходит из строя, что является значительным преимуществом (если только речь не идет о процессе в пространстве ядра). Таким образом, если что-то не удается, и вы написали свое приложение таким образом, чтобы оно было отказоустойчивым, вы можете легко восстановиться.

Еще одним преимуществом является вопрос отладки, который, как мы теперь знаем, является недостатком многопоточности. В многопроцессорном режиме гораздо проще отлаживать, поскольку легче обрабатывать небольшой элементарный процесс, чем многопоточное приложение, в котором потоки выполняются параллельно в одном и том же пространстве памяти процесса.

Кроме того, у вас будет меньше проблем с блокировками. Да, если процессы приложения реализованы аналогично многопоточности (например, вы работаете с одним и тем же пространством общей памяти), вы столкнетесь с теми же сложностями. Тем не менее, если данные реплицируются (а затем объединяются обратно, когда и если это необходимо), проблемы блокировки не существует.

Наконец, многопроцессорная обработка является масштабируемой. Вы можете выполнять процессы в другом месте, то есть выгружать их на удаленные машины или в облако, в то время как потоки всегда должны оставаться в контексте пространства памяти процесса.

Но масштабирование - это еще не все, и у многопроцессорной обработки тоже есть свои недостатки.

Многопроцессорная разработка: недостатки

Коммуникация - главный недостаток. Взаимодействие между процессами сложнее, чем взаимодействие между потоками. Для обмена данными и поддержки блокировок и синхронизации (при необходимости) требуется индивидуальная разработка.

Кроме того, количество поддерживающих библиотек (то, что мы называем «безопасными для процесса» библиотеками) по сравнению с потокобезопасными библиотеками относительно невелико.

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

Многопоточность и многопроцессорность: список критериев

Прежде чем решить, какая архитектура подходит вам больше, стоит принять во внимание следующие критерии:

  1. Синхронизация. У вас есть необходимость в синхронизации большого количества данных, и вам нужно поддерживать отношения между процессами? Синхронизация данных более проста в многопоточности, поскольку все потоки используют одно и то же пространство памяти процесса. Если ваши параллельные элементы (потоки или процессы) требуют обширной синхронизации и не являются инкапсулированными элементами, многопоточность может иметь преимущество с точки зрения простоты разработки.
  2. При многопроцессорной обработке синхронизация требует индивидуальной разработки, применения собственных логических механизмов и реализации элемента, с помощью которого процессы могут взаимодействовать или синхронизироваться – например, серверное приложение, общая память, прямая (одноранговая) связь, TCP-связь, база данных и т. д.

Вы используете большие наборы данных? В этом случае нужно задаться вопросом, необходимо только чтение, в котором использование общей памяти для многопроцессорного доступа не является большой проблемой, или нужно сложное чтение/запись. Блокировки потоков обеспечивают более надежную архитектуру для доступа к данным. В то же время возможность загрузки набора данных в стандартные объекты и классы в легко разделяемое пространство процессов намного проще, чем реализация общего доступа к памяти для многопроцессорного доступа.

  1. Простота разработки. Кто будет участвовать в разработке? Опытный разработчик или новичок?

С одной стороны, многопоточность включает множество готовых “потокобезопасных” библиотек. С другой стороны, в многопроцессорной среде проще отлаживать отдельный процесс. Кроме того, понимание кода намного проще в многопроцессорной среде. Проще говоря, многопроцессорная разработка проще, потому что инкапсуляцию легче поддерживать; менее опытные разработчики могут поддерживать код с меньшими трудностями.

  1. Масштабирование – масштаб проекта. Сколько задач вам нужно выполнить одновременно? Каковы будут масштабы проекта в будущем? Сколько времени требуется для выполнения каждой задачи? Каковы зависимости между задачами? Сколько памяти потребуется для каждой задачи? Многопроцессорный код является более масштабируемым, поскольку вы можете легко использовать кластеры или грид-решения или даже публичное облако, чтобы получить больше вычислительных ресурсов при требованию. При использовании многопроцессорности вы можете перенести свой код с работы на одной машине на несколько машин. Существуют даже некоторые решения (в частности, Incredibuild), которые позволяют масштабировать и использовать простаивающие машины и ядра в вашей сети или публичном облаке без необходимости что-либо кодировать. Таким образом, вы можете добиться масштабируемости, эффективно превратив машину, на которой выполняется ваше приложение, в суперкомпьютер с тысячами ядер и гигабайтами памяти. Думаете, что в будущем вашему программному обеспечению может потребоваться такая вычислительная мощность?

До этого мы рассматривали разницу между многопоточностью и многопроцессорностью в теории, теперь давайте рассмотрим еще реальные сценарии, такие как Maven и Make.

Maven против Make

Maven и Make - популярные и распространенные инструменты сборки. Maven обычно используется для компиляции Java, а Make в основном для компиляции C и C ++. Оба используются для создания большого проекта из небольших файлов. Оба имеют много (сотни и даже тысячи) задач компиляции, которые обычно элементарны и независимы друг от друга. Они имеют минимальную коммуникацию, а набор данных невелик.

Теперь давайте рассмотрим решение об архитектуре параллельной обработки, принятое с помощью каждого из этих инструментов. Maven является многопоточным в контексте одной сборки. Несколько лет назад он был однопоточным, но со временем стал многопоточным, что является положительным изменением, поскольку это позволяет нам использовать все восемь ядер вместо одного. Однако, кажется, что многопроцессорное выполнение было бы более выгодно для пользователей, которым необходимо увеличить скорость сборки. Почему? На то есть несколько причин:

Во-первых, разработчики, которые выполняют сборку с использованием открытого исходного кода, часто должны компилировать открытый исходный код как часть своего проекта. Следовательно, у них есть большой объем исходного кода, который одна машина, какой бы мощной она ни была, компилирует медленно. Это влияет на время компиляции, производительность и время выпуска на рынок, особенно при переходе к гибкой непрерывной интеграции (agile continuous integration).

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

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

Архитектура системы сборки Make, в отличие от Maven, является многопроцессорной. Распределение процессов может применяться только к многопроцессорным, но не к многопоточным решениям; потоки не могут быть распределены за пределами области памяти родительского процесса в многопроцессорном режиме. Каждый процесс имеет собственное пространство памяти, переменные среды и так далее. Фактически, большинство современных инструментов сборки являются многопроцессорными, а не многопоточными (например, MSBuild в Visual Studio, CMake, Scons, Ninja, JAM, JOM, WAF и многие другие). Причина в том, что он предлагает лучшее использование памяти, более простую разработку и масштабируемость.

Например, выполнение Qt на Maven или Make на восьмиядерном компьютере занимает около 16 минут. Но если вы используете многопроцессорную архитектуру, такую ​​как Make, вы можете использовать распределенные вычисления (Incredibuild), выполняя все задачи компиляции удаленно (через другие машины в сети или в общедоступном облаке), сокращая время сборки до 1 минуты 40 секунд! Подумайте об этом: 1 минута 40 секунд вместо 16 минут…

Другие сценарии

Конечно, есть и другие сценарии из реальной жизни, которые мы можем рассмотреть.

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

Другой сценарий из реальной жизни, в котором обычно используется многопоточность - это приложения реального времени, такие как финансовые транзакции, системы защиты и автомобильные устройства. Они, как правило, должны обеспечивать ответ в реальном времени, который не может ждать времени инициализации процесса.

Приложения баз данных (такие как CRM, ERP, SAP) являются еще одним примером использования в многопоточных сценариях, где большинство запросов доступны только для чтения, а меньшинство - для записи. Причина в том, что вы хотите легко предоставить общий доступ к набору данных как части пространства процессов и сделать его доступным для нескольких вычислительных потоков, работающих с ним.

Обобщая сценарии, в которых многопоточность является оптимальной, я бы сказал, что вы можете предпочесть многопоточность, когда:

  • Вы сталкиваетесь с простыми сценариями (когда нет необходимости в сложном многопроцессорном выполнении);
  • Вы работаете с приложениями реального времени;
  • Требуется длительное время инициализации (например, загрузка набора данных) при небольшом времени вычислений;
  • Есть необходимость в сложной коммуникации;
  • Ваше приложение включает в себя лишь несколько вычислительных задач.

Теперь давайте рассмотрим некоторые сценарии, в которых многопроцессорность имеет преимущество.

Один из таких сценариев - ситуация, когда у вас есть большие независимые наборы данных с низкими зависимостями между объектами данных, и вам не нужны все данные в памяти одновременно.

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

И есть сценарий, когда у вас есть много расчетов с малыми бизнес-единицами. Давайте для примера возьмем компанию Sarine Technologies. Sarine продает HW и программное обеспечение, которое запускает тонны симуляций для анализа необработанных алмазов и других драгоценных камней, чтобы найти лучший способ огранки и получения наилучшего дохода от каждого камня. Поскольку для анализа крупных алмазов могут быть выполнены миллионы независимых параллельных симуляций, наличие возможности работать в многопроцессорном режиме позволяет Sarine Technologies использовать для моделирования дополнительные вычислительные мощности в подключенных машинах в сети или в общедоступном облаке.

Потоковые сервисы также имеют тенденцию быть многопроцессорными. Например, Nvidia Shield – это приставка, которая позволяет любому человеку с комфортом играть в высококачественные игры с тяжелой графикой, используя удаленную группу высокопроизводительных компьютеров. Сервис OnLive имеет схожий характер, позволяя пользователям устанавливать приложение OnLive на любом устройстве. Netflix - это еще один пример, который не требует детализации. Когда выходные данные являются удаленными, мы всегда можем использовать балансировку нагрузки, чтобы гарантировать, что достаточно ресурсов и внутренних процессов доступно для обслуживания конечных пользователей в соответствии с динамическим спросом.

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

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

  • Может ли мое выполнение быть разбито на множество независимых задач?
  • Может ли работа быть распределена более чем на одном хосте?
  • Есть ли у меня большие наборы данных, с которыми мне нужно обрабатывать?
  • Сколько времени потребуется моим клиентам, чтобы выполнить объемный сценарий – есть ли у меня объемные сценарии?
  • Есть ли у меня какие-либо особые требования к связи и синхронизации?
  • Сложность разработки – будет ли мое программное обеспечение требовать сложных блокировок? Могу ли я этого избежать? И как часто я должен ожидать проблем, таких как сжатые сроки, проблемы со временем, нарушения совместного использования и так далее, которые сделают мою разработку и обслуживание программного обеспечения сложной и дорогостоящей?
  • Хотите ли вы масштабировать производительность обработки данных с помощью частного или публичного облака?

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

Обладая более чем 25-летним опытом управления и консультирования программных организаций, Дори Экстерман обнаружил, что начало такого обсуждения перед тем, как погрузиться в кодирование, избавит вас от многих разочарований в будущем. Это может даже повлиять на успех вашего продукта по мере его развития и усложнения, с повышением требований к вычислительным возможностям и масштабируемости.

Удачного кодирования!

Источник