Параллелизм и OpenGL

Параллелизм является понятием многократных вещей, происходящих одновременно. В контексте компьютеров параллелизм обычно относится к выполняющимся задачам больше чем на одном процессоре одновременно. Путем выполнения работы параллельно, задачи, завершенные раньше, и приложения, становятся более быстро реагирующими пользователю. Хорошие новости - то, что хорошо разработанные приложения OpenGL уже показывают определенную форму параллелизма — параллелизм между обработкой приложения на CPU и обработкой OpenGL на GPU. Многие методы, представленные в Стратегиях Проектирования приложений OpenGL, нацелены в частности на создавание приложений OpenGL, показывающих большой параллелизм CPU-GPU. Однако современные компьютеры не только содержат мощный GPU, но также и содержат многократный CPUs. Иногда тот CPUs имеет многократные ядра, каждый способный к выполнению вычислений независимо от других. Критически важно, что приложения разработаны для использования в своих интересах параллелизма, если это возможно. Разработка параллельного приложения означает анализировать работу, которую Ваше приложение выполняет в подзадачи и идентификацию, которой задачи могут безопасно управлять параллельно и какие задачи должны быть выполнены последовательно — т.е. какие задачи зависят или от ресурсов, используемых другими задачами или от результатов, возвращенных из тех задач.

Каждый процесс в OS X составлен из одного или более потоков. Поток является потоком выполнения, выполняющего код для процесса. Многожильные системы предлагают истинный параллелизм, позволяя многократным потокам выполниться одновременно. Apple предлагает и традиционные потоки и функцию под названием Grand Central Dispatch (GCD). Центральная Отгрузка позволяет Вам анализировать свое приложение в меньшие задачи, не требуя, чтобы приложение управляло потоками. GCD выделяет потоки на основе числа ядер, доступных в системе, и автоматически планирует задачи к тем потокам.

В более высоком уровне, предложениях Какао NSOperation и NSOperationQueue обеспечить абстракцию Objective C для создания и планирования единиц работы. На OS X v10.6, очереди работы используют GCD для диспетчеризации работы; на OS X v10.5, очереди работы создают потоки для выполнения задач приложения.

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

Идентификация, может ли приложение OpenGL получить преимущества от параллелизма

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

Хороший кандидат имеет или или обе из следующих характеристик:

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

Для получения дополнительной информации о том, как определить, где Ваше приложение проводит свое время, посмотрите Настройку Вашего Приложения OpenGL.

OpenGL ограничивает каждый контекст единственным потоком

Каждый поток в процессе OS X имеет единственный текущий контекст рендеринга OpenGL. Каждый раз, когда Ваше приложение вызывает функцию OpenGL, OpenGL неявно ищет контекст, связанный с текущим потоком, и изменяет состояние или объекты, связанные с тем контекстом.

OpenGL не повторно используем. При изменении того же контекста от многократных потоков одновременно результаты непредсказуемы. Ваше приложение могло бы отказать, или оно могло бы представить неправильно. Если по некоторым причинам Вы решаете установить больше чем один поток для предназначения для того же контекста, то необходимо синхронизировать потоки путем размещения взаимного исключения вокруг всех вызовов OpenGL к контексту, такой как gl* и CGL*. Команды OpenGL, блокирующие — такой как fence команды — не синхронизируют потоки.

GCD и NSOperationQueue объекты могут оба выполнить Ваши задачи на потоке их выбора. Они могут создать поток в частности для той задачи, или они могут снова использовать существующий поток. Но в любом случае, Вы не можете гарантировать, какой поток выполняет задачу. Для приложения OpenGL, означающего:

Стратегии реализации параллелизма в приложениях OpenGL

Параллельное приложение OpenGL хочет фокусироваться на параллелизме CPU так, чтобы OpenGL мог обеспечить больше работы для GPU. Вот несколько рекомендуемых стратегий реализации параллелизма в приложении OpenGL:

Многопоточный OpenGL

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

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

Перечисление 14-1 показывает код, требуемый включить многопоточный механизм OpenGL.

Перечисление 14-1  , Разрешающее многопоточный механизм OpenGL

CGLError err = 0;
CGLContextObj ctx = CGLGetCurrentContext();
 
// Enable the multithreading
err =  CGLEnable( ctx, kCGLCEMPEngine);
 
if (err != kCGLNoError )
{
     // Multithreaded execution may not be available
     // Insert your code to take appropriate action
}

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

Выполните вычисления OpenGL в задаче работника

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

Подход описал в альтернативах рисунка 9-3 между обновлением объектов OpenGL и выполнением команд рендеринга, использующих те объекты. OpenGL представляет на GPU параллельно с обновлениями Вашего приложения, работающими на CPU. Если вычисления, выполняемые на CPU, занимают больше времени обработки, чем те на GPU, то GPU проводит больше неактивного времени. В этой ситуации можно быть в состоянии использовать в своих интересах параллелизм в системах с многократным CPUs. Разделите свой код рендеринга OpenGL на отдельное вычисление и обработку задач, и выполните их параллельно. Рисунок 14-1 показывает ясное разделение труда. Одна задача производит данные, использующиеся вторым и представленным к OpenGL.

Рисунок 14-1  обработка CPU и OpenGL на отдельных потоках
CPU processing and OpenGL on separate threads

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

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

  1. Установите текущий контекст.

  2. Отобразите первый буфер.

  3. Создайте NSOperation возразите, чья задача состоит в том, чтобы заполнить тот буфер.

  4. Очередь, что работа на очереди работы.

  5. Выполните шаги 2 - 4 для других буферов.

  6. Вызвать waitUntilAllOperationsAreFinished на очереди работы.

  7. Не отобразите буферы.

  8. Выполните команды рендеринга.

В многожильной системе многократные потоки выполнения могут позволить буферам быть заполненными одновременно. Шаги 7 и 8 могли даже быть выполнены отдельной работой, поставленной в очередь на ту же очередь работы, при условии, что работа установила надлежащие зависимости.

Используйте многократные контексты OpenGL

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

Специфичный для Apple OpenGL APIs также предоставляет возможность для совместного использования данных между контекстами, как показано на рисунке 14-2. Совместно используемые ресурсы автоматически устанавливаются как взаимное исключение (взаимное исключение) объекты. Заметьте, что распараллеливают 2, рисует к пиксельному буферу, соединяющемуся с общим состоянием как текстура. Поток 1 может тогда нарисовать использование та текстура.

Рисунок 14-2  Два контекста на отдельных потоках
Vertex and texture processing on separate threads

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

Инструкции для поточной обработки приложений OpenGL

Следуйте этим инструкциям для обеспечения успешной поточной обработки в приложении, использующем OpenGL: