Поделиться через


Написание программного кода для многоядерных процессоров Xbox 360 и Windows

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

Правила изменились. Производительность отдельных ядер процессора в настоящее время растет очень медленно, если вообще. Однако вычислительная мощность, доступная на типичном компьютере или консоли, продолжает расти. Разница заключается в том, что большая часть этой производительности теперь происходит от нескольких ядер процессора на одном компьютере, часто в одном чипе. ЦП Xbox 360 имеет три ядра процессора на одном чипе, и примерно 70 процентов процессоров ПК, проданных в 2006 году, были многоядерными.

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

Важность хорошего дизайна

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

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

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

Типичные потоковые задачи

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

Отрисовка

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

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

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

Флаг D3DCREATE_MULTITHREADED иногда используется для отображения в одном потоке и создании ресурсов на других потоках; этот флаг игнорируется в Xbox 360, и его следует избегать использования в Windows. В Windows, указывая этот флаг, D3D заставляет D3D тратить значительное время на синхронизацию, тем самым замедляя поток отрисовки.

Распаковка файла

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

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

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

Графический пух

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

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

Физика

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

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

Примеры многопоточных конструкций

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

Рисунок 1. Проектирование потоков в игре для Windows

threading design in a game for windows

Типичная игра Xbox 360 может использовать дополнительные потоки программного обеспечения с большим объемом ЦП, поэтому она может разбить рабочую нагрузку на поток обновления, отрисовку потока и три рабочих потока, как показано на рис. 2.

Рисунок 2. Проектирование потоков в игре для Xbox 360

threading design in a game for 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 также плохо взаимодействует с отладчиком Visual Studio. Избегайте приостановки. Вместо этого используйте 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 и другие отладчики Майкрософт позволяют называть потоки. Реализуйте следующую функцию 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:

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

Итоги

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

Ссылки

  • Джим Беверидж и Роберт Вайнер, многопоточные приложения в Win32, Addison-Wesley, 1997
  • Chuck Walbourn, Game Timing and Multicore Процессоры, Корпорация Майкрософт, 2005
  • библиотека MSDN: GetLogicalProcessorInformation
  • OpenMP