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


64-разрядное программирование для разработчиков игр

Производители процессоров исключительно поставляемые 64-разрядные процессоры на своих настольных компьютерах, и даже наборы микросхем большинства ноутбуков поддерживают технологию x64. Разработчикам игр важно воспользоваться преимуществами 64-разрядных процессоров в новых приложениях и обеспечить правильную работу предыдущих приложений на новых процессорах и 64-разрядных выпусках Windows Vista и Windows 7. В этой статье рассматриваются проблемы совместимости и переноса, которые помогают разработчикам упростить переход на 64-разрядные платформы.

В настоящее время корпорация Майкрософт имеет следующие 64-разрядные операционные системы:

  • Windows 10
  • Windows 11
  • Windows Server 2019 или более поздней версии;

Предыдущие 64-разрядные операционные системы:

  • Windows Server 2003 с пакетом обновления 1
  • Windows XP Professional x64 Edition (доступно для изготовителей оборудования и разработчиков через MSDN)
  • Windows Vista
  • Windows 7
  • Windows 8.0
  • Windows 8.1
  • Windows Server 2008 – 2016

Примечание

Windows Server 2008 R2 или более поздней версии доступен только в виде 64-разрядной версии. Windows 11 доступен только в виде 64-разрядной версии или ARM64.

 

Различия в адресируемой памяти

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

  • 32-разрядные приложения на 32-разрядных платформах могут содержать до 2 ГБ.

  • 32-разрядные приложения, созданные с флагом компоновщика /LARGEADDRESSAWARE:YES в 32-разрядной версии Windows XP или Windows Server 2003 со специальным параметром загрузки /3GB, могут содержать до 3 ГБ. Это ограничивает ядро размером только 1 ГБ, что может привести к сбою некоторых драйверов или служб.

  • 32-разрядные приложения, созданные с флагом компоновщика /LARGEADDRESSAWARE:YES в 32-разрядных выпусках Windows Vista, Windows Server 2008 и Windows 7, могут обращаться к памяти до числа, указанного элементом данных конфигурации загрузки (BCD) IncreaseUserVa. Параметр IncreaseUserVa может иметь значение в диапазоне от 2048 по умолчанию до 3072 (что соответствует объему памяти, настроенной параметром загрузки /3 ГБ в Windows XP). Оставшиеся 4 ГБ выделяются ядру и могут привести к сбою конфигураций драйверов и служб.

    Дополнительные сведения о BCD см. в разделе Данные конфигурации загрузки.

  • 32-разрядные приложения на 64-разрядных платформах могут адресовать до 2 ГБ или до 4 ГБ с флагом компоновщика /LARGEADDRESSAWARE:YES.

  • 64-разрядные приложения используют 43 бита для адресации, что обеспечивает 8 ТБ виртуального адреса для приложений и 8 ТБ, зарезервированных для ядра.

Помимо только памяти, 64-разрядные приложения, использующие сопоставленные с памятью операции ввода-вывода, значительно выигрывают от увеличения виртуального адресного пространства. 64-разрядная архитектура также повысила производительность с плавающей запятой и ускорила передачу параметров. 64-разрядные процессоры имеют удвоение числа регистров как общего назначения, так и потоковых расширений SIMD (SSE), а также поддержку наборов инструкций SSE и SSE2; многие 64-разрядные процессоры даже поддерживают наборы инструкций SSE3.

Указание больших адресов при сборке

Рекомендуется указывать большие адреса при создании 32-разрядных приложений с помощью флага компоновщика /LARGEADDRESSAWARE, даже если приложение не предназначено для 64-разрядной платформы из-за преимуществ, которые получаются бесплатно. Как упоминалось ранее, включение этого флага для сборки позволяет 32-разрядной программе получать доступ к большему объему памяти с помощью специальных параметров загрузки в 32-разрядной или 64-разрядной ОС. Однако разработчики должны быть осторожны, чтобы не были сделаны предположения указателя, например, предполагая, что высокий бит никогда не устанавливается в 32-разрядном указателе. Как правило, рекомендуется включить флаг /LARGEADDRESSAWARE.

32-разрядные приложения с поддержкой больших адресов могут определить во время выполнения, сколько общего виртуального адресного пространства доступно им с текущей конфигурацией ОС, вызвав GlobalMemoryStatusEx. Результат ullTotalVirtual будет варьироваться от 2147352576 байтов (2 ГБ) до 4294836224 байтов (4 ГБ). Значения, превышающие 3221094400 (3 ГБ), можно получить только в 64-разрядных выпусках Windows. Например, если параметр IncreaseUserVa имеет значение 2560, результатом будет ullTotalVirtual со значением 2684223488 байтов.

Совместимость 32-разрядных приложений на 64-разрядных платформах

