Календарь на Апрель 2024 года: calendar2008.ru/2024/aprel/
Навигация
Главная »  Visual Studio 

Ускоряем OpenMP в Visual C++


Источник: habrahabr
infsega
Одним из популярных и дешёвых средств реализации многопоточных вычислений на языке C++ является OpenMP. Достоинства технологии очевидны: простота; малые, и легко отключаемые изменения в коде; поддержка от авторов самых популярных компиляторов:
  • Visual C++
  • GCC 4.2
  • Intel C++ Compiler
Проверка боем проходит успешно, и вот распараллеленный код проникает в самые укромные уголки проекта, бьёт все рекорды производительности, выдавая шикарную статистику, подтверждающую успешность релиза. Проходит пара лет, вы успешно мигрируете на Visual Studio 2010,… и обнаруживаете себя сидящим в луже. Если вчера обработка большого массива данных на машинах с многоядерными процессорами проходила за считанные секунды, то сегодня наличие любого фонового приложения, занимающего собственными вычислениями одно или несколько ядер, практически вешает приложение. Почему так происходит, и как с этим бороться?
В новой реализации OpenMP перед тем, как выйти из активной области в состояние Idle, ваша программа будет ждать завершения операций ввода/вывода, причём ждать с помощью активного SpinWait. Т.е. если мы с помощью OMP создали N потоков (по 1 на ядро), и неожиданно выяснили, что одно из ядер занято другим приложением, то с большой вероятностью на одном ядре будут выполняться 2 и больше потоков, переключение между которыми будет перемежаться 200-миллисекундными паузами. Но в рассчётных программах мы не хотим никаких дополнительных синхронизаций!
Разработчики Intel об этом знают, и предлагают пользователю самому заботиться о дополнительных ограничениях с помощью опции kmp_set_blocktime(). К сожалению, их коллеги из Microsoft решили не путать пользователей лишними настройками. Если переписывать программу на честный threading pool лень или не позволяет религия - предлагаю воспользоваться моим опытом… даунгрейда OpenMP на оригинальную версию из Microsoft Visual Studio 2005. Также инструкция подходит для Visual Studio 2008 с минимальными изменениями. Во-первых, скопируем в отдельную папку vcomp.lib, vcompd.lib из комплекта Visual C++ 2005 (можно ссылаться напрямую на установленный дистрибутив, но это не так удобно). Заходим в свойства использующих OpenMP проектов, и добавляем в "Additional Library Directories" нашу директорию. Вуаля - теперь проект линкуется с "правильной", быстро работающей версией OpenMP. Но это ещё не все. Заменим включение заголовочным файлом следующего содержания:
#pragma once  #include   #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 
Это необходимо для корректной подгрузки манифеста в исполняемых и .dll-файлах. Не забудьте, что даже если 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-файла. На мой взгляд, лучше всего для этого подойдёт утилита objconv в режиме дизассемблера. Выясняем, что нам нужно определить переменную размера byte с указанным именем. К сожалению, в 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 добавляем в Additional Input проектов, использующих OpenMP. Всё - у нас должен был получиться рабочий код. Проверить версию OpenMP можно двумя способами:
  1. запустить приложение, подцепиться отладчиком и найти в списке загруженных модулей (Debug/Windows/Modules) библиотеку OpenMP
  2. попытаться найти в исполняемых файлах подстроку vcomp100. Если всё сделано по инструкции, то этой строчки быть не должно
Приятного параллельного программирования!



 

 IBM Rational Purify PureCoverage.
 7 шагов успешной миграции портала SharePoint 2007 на SharePoint 2010.
 Редакции IBM Rational Functional Tester.
 Семь инструментов Visual Studio .NET для работы с базой данных.
 Используем фичи C# 5 (async и await) в .NET 2.0.


Главная »  Visual Studio 

© 2024 Team.Furia.Ru.
Частичное копирование материалов разрешено.