Одним из популярных и дешевых способов реализации многопоточных вычислений на C++ является OpenMP .
Преимущества технологии очевидны: простота; небольшие и легко отключаемые изменения кода; поддержка от авторов самых популярных компиляторов:
- Визуальный С++
- GCC 4.2
- Intel C++-компилятор
Проходит пара лет, вы успешно мигрируете на Visual Studio 2010. и оказываетесь в луже.
Если вчера обработка большого массива данных на машинах с многоядерными процессорами происходила за считанные секунды, то сегодня наличие любого фонового приложения, занимающего одно или несколько ядер своими вычислениями, практически подвешивает приложение.
Почему это происходит и как с этим бороться? В новой реализации OpenMP ваша программа будет ждать завершения операций ввода-вывода перед выходом из активной области в состояние ожидания и ждать, используя активный SpinWait. Те.
если мы создали N потоков с помощью OMP (по 1 на ядро), и неожиданно обнаружили, что одно из ядер занято другим приложением, то с большой вероятностью на одном ядре будут выполняться 2 и более потоков, переключение между которыми будет чередоваться с паузой 200 мс.
А вот в расчетных программах нам не нужны никакие дополнительные синхронизации! Разработчики Intel знают об этом и предлагают пользователю позаботиться о дополнительных ограничениях с помощью опции kmp_set_blocktime().
К сожалению, их коллеги из Microsoft решили не смущать пользователей ненужными настройками.
Если вы ленивы или религия не позволяет переписать программу на честном пуле потоков, предлагаю воспользоваться своим опытом.
понизить OpenMP до исходной версии от Microsoft Visual Studio 2005. Также инструкция подойдет для Visual Studio 2008. с минимальными изменениями.
Для начала скопируем vcomp.lib, vcompd.lib из пакета Visual C++ 2005 в отдельную папку (можно связать напрямую с установленным дистрибутивом, но это не так удобно).
Заходим в свойства проектов, использующих OpenMP, и добавляем нашу директорию в «Каталоги дополнительных библиотек».
Вуаля — теперь проект связан с «правильной», быстродействующей версией OpenMP. Но это еще не все.
Давайте заменим включение со следующим заголовочным файлом:
Это необходимо для корректной загрузки манифеста в исполняемые и DLL-файлы.#pragma once #include <omp.h> #ifndef __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX #define __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX "Microsoft.VC80" #endif #ifndef _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN #define _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "1fc8b3b9a1e18e3b" #endif #ifndef __OMP_CRT_ASSEMBLY_VERSION #define __OMP_CRT_ASSEMBLY_VERSION "8.0.50727.762" #endif #if defined(_DEBUG) #if defined(_M_IX86) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".
DebugOpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='x86' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_AMD64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".
DebugOpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='amd64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_IA64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".
DebugOpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='ia64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #endif #else // _DEBUG #if defined(_M_IX86) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".
OpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='x86' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_AMD64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".
OpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='amd64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_IA64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".
OpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='ia64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #endif #endif // _DEBUG
Не забывайте, что даже если OpenMP используется в загружаемых файлах .
dll, для исполняемого файла также необходимо написать манифест! Значения __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX, _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN и __OMP_CRT_ASSEMBLY_VERSION взяты из включен в дистрибутив Visual C++ 2005. Если у вас другая версия (например, не установлен SP1), то указанные цифры необходимо заменить на свои значения Проект до сих пор не продвигается — теперь студия возмущается до предела, что не определен символ __You_must_link_with_Microsoft_OpenMP_library. Да, это был очень тонкий намек компилятора.
Давайте посмотрим на содержимое созданного файла .
obj. На мой взгляд, утилита подходит для этого лучше всего объектное соглашение в режиме дизассемблера.
Мы обнаруживаем, что нам нужно определить переменную размера в байтах с указанным именем.
К сожалению, в C и C++ мы не сможем точно воссоздать импортированный символ, поэтому нам придется использовать MASM32.
Добавьте бессмысленную переменную: PUBLIC __You_must_link_with_Microsoft_OpenMP_library
data segment
__You_must_link_with_Microsoft_OpenMP_library db 1
data ends
end
скомпилировать: ml /c antiomp.asm
и добавьте полученный antiomp.obj в Дополнительный ввод проектов, использующих OpenMP.
Всё — у нас должен быть работающий код. Есть два способа проверить версию OpenMP:
- запускаем приложение, поднимаем отладчик и находим библиотеку OpenMP в списке загруженных модулей (Debug|Windows|Modules)
- попробуйте найти подстроку vcomp100 в исполняемых файлах.
Если все сделано по инструкции, то этой строки быть не должно.
-
Ударять
19 Oct, 24 -
Горячие Чипы Процессора
19 Oct, 24 -
Zabbix Summit 2019: Чего Ожидать В Этом Году
19 Oct, 24 -
Топ-6 Оптимизаций Для Netty
19 Oct, 24