Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В течение многих лет производительность процессоров постоянно росла, и игры и другие программы пожинали преимущества этой растущей мощности, не делая ничего особенного.
Правила изменились. Производительность отдельных ядер процессора в настоящее время растет очень медленно, если вообще. Однако вычислительная мощность, доступная на типичном компьютере или консоли, продолжает расти. Разница заключается в том, что большая часть этой производительности теперь происходит от нескольких ядер процессора на одном компьютере, часто в одном чипе. ЦП Xbox 360 имеет три ядра процессора на одном чипе, и примерно 70 процентов процессоров ПК, проданных в 2006 году, были многоядерными.
Увеличение доступной мощности обработки столь же драматично, как и в прошлом, но теперь разработчикам придется писать многопоточный код, чтобы использовать эту мощность. Многопоточное программирование приносит с ним новые задачи проектирования и программирования. В этом разделе приведены некоторые рекомендации по началу работы с многопоточных программ.
Важность хорошего дизайна
Хороший многопоточный дизайн программы имеет решающее значение, но это может быть очень трудно. Если вы случайно перемещаете основные игровые системы на разные потоки, скорее всего, каждый поток тратит большую часть времени на ожидание других потоков. Этот тип проектирования приводит к увеличению сложности и значительным усилиям по отладке, при этом производительность практически не увеличивается.
Каждый раз, когда потоки должны синхронизироваться или обмениваться данными, существует риск повреждения данных, накладных расходов на синхронизацию, взаимоблокировок и возникновения сложности. Таким образом, ваш многопоточный дизайн должен четко документировать каждую точку синхронизации и связи, и он должен минимизировать такие точки как можно больше. Где потоки должны взаимодействовать, усилие по программированию увеличится, что может снизить производительность, если это влияет на слишком много исходного кода.
Простейшая цель проектирования для многопоточности заключается в том, чтобы разбить код на большие независимые части. Если вы затем ограничите эти части общением всего несколько раз за кадр, вы увидите значительное увеличение скорости от многопоточности, без чрезмерной сложности.
Типичные потоковые задачи
Некоторые типы задач доказали, что они могут быть помещены в отдельные потоки. Следующий список не предназначен для того, чтобы быть исчерпывающим, но должен дать некоторые идеи.
Рендеринг
Отрисовка, которая может включать обход графа сцены или, возможно, только вызов функций D3D, часто составляет 50 процентов или больше времени процессора. Поэтому перемещение отрисовки в другой поток может иметь значительные преимущества. Поток обновления может заполнить какой-то буфер описания отрисовки, который затем может обрабатывать поток отрисовки.
Поток обновления игры всегда на один кадр опережает поток отрисовки, что означает, что требуется два кадра, перед тем как действия пользователя отображаются на экране. Хотя эта повышенная задержка может быть проблемой, увеличение частоты кадров от разделения рабочей нагрузки обычно сохраняет общую задержку приемлемой.
В большинстве случаев все отрисовки по-прежнему выполняются в одном потоке, но он отличается от потока, в котором выполняется обновление игры.
Флаг D3DCREATE_MULTITHREADED иногда используется для отображения в одном потоке и создании ресурсов на других потоках; этот флаг игнорируется в Xbox 360, и его следует избегать использования в Windows. При указании этого флага в Windows, D3D вынуждено тратить значительное время на синхронизацию, что замедляет поток рендеринга.
Декомпрессия файла
Время загрузки всегда слишком длинное, и потоковая передача данных в память, не влияя на частоту кадров, может быть сложной задачей. Если все данные агрессивно сжимаются на диске, скорость передачи данных с жесткого диска или оптического диска менее вероятно, будет ограничивающим фактором. На однопоточном процессоре обычно недостаточно процессорного времени для компрессии, чтобы улучшить время загрузки. Однако в многопроцессорной системе распаковка файлов использует циклы ЦП, которые в противном случае будут потеряны; он улучшает время загрузки и потоковую передачу; и экономит место на диске.
Не используйте декомпрессию файлов в качестве замены для обработки, которая должна выполняться во время рабочей среды. Например, если вы выделяете дополнительный поток для анализа XML-данных во время загрузки уровня, вы не используете многопоточность для улучшения опыта игрока.
При использовании потока распаковки файлов необходимо использовать асинхронные операции ввода-вывода и большие операции чтения для повышения эффективности чтения данных.
Графические излишества
Есть много графических улучшений, которые улучшают внешний вид игры, но не являются строго необходимыми. Сюда входят процедурные анимации облаков, моделирование ткани и волос, процедурные волны, процедурная растительность, больше частиц или физика, не связанная с игровым процессом.
Так как эти эффекты не влияют на игровой процесс, они не вызывают сложные проблемы синхронизации— они могут синхронизироваться с другими потоками один раз на кадр или реже. Кроме того, в играх для Windows эти эффекты могут добавлять ценность для игроков с многоядерными процессорами, в то время как незаметно пропускаются на одноядерных компьютерах, что позволяет легко масштабировать их на широкий спектр возможностей.
Физика
Физика часто не может быть помещена в отдельный поток, чтобы работать параллельно с обновлением игры, потому что обновление игры обычно требует результатов физических вычислений немедленно. Альтернативой многопоточной физики является запуск его на нескольких процессорах. Хотя это можно сделать, это сложная задача, требующая частого доступа к общим структурам данных. Если вы можете поддерживать нагрузку физики достаточно низкой, чтобы она помещалась в основной поток, ваша работа станет проще.
Доступны библиотеки, поддерживающие выполнение физики на нескольких потоках. Тем не менее, это может привести к проблеме: когда ваша игра выполняет физические расчеты, она использует много потоков, но в остальное время она использует мало. Для выполнения физики на нескольких потоках потребуется устранить эту проблему, чтобы рабочая нагрузка распределялась равномерно по кадру. Если вы пишете многопоточный механизм физики, необходимо внимательно обратить внимание на все структуры данных, точки синхронизации и балансировку нагрузки.
Примеры многопоточных конструкций
Игры для Windows должны работать на компьютерах с разными числами ядер ЦП. Большинство игровых машин по-прежнему имеют только одно ядро, хотя число двухядерных машин быстро растет. Типичная игра для Windows может разбить рабочую нагрузку на один поток для обновления и отрисовки с дополнительными рабочими потоками для добавления дополнительных функций. Кроме того, некоторые фоновые потоки для выполнения операций ввода-вывода и сети, вероятно, будут использоваться. На рисунке 1 показаны потоки вместе с основными точками передачи данных.
Рис. 1. Проектирование потоков в игре для Windows
Типичная игра Xbox 360 может использовать дополнительные процессорозатратные программные потоки, поэтому она может разбивать рабочую нагрузку на поток обновления, поток отрисовки и три рабочих потока, как показано на Рис. 2.
Рис. 2. Проектирование потоков в игре для Xbox 360
За исключением файловых операций ввода-вывода и сетевых подключений, все эти задачи могут быть достаточно интенсивными для ЦП, чтобы воспользоваться преимуществами собственного аппаратного потока. Эти задачи также могут быть достаточно независимыми, что они могут выполняться на протяжении целого кадра без взаимодействия.
Поток обновления игры управляет входными данными контроллера, ИИ и физикой, а также подготавливает инструкции для других четырех потоков. Эти инструкции помещаются в буферы, принадлежащие потоку обновления игры, поэтому синхронизация не требуется по мере создания инструкций.
В конце кадра поток обновления игры передает буферы инструкций четырем другим потокам, а затем начинает работать над следующим кадром, заполняя другой набор буферов инструкций.
Так как потоки обновления и отрисовки работают друг с другом, их буферы обмена данными просто дважды буферизируются: в любое время поток обновления заполняет один буфер, пока поток отрисовки считывается с другого.
Другие рабочие потоки не обязательно привязаны к частоте кадров. Распаковка фрагмента данных может занять намного меньше времени, чем один кадр, или, наоборот, потребовать много кадров. Даже имитация ткани и волос может не потребоваться выполнять точно в частоте кадров, потому что менее частые обновления могут быть довольно приемлемыми. Таким образом, эти три потока нуждаются в разных структурах данных для взаимодействия с потоком обновления и потоком отрисовки. Каждому потоку требуется входящая очередь для хранения рабочих запросов, а потоку отрисовки необходима очередь для хранения результатов, полученных потоками. В конце каждого кадра поток обновления добавит блок запросов на выполнение в очереди рабочих потоков. Добавление в список только один раз на кадр гарантирует, что поток обновления минимизирует затраты на синхронизацию. Каждый из рабочих потоков извлекает назначения из рабочей очереди как можно быстрее, используя цикл, который выглядит примерно так:
for(;;)
{
while( WorkQueueNotEmpty() )
{
RemoveWorkItemFromWorkQueue();
ProcessWorkItem();
PutResultInDataQueue();
}
WaitForSingleObject( hWorkSemaphore );
}
Поскольку данные передаются из потоков обновления в рабочие потоки, а затем в поток отрисовки, может наблюдаться задержка в три или более кадра, прежде чем некоторые действия отображаются на экране. Однако если вы назначаете задачи, устойчивые к задержкам, рабочим потокам, это не должно быть проблемой.
Альтернативным вариантом было бы использование нескольких рабочих потоков, которые выбирают задания из той же рабочей очереди. Это обеспечит автоматическую балансировку нагрузки и повысит вероятность того, что все рабочие потоки останутся занятыми.
Поток обновления игры должен заботиться о том, чтобы не давать слишком много работы рабочим потокам, или в противном случае рабочие очереди могут постоянно расти. Управление этим потоком обновления зависит от того, какие задачи выполняют рабочие потоки.
Одновременная многопоточность и количество потоков
Все потоки не создаются равными. Два аппаратных потока могут находиться на отдельных микросхемах, на одном чипе или даже на одном ядре. Наиболее важной конфигурацией для программистов игр является два аппаратных потока на одном ядре — одновременная многопотоковая (SMT) или технология Hyper-Threading (технология HT).
Потоки технологии SMT или HT используют ресурсы ядра центрального процессора. Поскольку они совместно используют единицы выполнения, максимальное ускорение работы двух потоков вместо одного обычно составляет 10–20 процентов, вместо 100 процентов, которое возможно при использовании двух независимых аппаратных потоков.
Более того, потоки технологии SMT или HT разделяют инструкции и кэши данных L1. Если их шаблоны доступа к памяти несовместимы, они могут в итоге конкурировать за кэш и вызывать множество промахов кэша. В худшем случае общая производительность ядра ЦП может фактически уменьшиться при запуске второго потока. На Xbox 360 это довольно простая проблема. Конфигурация Xbox 360 известна — три ядра ЦП, каждый из которых состоит из двух аппаратных потоков, и разработчики назначают свои программные потоки определенным потокам ЦП и могут оценить, дает ли им дополнительную производительность.
В Windows ситуация сложнее. Количество потоков и их конфигурации зависит от компьютера до компьютера, и определение конфигурации сложно. Функция GetLogicalProcessorInformation предоставляет сведения о связи между разными аппаратными потоками, и эта функция доступна в Windows Vista, Windows 7 и Windows XP с пакетом обновления 3 (SP3). Таким образом, теперь необходимо использовать инструкцию CPUID и алгоритмы, предоставляемые Intel и AMD, чтобы решить, сколько "реальных" потоков у вас есть. Дополнительные сведения см. в справочниках.
Пример CoreDetection в пакете SDK DirectX содержит пример кода, который использует функцию GetLogicalProcessorInformation или инструкцию CPUID для возврата топологии ядра ЦП. Инструкция CPUID используется, если GetLogicalProcessorInformation не поддерживается на текущей платформе. CoreDetection можно найти в следующих расположениях:
-
источник :
-
корень SDK DirectX\Samples\C++\Misc\CoreDetection
-
Исполняемый файл:
-
корневой каталог пакета SDK DirectX \Samples\C++\Misc\Bin\CoreDetection.exe
Самое безопасное предположение состоит в том, чтобы на каждом ядре ЦП был не более одного потока, интенсивно использующего ЦП. Наличие более интенсивных потоков ЦП, чем ядра ЦП, дает мало или нет преимуществ, и приводит к дополнительным затратам и сложности дополнительных потоков.
Создание потоков
Создание потоков является довольно простой операцией, но существует множество потенциальных ошибок. Приведенный ниже код показывает правильный способ создания потока, ожидание его завершения и последующую очистку.
const int stackSize = 65536;
HANDLE hThread = (HANDLE)_beginthreadex( 0, stackSize,
ThreadFunction, 0, 0, 0 );
// Do work on main thread here.
// Wait for child thread to complete
WaitForSingleObject( hThread, INFINITE );
CloseHandle( hThread );
...
unsigned __stdcall ThreadFunction( void* data )
{
#if _XBOX_VER >= 200
// On Xbox 360 you must explicitly assign
// software threads to hardware threads.
XSetThreadProcessor( GetCurrentThread(), 2 );
#endif
// Do child thread work here.
return 0;
}
При создании потока можно указать размер стека дочернего потока или указать ноль, в этом случае дочерний поток наследует размер стека родительского потока. На Xbox 360, где стеки полностью фиксируются при запуске потока, указание нуля может привести к значительным затратам памяти, так как многие дочерние потоки не потребуют столько стека, сколько родительский поток. На Xbox 360 также важно, чтобы размер стека был кратным 64 КБ.
Если вы используете функцию CreateThread для создания потоков, среда выполнения C/C++ (CRT) не будет правильно инициализирована в Windows. Вместо этого рекомендуется использовать функцию CRT _beginthreadex.
Возвращаемое значение из CreateThread или _beginthreadex — это дескриптор потока. Этот поток можно использовать для ожидания завершения дочернего потока, что гораздо проще и эффективнее, чем вращение в цикле, проверяющее состояние потока. Чтобы дождаться завершения потока, просто вызовите WaitForSingleObject с дескриптором потока.
Ресурсы для потока не будут освобождены, пока поток не завершится, и дескриптор потока будет закрыт. Поэтому важно закрыть дескриптор потока с помощью CloseHandle после завершения работы с ним. Если вы ожидаете завершения потока с WaitForSingleObject, убедитесь в том, чтобы не закрывать дескриптор до окончания ожидания.
В Xbox 360 необходимо явно назначить потоки программного обеспечения определенному аппаратному потоку с помощью XSetThreadProcessor. В противном случае все дочерние потоки будут оставаться в том же аппаратном потоке, как и родительский. В Windows вы можете использовать SetThreadAffinityMask, чтобы настоятельно указать операционной системе, на каких аппаратных потоках должен выполняться ваш поток. Этот метод, как правило, следует избегать в Windows, так как вы не знаете, какие другие процессы могут выполняться в системе. Обычно лучше позволить планировщику Windows назначать ваши потоки бездействующим аппаратным потокам.
Создание потоков является дорогостоящей операцией. Создавать и уничтожать потоки следует редко. Если вы хотите часто создавать и завершать потоки, используйте пул потоков, которые находятся в состоянии ожидания работы.
Синхронизация потоков
Для совместной работы нескольких потоков необходимо синхронизировать потоки, передавать сообщения и запрашивать монопольный доступ к ресурсам. Windows и Xbox 360 предоставляют широкий набор примитивов синхронизации. Полные сведения об этих примитивах синхронизации см. в документации по платформе.
Эксклюзивный доступ
Получение эксклюзивного доступа к ресурсу, структуре данных или пути кода является распространенным необходимостью. Один из вариантов получения монопольного доступа — это мьютекс, обычное использование которого показано здесь.
// Initialize
HANDLE mutex = CreateMutex( 0, FALSE, 0 );
// Use
void ManipulateSharedData()
{
WaitForSingleObject( mutex, INFINITE );
// Manipulate stuff...
ReleaseMutex( mutex );
}
// Destroy
CloseHandle( mutex );
The kernel guarantees that, for a particular mutex, only one thread at a time can
acquire it.
The main disadvantage to mutexes is that they are relatively expensive to acquire
and release. A faster alternative is a critical section.
// Initialize
CRITICAL_SECTION cs;
InitializeCriticalSection( &cs );
// Use
void ManipulateSharedData()
{
EnterCriticalSection( &cs );
// Manipulate stuff...
LeaveCriticalSection( &cs );
}
// Destroy
DeleteCriticalSection( &cs );
Критические разделы имеют аналогичную семантику для мьютексов, но их можно использовать для синхронизации только в рамках процесса, а не между процессами. Их основное преимущество заключается в том, что они выполняют операции примерно в двадцать раз быстрее, чем мьютексы.
События
Если два потока (возможно, поток обновления и поток отрисовки) поочерёдно используют пару буферов описания отрисовки, им нужен способ указать, когда они закончили с их индивидуальным буфером. Это можно сделать, связав событие (выделенное с CreateEvent) с каждым буфером. Когда поток завершил работу с буфером, он может использовать SetEvent чтобы сигнализировать об этом, а затем может вызывать WaitForSingleObject чтобы ожидать событие другого буфера. Этот метод легко экстраполирует тройную буферизацию ресурсов.
Семафоры
Семафор используется для управления количеством потоков, которые могут выполняться, и обычно используется для реализации очередей задач. Один поток добавляет работу в очередь и использует ReleaseSemaphore при добавлении нового элемента в очередь. Это позволяет освободить один рабочий поток из пула ожидающих потоков. Рабочие потоки просто вызывают WaitForSingleObject, и когда он возвращается, они знают, что в очереди есть рабочий элемент. Кроме того, для обеспечения безопасного доступа к общей рабочей очереди необходимо использовать критически важный раздел или другой метод синхронизации.
Избегайте использования SuspendThread
Иногда, когда вы хотите приостановить выполнение потока, заманчиво использовать SuspendThread вместо корректных примитивов синхронизации. Это всегда плохая идея и может легко привести к взаимоблокировкам и другим проблемам. SuspendThread также плохо взаимодействует с отладчиком Visual Studio. Избегайте SuspendThread. Вместо этого используйте WaitForSingleObject.
WaitForSingleObject и WaitForMultipleObjects
Функция WaitForSingleObject является наиболее часто используемой функцией синхронизации. Однако иногда требуется, чтобы поток подождал несколько условий одновременно или до тех пор, пока не будет выполнен один из наборов условий. В этом случае следует использовать WaitForMultipleObjects.
Межблокированные функции и программирование без блокировки
Существует семейство функций для выполнения простых потокобезопасных операций без использования блокировок. Это семейство функций Interlocked, например InterlockedIncrement. Эти функции, а также другие методы, использующие тщательную настройку флагов, вместе называются программированием без блокировок. Программирование без блокировки может быть чрезвычайно сложно сделать правильно, и значительно сложнее на Xbox 360, чем в Windows.
Дополнительные сведения о программировании без блокировок см. в рекомендации по программированию без блокировки для Xbox 360 и Microsoft Windows.
Минимизация синхронизации
Некоторые методы синхронизации быстрее, чем другие. Однако вместо оптимизации кода, выбрав самые быстрые методы синхронизации, обычно лучше синхронизировать реже. Это происходит быстрее, чем синхронизация слишком часто, и это упрощает отладку кода.
Для правильной работы некоторых операций, таких как выделение памяти, может потребоваться использовать примитивы синхронизации. Таким образом, выполнение частых выделений из общей кучи по умолчанию приведет к частой синхронизации, что приведет к потере некоторой производительности. Избегая частых выделений или использования кучи для каждого потока (с применением HEAP_NO_SERIALIZE при использовании HeapCreate), можно избежать этой скрытой синхронизации.
Еще одна причина скрытой синхронизации — это D3DCREATE_MULTITHREADED, что приводит к тому, что D3D в Windows использует синхронизацию во многих операциях. (Флаг игнорируется на Xbox 360.)
Данные для каждого потока, также известные как локальное хранилище потоков, могут быть важным способом предотвращения синхронизации. Visual C++ позволяет объявлять глобальные переменные как однопотоковые с синтаксисом __declspec(thread).
__declspec( thread ) int tls_i = 1;
Это дает каждому потоку в процессе собственную копию tls_i, на которую можно безопасно и эффективно ссылаться, не требуя синхронизации.
Метод __declspec(thread) не работает с динамически загружаемыми библиотеками (DLL). Если вы используете динамически загруженные библиотеки DLL, необходимо использовать семейство функций TLSAlloc для реализации локального хранилища потоков.
Уничтожение потоков
Единственным безопасным способом уничтожения потока является выход потока из основной функции потока или вызов потока ExitThread или _endthreadex. Если поток создается с _beginthreadex, то он должен использовать _endthreadex или вернуться из основной функции потока, так как использование ExitThread не сможет должным образом освободить ресурсы CRT. Никогда не вызывайте функцию TerminateThread, так как поток не будет правильно очищен. Потоки всегда должны совершить самоубийство - они никогда не должны быть убиты.
OpenMP
OpenMP — это расширение языка для добавления многопоточных операций в программу с помощью pragmas, чтобы управлять компилятором в параллельных циклах. OpenMP поддерживается Visual C++ 2005 в Windows и Xbox 360 и может использоваться в сочетании с управлением потоками вручную. OpenMP может быть удобным способом многопоточности частей вашего кода, но вряд ли это идеальное решение, особенно для игр. OpenMP может быть более применимым к более длительным рабочим задачам, таким как обработка искусства и других ресурсов. Дополнительные сведения см. в документации по Visual C++ или на веб-сайте OpenMP .
Профилирование
Многопоточное профилирование важно. Легко получить длительные задержки, когда потоки ожидают друг друга. Эти поломки может быть трудно обнаружить и диагностировать. Чтобы помочь их идентификации, рассмотрите возможность добавления инструментирования в вызовы синхронизации. Профилировщик выборки также может помочь определить эти проблемы, так как он может записывать сведения о времени без существенного изменения.
Тайминг
Инструкция rdtsc — это один из способов получения точных сведений о времени в Windows. К сожалению, rdtsc имеет несколько проблем, которые делают его плохим выбором для вашего названия доставки. Счетчики rdtsc не обязательно синхронизируются между ЦП, поэтому при перемещении потока между аппаратными потоками могут возникнуть большие положительные или отрицательные различия. В зависимости от параметров управления питанием частота, с которой увеличивается счетчик rdtsc, также может измениться при запуске игры. Чтобы избежать этих трудностей, следует предпочесть QueryPerformanceCounter и QueryPerformanceFrequency для высокоточного времени в игре доставки. Дополнительные сведения о времени см. в разделе Время игры и многоядерные процессоры.
Отладка
Visual Studio полностью поддерживает многопоточную отладку для Windows и Xbox 360. Окно потоков Visual Studio позволяет переключаться между потоками, чтобы увидеть различные стеки вызовов и локальные переменные. Окно потоков также позволяет заморозить и разморозить конкретные потоки.
На Xbox 360 можно использовать мета-переменную @hwthread в окне наблюдения, чтобы отобразить аппаратный поток, на котором выполняется выбранный программный поток.
Окно потоков проще использовать, если вы назовете потоки значимым образом. Visual Studio и другие отладчики Microsoft позволяют называть потоки. Реализуйте следующую функцию SetThreadName и вызовите её из каждого потока при его запуске.
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // must be 0x1000
LPCSTR szName; // pointer to name (in user address space)
DWORD dwThreadID; // thread ID (-1 = caller thread)
DWORD dwFlags; // reserved for future use, must be zero
} THREADNAME_INFO;
void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName )
{
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = szThreadName;
info.dwThreadID = dwThreadID;
info.dwFlags = 0;
__try
{
RaiseException( 0x406D1388, 0,
sizeof(info) / sizeof(DWORD),
(DWORD*)&info );
}
__except( EXCEPTION_CONTINUE_EXECUTION ) {
}
}
// Example usage:
SetThreadName(-1, "Main thread");
Отладчик ядра (KD) и WinDBG также поддерживают многопоточную отладку.
Тестирование
Многопоточное программирование может быть сложно, и некоторые многопоточные ошибки отображаются только редко, что затрудняет поиск и исправление. Одним из лучших способов их очистки является тестирование на широком спектре компьютеров, особенно с четырьмя или более процессорами. Многопоточный код, который прекрасно работает на однопоточном компьютере, может мгновенно завершиться сбоем на четырехпроцессорном компьютере. Характеристики производительности и времени процессоров AMD и Intel могут существенно отличаться, поэтому обязательно тестируйте на многопроцессорных компьютерах на основе ЦП обоих поставщиков.
Улучшения Windows Vista и Windows 7
Для игр, предназначенных для более новых версий Windows, существует ряд API, которые могут упростить создание масштабируемых многопоточных приложений. Это особенно верно для нового API ThreadPool и некоторых дополнительных примитивов синхронизации (условные переменные, лёгкая блокировка чтения/записи и однократная инициализация). Общие сведения об этих технологиях см. в следующих статьях журнала MSDN:
- улучшить масштабируемость с помощью новых API пула потоков
- примитивы синхронизации, новые для Windows Vista
Приложения, использующие Функции Direct3D 11 в этих операционных системах, также могут воспользоваться новым дизайном для параллельного создания объектов и отложенных списков команд контекста для повышения масштабируемости многопоточной отрисовки.
Сводка
Благодаря тщательной разработке, которая сводит к минимуму взаимодействие между потоками, вы можете получить значительные преимущества производительности от многопоточного программирования, не добавляя слишком сложную работу в код. Это позволит вашему игровому коду оседлать следующую волну улучшений в технологиях процессора и предоставлять всё более захватывающие игровые впечатления.
Ссылки
- Джим Беверидж & Роберт Вейнер, Многопоточные приложения в Win32, Addison-Wesley, 1997
- Чак Вальборн, Время игры и многоядерные процессоры, Корпорация Майкрософт, 2005
- Библиотека MSDN: GetLogicalProcessorInformation
- OpenMP