Подписка
Автор: 
Татьяна Митина, сотрудник C3D Labs

Вопрос о многопоточности задают даже далёкие от разработки люди, когда речь заходит о производительности 3D-приложений, в частности систем проектирования (САПР). Поддержка многопоточности закладывается в геометрическом ядре системы. Поэтому мы решили на своём примере показать, какие механизмы для этого разработаны и как они помогают использовать многопоточные вычисления в 3D-приложении.

Этот пост подготовила Татьяна Митина, сотрудник C3D Labs, в прошлом Intel (читатели Хабра знакомы с ней по истории «Мне 57 лет, и я scrum-мастер»).

 

Модель завода с технологическим оборудованием в КОМПАС-3D ООО «ОКБ» (Новосибирск)

Модель завода с технологическим оборудованием в КОМПАС-3D ООО «ОКБ» (Новосибирск)

 

Параллельные вычисления — наше будущее.
И так будет всегда!

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

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

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

 

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

Применение технологии OpenMP для оптимизации кода ядра позволяет решить проблемы его кроссплатформенности и совместимости.

Компиляторы обеспечивают поддержку OpenMP в разной степени. Например, на данный момент компилятор Intel С++ реализует OpenMP версии 4.5 и некоторое подмножество OpenMP версии 5.0, тогда как компилятор Microsoft C++ обеспечивает поддержку только OpenMP версии 2.0.
 

Параллельные вычисления в геометрическом ядре

Основные многопоточные операции ядра C3D включают в себя:
 

  • построение плоских проекций,
  • расчет полигональных сеток,
  • расчет массо-центровочных характеристик,
  • операции конвертеров,

но не ограничиваются данным списком.

 

Время работы функции MassInertiaProperties() в разных режимах ядра на примере системы КОМПАС-3D

Время работы функции MassInertiaProperties() в разных режимах ядра на примере системы КОМПАС-3D

Реализация параллельной обработки независимых данных, как правило, относительно проста и достаточно эффективна.

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

Самым простой способ – использование блокировок, когда поток получает монопольный доступ к совместно используемым данным путём их блокирования.

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

В ядре C3D эффективность параллельных вычислений и потокобезопасность объектов обеспечивается специальным механизмом – многопоточными кэшами.

 

Потокобезопасный доступ к объектам ядра

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

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

Для решения проблемы потокобезопасного доступа к данным мы используем многопоточное кэширование.

 

Как работают многопоточные кэши

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

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

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

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

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

 

Режимы многопоточности ядра

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

Ядро C3D может работать в следующих режимах:
 

  • Режим Off – многопоточность ядра отключена. Все операции ядра выполняются последовательно. Механизм, обеспечивающий потокобезопасность объектов ядра, отключен.
  • Режим Standard – стандартный режим многопоточности. Работает ограниченное распараллеливание операций ядра (распараллеливаются только операции, обрабатывающие независимые данные). Механизм потокобезопасности объектов ядра отключен.
  • Режим SafeItems – режим потокобезопасности объектов ядра, при котором включается механизм многопоточного кэширования, но по-прежнему работает ограниченное распараллеливание операций ядра. Данный режим разработан для поддержки многопоточных операций в пользовательских приложениях.
  • Режим Items – максимальный режим многопоточности ядра, когда включен механизм многопоточного кэширования и идёт распараллеливание всех операций ядра, где вычисления могут выполняться параллельно.

У разработчиков, работающих с ядром C3D, есть возможность динамически изменять режим его многопоточности.
 

Поддержка пользовательской многопоточности

Реализация геометрического ядра ориентирована на поддержку многопоточного использования интерфейсов ядра в пользовательских приложениях.

 

Как обеспечивается потокобезопасность ядра

Все геометрические объекты ядра являются потокобезопасными при условии использования механизма многопоточного кэширования (режим многопоточности не ниже SafeItems).

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

Блокировки ядра реализованы на базе нативных механизмов синхронизации (таких, как WinAPI на Windows и pthread API на Linux), что обеспечивает безопасность использования интерфейсов ядра в пользовательских приложениях, использующих различные механизмы распараллеливания.

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

Важно! Пользовательское приложение, вызывающее интерфейсы C3D из нескольких потоков, должно уведомлять ядро о входе в параллельные вычисления и выходе из них.

 

Управление режимами многопоточности ядра

У пользователей ядра C3D есть возможность динамически менять режим многопоточности ядра.
Изменение режима многопоточности позволяет:
 

  • управлять потокобезопасностью объектов ядра (включать/выключать многопоточное кэширование)
  • определять, какие операции ядра будут распараллеливаться – все или только те, которые обрабатывают независимые данные.

Когда это может пригодиться? В некоторых случаях наличие внутренних параллельных циклов ядра может оказывать влияние на эффективность внешнего распараллеливания в пользовательском приложении.

Важно! Необходимость переключения режима многопоточности ядра надо анализировать в каждом конкретном случае.

 

Использование интерфейсов ядра в многопоточном приложении

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

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

Для этого рекомендуем один из следующих способов:
 

  • класс ParallelRegionGuard (защитник параллельного региона в области видимости)
  • функции EnterParallelRegion и ExitParallelRegion
  • макросы ENTER_PARALLEL и EXIT_PARALLEL.

 

Примеры уведомления ядра о входе в параллельный регион и выходе из него

Примеры уведомления ядра о входе в параллельный регион и выходе из него

 

Заключение

Подведем итоги. Мы активно использует внутренние многопоточные вычисления. Особенностью ядра C3D является возможность выбора режимов многопоточности при выполнении математических расчётов. Ядро предоставляет пользователям возможность динамически менять режим внутренней многопоточности ядра.

Ядро поддерживает пользовательскую многопоточность, обеспечивая потокобезопасность операций ядра и предоставляя потокобезопасный доступ к данным объектов ядра. Ядро может быть сконфигурировано под особенности организации многопоточности в пользовательском приложении.

 

Источник

 

 

 

Еще больше новостей
в нашем телеграмм-канале

 

Внимание!
Принимаем к размещению новости, статьи
или пресс-релизы с ссылками и изображениями.
ritm@gardesmash.com

 


Реклама наших партнеров