64-разрядные операционные системы Windows совместимы с архитектурой IA32, а большинство ИНТЕРФЕЙСов API, используемых в 32-разрядных приложениях, доступны через 32-разрядную версию Windows в 64-разрядном эмуляторе Windows WOW64. WOW64 помогает гарантировать, что эти API будут работать должным образом.

WOW64 имеет уровень выполнения, который обрабатывает маршалирование 32-разрядных данных. WOW64 перенаправляет запросы dll-файлов, некоторые ветви реестра для 32-разрядных приложений и отражает некоторые ветви реестра для 32- и 64-разрядных приложений.

Дополнительные сведения о WOW64 см. в разделе Сведения о реализации WOW64.

Потенциальные проблемы совместимости

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

  • Все драйверы для 64-разрядных выпусков операционных систем Windows должны быть 64-разрядными версиями. Требование новых 64-разрядных драйверов влияет на схемы защиты от копирования, основанные на старых драйверах. Обратите внимание, что для загрузки 64-разрядными выпусками Windows драйверы в режиме ядра должны иметь подпись Authenticode.
  • 64-разрядные процессы не могут загружать 32-разрядные библиотеки DLL, а 32-разрядные процессы не могут загружать 64-разрядные библиотеки DLL. Прежде чем продолжить разработку, разработчики должны обеспечить доступность 64-разрядных версий сторонних библиотек DLL. Если в 64-разрядном процессе необходимо использовать 32-разрядную библиотеку DLL, можно использовать межпроцессное взаимодействие Windows (IPC). Com-компоненты также могут использовать внепроцессные серверы и маршалирование для обмена данными между границами, но это может привести к снижения производительности.
  • Многие процессоры x64 также являются многоядерными, и разработчикам необходимо проверить, как это влияет на их устаревшие приложения. Дополнительные сведения о многоядерных процессорах и последствиях для игровых приложений можно найти в разделе Game Timing и Многоядерные процессоры.
  • Приложения также должны вызывать SHGetFolderPath для обнаружения путей к файлам, так как некоторые имена папок в некоторых случаях изменились. Например, CSIDL_PROGRAM_FILES возвращает "C:\Program Files(x86)" для 32-разрядного приложения, работающего на 64-разрядной платформе, а не "C:\Program Files". Разработчики должны помнить о том, как работают возможности перенаправления и отражения эмулятора WOW64.

Кроме того, разработчики должны быть осторожны с 16-разрядными программами, которые они могут по-прежнему использовать. WOW64 не может обрабатывать 16-разрядные приложения; сюда входят старые установщики и все программы MS-DOS.

Примечание

Наиболее распространенными проблемами совместимости являются установщики, которые выполняют 16-разрядный код и не имеют 64-разрядных драйверов для схем защиты от копирования.

 

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

Перенос приложений на 64-разрядные платформы

Наличие правильных инструментов и библиотек поможет упростить переход от 32-разрядной к 64-разрядной разработке. Пакет SDK для DirectX 9 содержит библиотеки для поддержки проектов на основе x86 и x64. Microsoft Visual Studio 2005 и Visual Studio 2008 поддерживают создание кода как для x86, так и для 64-разрядных систем и поступают с библиотеками, оптимизированными для создания кода x64. Однако разработчикам также потребуется распространять среды выполнения Visual C со своими приложениями. Обратите внимание, что экспресс-выпуски Visual Studio 2005 и Visual Studio 2008 не включают компилятор x64, но все это делают выпуски Standard, Professional и Team System.

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

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

Примечание

Visual C++ 2010 или более поздней версии включает заголовки stdint.h и cstdint C99, которые определяют стандартные типы переносимости int32_t, uint32_t, int64_t, uint64_t, intptr_t и uintptr_t. Использование этих типов вместе со стандартными ptrdiff_t и size_t типами данных может быть предпочтительнее типов windows portabilty, используемых ниже для повышения переносимости кода.

 

Ниже перечислены основные проблемы с переносом.

Усечение указателя

Указатели являются 64-разрядными в 64-разрядной ОС, поэтому приведение указателей к другим типам данных может привести к усечению, а арифметика указателя может привести к повреждению. Использование флага /Wp64 обычно выдает предупреждение об этой проблеме, но рекомендуется использовать полиморфные типы (INT_PTR, DWORD_PTR, SIZE_T, UINT_PTR и т. д.) при приведение типов указателей. Так как указатели являются 64-разрядными на новых платформах, разработчикам следует проверка порядок указателей и типы данных в классах и структурах, чтобы уменьшить или исключить заполнение.

Типы данных и двоичные файлы

