(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

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

Источник: habrahabr
infsega

Одним из популярных и дешёвых средств реализации многопоточных вычислений на языке C++ является OpenMP.

Достоинства технологии очевидны: простота; малые, и легко отключаемые изменения в коде; поддержка от авторов самых популярных компиляторов:

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

Проходит пара лет, вы успешно мигрируете на 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.

Но это ещё не все. Заменим включение <omp.h> заголовочным файлом следующего содержания:

#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

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

Значения __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX, _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN и __OMP_CRT_ASSEMBLY_VERSION взяты из <crtassem.h>, входящего в поставку 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. Если всё сделано по инструкции, то этой строчки быть не должно

Приятного параллельного программирования!

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 21.11.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
ZBrush 4R6 Win Commercial Single License ESD
ABBYY Lingvo x6 Многоязычная Домашняя версия, электронный ключ
VMware Workstation 14 Player for Linux and Windows, ESD
Quest Software. SQL Navigator Professional Edition
TeeChart Standard VCL/FMX 2 developer license
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
СУБД Oracle "с нуля"
Новые материалы
Программирование на Visual Basic/Visual Studio и ASP/ASP.NET
Мастерская программиста
Windows и Office: новости и советы
Работа в Windows и новости компании Microsoft
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100