Хотя число указателей увеличивается с 32 бит до 64 на 64-разрядной платформе, другие типы данных этого не поддерживают. Типы данных с фиксированной точностью (DWORD32, DWORD64, INT32, INT64, LONG32, LONG64, UINT32, UINT64) можно использовать в местах, где размер типа данных должен быть известен; например, в двоичной структуре файлов. Изменения размера указателя и выравнивания данных требуют специальной обработки для обеспечения совместимости между 32-разрядными и 64-разрядными. Дополнительные сведения см. в статье Новые типы данных.

Старые API Win32 и выравнивание данных

Некоторые API Win32 устарели и заменены более нейтральными вызовами API, такими как SetWindowLongPtr вместо SetWindowLong.

Снижение производительности для несвязанных обращений выше на платформе x64, чем на платформе x86. Макросы TYPE_ALIGNMENT(t) и FIELD_OFFSET(t, член) можно использовать для определения сведений о выравнивании, которые могут использоваться непосредственно в коде. Правильное использование этих вышеупомянутых макросов должно исключить потенциальные штрафы за несоотвязанный доступ.

Дополнительные сведения о макросе TYPE_ALIGNMENT, макросе FIELD_OFFSET и общих сведениях о 64-разрядном программировании см. в статье 64-разрядное программирование Windows: советы по миграции: дополнительные рекомендации и правила использования указателей.

Код сборки

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

X87, MMX и 3DNow! Наборы инструкций являются устаревшими в 64-разрядных режимах. Наборы инструкций по-прежнему существуют для обеспечения обратной совместимости в 32-разрядном режиме; однако во избежание проблем совместимости в будущем их использование в текущих и будущих проектах не рекомендуется.

Нерекомендуемые API

Некоторые старые API DirectX были удалены для 64-разрядных собственных приложений: DirectPlay 4 и более ранних версий, DirectDraw 6 и более ранних версий, Direct3D 8 и более ранних версий, а также DirectInput 7 и более ранних версий. Кроме того, основной API DirectMusic доступен для собственных 64-разрядных приложений, но уровень производительности и DirectMusic Producer являются устаревшими.

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

Профилирование и оптимизация перенесенных приложений

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

В некоторых структурах внутренние типы данных могут быть переупорядочены для экономии места в памяти и улучшения кэширования. В некоторых случаях индексы массива можно использовать вместо полного 64-разрядного указателя. Флаг /fp:fast может улучшить оптимизацию и векторизацию с плавающей запятой. Использование __restrict, declspec(restrict) и declspec(noalias) может помочь компилятору разрешить псевдонимы и улучшить использование файла регистра.

Дополнительные сведения о /fp:fast см. в разделе /fp (указание Floating-Point поведения).

Дополнительные сведения о __restrict см. в статье Модификаторы, относящиеся к Майкрософт.

Дополнительные сведения о declspec(restrict) см. в статье Рекомендации по оптимизации.

Дополнительные сведения о declspec(noalias) можно найти по адресу __declspec(noalias).

Управляемый код в 64-разрядной операционной системе

Управляемый код используется многими разработчиками игр в их цепочке инструментов, поэтому понимание того, как он ведет себя в 64-разрядной ОС, может быть полезным. Управляемый код не зависит от набора инструкций, поэтому при запуске управляемого приложения в 64-разрядной ОС среда CLR может запускать его как 32-разрядный или 64-разрядный процесс. По умолчанию среда CLR запускает управляемые приложения как 64-разрядные, и они должны работать нормально без проблем. Однако если приложение зависит от собственной 32-разрядной библиотеки DLL, приложение завершится ошибкой при попытке вызвать эту библиотеку DLL. Для 64-разрядного процесса требуется полностью 64-разрядный код, а 32-разрядная библиотека DLL не может быть вызвана из 64-разрядного процесса. Лучшим долгосрочным решением является компиляция машинного кода как 64-разрядного, но вполне разумным краткосрочным решением является просто пометить управляемое приложение как для x86 только с помощью флага сборки /platform:x86.

Влияние на производительность при запуске 64-разрядной операционной системы

Так как процессоры с архитектурой AMD64 и Intel 64 могут выполнять 32-разрядные инструкции изначально, они могут запускать 32-разрядные приложения на полной скорости даже в 64-разрядной ОС. Существует небольшая стоимость преобразования параметров между 32-разрядными и 64-разрядными при вызове функций операционной системы, но эти затраты, как правило, незначительны. Это означает, что при запуске 32-разрядных приложений в 64-разрядной ОС не должно наблюдаться замедление.

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

Сводка

Шестьдесят четыре битовые архитектуры позволяют разработчикам накладывать ограничения на внешний вид, звук и игру игр. Однако переход от 32-разрядного программирования к 64-разрядному программированию не является тривиальным. Благодаря пониманию различий между ними и использованию новейших средств переход на 64-разрядные платформы может быть проще и быстрее.

Дополнительные сведения о 64-разрядном программировании см. в разделе Центр разработчиков Visual C++: 64-разрядное программирование.