OpenMP и Visual Studio

OpenMP поз­во­ля­ет лег­ко со­зда­вать па­ра­л­лель­ные про­грам­мы для си­стем с об­щей па­мя­тью (мно­го­ядер­ных и мно­го­про­цес­сор­ных). В этой ста­тье я рас­ска­жу о том, как вклю­чить OpenMP в са­мой рас­про­стра­нён­ной сре­де про­грам­ми­ро­ва­ния — Visual Studio. Со­глас­но офи­ци­аль­ной вер­сии Mic­ro­soft, OpenMP под­дер­жи­ва­ет­ся толь­ко в вер­си­ях Professional сре­ды раз­ра­бот­ки Visual Studio 2005/2008/2010. Од­на­ко бес­плат­ная Visual Studio Express об­ла­да­ет тем же ком­пи­ля­то­ром, что и вер­сия Professional. По­это­му по­сле не­боль­шой «до­ра­бот­ки на­пиль­ни­ком» па­ра­л­лель­ные OpenMP-про­грам­мы бу­дут ком­пи­ли­ро­вать­ся и, глав­ное, ра­бо­тать да­же в Visual Studio Express.

OpenMP от Microsoft реа­ли­зо­ван по­сред­ством сле­дую­щих ком­по­нен­тов:

  1. ком­пи­ля­тор C++, вхо­дя­щий в со­став Visual Studio;
  2. за­го­ло­воч­ный файл omp.h;
  3. биб­лио­те­ки ста­дии ком­пи­ля­ции: vcomp.lib и vcompd.lib (по­след­няя ис­поль­зу­ет­ся для от­лад­ки);
  4. биб­лио­те­ки вре­ме­ни вы­пол­не­ния: vcomp90.dll и vcomp90d.dll. Циф­ры в на­зва­нии мо­гут раз­ли­чать­ся: в Visual Studio 2005 вме­сто 90 стоя́т циф­ры 80.

В бес­плат­ной Visual Studio Express пе­ре­чис­лен­ные биб­лио­те­ки от­сут­ству­ют.

OpenMP и Visual Studio Express

Ес­ли вы хо­ти­те со­зда­вать па­ра­л­лель­ные OpenMP-про­грам­мы под Windows, то са­мый удоб­ный спо­соб — это вос­поль­зо­вать­ся Visual Studio 2005/2008/2010 Professional. На­по­ми­наю, что она бес­плат­на для сту­ден­тов и ас­пи­ран­тов. Кро­ме то­го, вы мо­же­те ку­пить Visual Studio Professional за $600 (ко­неч­но, су­ще­ству­ет ещё и тре­тий ва­ри­ант, но мы о нём не бу­дем го­во­рить).

Ес­ли на ва­шем ком­пью­те­ре уста­нов­ле­на вер­сия Professional — пе­ре­хо­ди­те к сле­дую­ще­му раз­де­лу ста­тьи. В этом раз­де­ле рас­смот­рим слу­чай, ко­гда по ка­ким-ли­бо при­чи­нам вы вы­нуж­де­ны ис­поль­зо­вать Visual Studio Express.

Со вре­ме­ни на­пи­са­ния этой ста­тьи про­шло мно­го вре­ме­ни. Что­бы не по­те­рять ак­ту­аль­ность, ска­жу, что вы­шла вер­сия Visual Studio 2010 Express. Од­на­ко, я её по­ка не ис­пы­ты­вал.

Visual Studio Express — это бес­плат­ная уре­зан­ная вер­сия Visual Studio. Нас бу­дет ин­те­ре­со­вать вер­сия 2008 го­да. Ска­чать её мож­но от­сю­да: http://www.microsoft.com/exPress/download/. Ес­те­ствен­но, про­грам­ми­ро­вать бу­дем на C++, по­это­му вы­би­рай­те Visual C++ 2008.

Про­грам­ма уста­нов­ки бу­дет за­гру­жать дан­ные из Ин­тер­не­та (при­мер­но 100 ме­га­байт), по­это­му вы мо­же­те сэко­но­мить не­мно­го тра­фи­ка, от­клю­чив уста­нов­ку Microsoft Silverlight и Microsoft SQL Server, ес­ли они вам не тре­бу­ют­ся.

Со вре­ме­ни на­пи­са­ния этой ста­тьи вы­шел но­вый SDK: Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1. К со­жа­ле­нию, я его не ис­пы­ты­вал. Все су­ще­ствую­щие SDK пе­ре­чис­ле­ны на этой стра­ни­це.

По­сле уста­нов­ки Visual Studio Express нам по­на­до­бит­ся до­ба­вить в неё OpenMP-ком­по­нен­ты. Ле­галь­ный бес­плат­ный спо­соб сде­лать это — уста­но­вить Windows SDK for Windows Server 2008 and .NET Framework 3.5. Во вре­мя уста­нов­ки это­го па­ке­та про­грамм бу­дет про­из­ве­де­но об­нов­ле­ние Visual Studio. Про­цесс об­нов­ле­ния не смот­рит, ка­кая имен­но вер­сия Visual Studio у вас уста­нов­ле­на (Express или Professional), по­это­му во вре­мя уста­нов­ки бу­дут «слу­чай­но» до­бав­ле­ны не­до­стаю­щие ком­по­нен­ты.

Так как мы ста­вим Windows SDK толь­ко ра­ди OpenMP, то нам не ну­жен тот ги­га­байт до­ку­мен­та­ции, ко­то­рый идёт в ком­плек­те. Ре­ко­мен­дую оста­вить толь­ко сле­дую­щие эле­мен­ты:

Не­об­хо­ди­мые нам ком­по­нен­ты SDK

Ри­су­нок 1. Не­об­хо­ди­мые нам ком­по­нен­ты SDK

К со­жа­ле­нию, в со­став SDK не вхо­дит биб­лио­те­ка vcomp90d.dll, по­это­му на дан­ный мо­мент в Visual Studio Express вы смо­же­те за­пус­кать толь­ко OpenMP-про­грам­мы, от­ком­пи­ли­ро­ван­ные в ре­жи­ме Release. Я на­шёл спо­соб обой­ти и это огра­ни­че­ние, об этом чи­тай­те да­лее (раз­дел «От­лад­ка OpenMP-про­грам­мы в Visual Studio Express»).

Ис­поль­зо­ва­ние OpenMP в Visual Studio

По­сле то­го, как вы вы­пол­ни­ли ша­ги, опи­сан­ные в преды­ду­щем раз­де­ле, уже не важ­но, ка­кой вер­си­ей Visual Studio вы поль­зу­е­тесь. По­ка­жу шаг за ша­гом, как со­здать про­ект с под­держ­кой OpenMP в этой сре­де раз­ра­бот­ки. Пре­жде все­го, нуж­но за­пу­стить Visual Studio, и вы­брать File →​ New → ​Project... По­явит­ся ок­но со­зда­ния про­ек­та. Вы­бе­ри­те тип про­ек­та «Win32», шаб­лон — «Win32 Console Application». Вве­ди­те осмыс­лен­ное имя про­ек­та, вы­бе­ри­те пап­ку для хра­не­ния про­ек­та, убе­ри­те га­лоч­ку «Create directory for solution»:

Ок­но со­зда­ния про­ек­та
Ок­но со­зда­ния про­ек­та

Ри­су­нок 2. Ок­но со­зда­ния про­ек­та

На­жми­те кноп­ку «OK», по­явит­ся ок­но на­строй­ки бу­ду­ще­го про­ек­та. Вы­бе­ри­те вклад­ку «Application Settings», и вклю­чи­те гал­ку «Empty project»:

Ок­но на­строй­ки бу­ду­ще­го про­ек­та
Ок­но на­строй­ки бу­ду­ще­го про­ек­та

Ри­су­нок 3. Ок­но на­строй­ки бу­ду­ще­го про­ек­та

По на­жа­тию кноп­ки «Finish» про­ект бу­дет со­здан. Ни­ка­ких ви­ди­мых из­ме­не­ний в глав­ном ок­не Visual Studio не про­изой­дёт. Толь­ко имя про­ек­та в за­го­лов­ке ок­на как бы го­во­рит нам о том, что мы ра­бо­та­ем с про­ек­том.

Те­перь на­жми­те Project → Add New Item, по­явит­ся ок­но до­бав­ле­ния эле­мен­тов в про­ект. До­бавь­те .cpp-файл в про­ект:

Ок­но до­бав­ле­ния эле­мен­тов в про­ект
Ок­но до­бав­ле­ния эле­мен­тов в про­ект

Ри­су­нок 4. Ок­но до­бав­ле­ния эле­мен­тов в про­ект

По­сле это­го вам бу­дет предо­став­ле­но ок­но для вво­да ис­ход­но­го ко­да про­грам­мы. Бу­дем вы­пол­нять те­сты на сле­дую­щем ко­де, про­ве­ряю­щем раз­лич­ные ас­пек­ты функ­цио­ни­ро­ва­ния OpenMP:

#include <iostream>
#include <omp.h>

using namespace std;

int main(int argc, char **argv)
{
    int test( 999 );

    omp_set_num_threads( 2 );
    #pragma omp parallel reduction(+:test)
    {
        #pragma omp critical
        cout << "test = " << test << endl;
    }

    return EXIT_SUCCESS;
}

Ли­стинг 1. Про­стей­шая про­грам­ма, ис­поль­зую­щая OpenMP

За­пу­сти­те про­грам­му, на­жав Debug → Start Without Debugging. Ес­ли всё бы­ло сде­ла­но пра­виль­но, про­грам­ма от­ком­пи­ли­ру­ет­ся (ес­ли спро­сит вас, ком­пи­ли­ро­вать ли, на­жми­те «Yes»), за­тем за­пу­стит­ся и вы­ве­дет test = 999:

Ре­зуль­тат ра­бо­ты про­грам­мы из ли­стин­га 1
Ре­зуль­тат ра­бо­ты про­грам­мы из ли­стин­га 1

Ри­су­нок 5. Ре­зуль­тат ра­бо­ты про­грам­мы из ли­стин­га 1

«Как же так?! — ска­же­те вы — Ведь про­грам­ма долж­на бы­ла вы­ве­сти ноль, при­чём два­жды!». Де­ло в том, что OpenMP ещё не вклю­чен, и по­это­му со­от­вет­ствую­щие ди­рек­ти­вы бы­ли про­игно­ри­ро­ва­ны ком­пи­ля­то­ром.

Для вклю­че­ния OpenMP на­жми­те Project → OMP Properties (OMP — имя про­ек­та из мо­их при­ме­ров). Сле­ва ввер­ху по­явив­ше­го­ся ок­на вы­бе­ри­те «All Configurations» и в раз­де­ле Configuration Properties → C/C++ → Language вклю­чи­те «OpenMP Support»:

Вклю­ча­ем OpenMP в свой­ствах про­ек­та

Ри­су­нок 6. Вклю­ча­ем OpenMP в свой­ствах про­ек­та

По­сле это­го сно­ва за­пу­сти­те про­грам­му, на­жав Debug → Start Without Debugging. На этот раз про­грам­ма вы­ве­дет test = 0 два­жды:

Ре­зуль­тат ра­бо­ты про­грам­мы из ли­стин­га 1 с вклю­чён­ным OpenMP
Ре­зуль­тат ра­бо­ты про­грам­мы из ли­стин­га 1

Ри­су­нок 7. Ре­зуль­тат ра­бо­ты про­грам­мы из ли­стин­га 1 с вклю­чён­ным OpenMP

Ура! OpenMP ра­бо­та­ет.

При­ме­ча­ние. Ес­ли вы ис­поль­зу­е­те Visual Studio Express, то вы­бе­ри­те те­ку­щую кон­фи­гу­ра­цию «Release», ина­че ра­бо­тать не бу­дет (чи­тай­те да­лее):

Вы­бор те­ку­щей кон­фи­гу­ра­ции

Ри­су­нок 8. Вы­бор те­ку­щей кон­фи­гу­ра­ции

От­лад­ка OpenMP-про­грам­мы в Visual Studio Express

Как бы­ло ска­за­но ра­нее, да­же по­сле уста­нов­ки Windows SDK у нас не бу­дет в на­ли­чии не­об­хо­ди­мой для от­лад­ки биб­лио­те­ки vcomp90d.dll, по­это­му мы по­ка не мо­жем от­ла­жи­вать OpenMP про­грам­му в Visual Studio Express. Про­стое ко­пи­ро­ва­ние имею­щей­ся биб­лио­те­ки vcomp90.dll и пе­ре­име­но­ва­ние её в vcomp90d.dll не сра­бо­та­ет, ибо не сов­па­дёт кон­т­роль­ная сум­ма и вер­сия, ука­зан­ные во встраи­ва­е­мом в exe-файл ма­ни­фе­сте. По­это­му бу­дем «ко­пать» с про­ти­во­по­лож­ной сто­ро­ны.

При ком­пи­ля­ции в кон­фи­гу­ра­ции «Debug» («От­лад­ка»), за­го­ло­воч­ный файл omp.h тре­бу­ет биб­лио­те­ку vcompd.lib (она у нас име­ет­ся), ко­то­рая, в свою оче­редь, тре­бу­ет vcomp90d.dll (от­сут­ству­ет). Ли­цен­зия не поз­во­ля­ет нам ис­поль­зо­вать в при­ло­же­ни­ях мо­дифи­ци­ро­ван­ные за­го­ло­воч­ные фай­лы от Microsoft, по­это­му вме­сто мо­дифи­ка­ции omp.h вклю­чим его в на­шу про­грам­му сле­дую­щим об­ра­зом, что­бы он не до­га­дал­ся о вклю­чён­ном ре­жи­ме от­лад­ки:

#include <iostream>

#ifdef _DEBUG
#undef _DEBUG
#include <omp.h>
#define _DEBUG
#else
#include <omp.h>
#endif

using namespace std;

int main(int argc, char **argv)
{
    int test( 999 );

    omp_set_num_threads( 2 );
    #pragma omp parallel reduction(+:test)
    {
        #pragma omp critical
        cout << "test = " << test << endl;
    }

    return EXIT_SUCCESS;
}

Ли­стинг 2. Вклю­ча­ем omp.h «хит­рым» спо­со­бом

При­ве­дён­но­го дей­ствия не до­ста­точ­но для то­го, что­бы всё ра­бо­та­ло (по­ка мы ис­пра­ви­ли лишь ма­ни­фест, встраи­вае­мый в про­грам­му). Де­ло в том, что Visual Studio в ре­жи­ме от­лад­ки по преж­не­му ав­то­ма­ти­че­ски (из-за вклю­чён­но­го OpenMP) при­лин­ко­вы­ва­ет vcompd.lib, тре­бу­ю­щую vcomp90d.dll. Что­бы это ис­пра­вить, сно­ва зай­ди­те в на­строй­ки про­ек­та (Project → OMP Properties), вы­бе­ри­те на этот раз Configuration: «Debug». В раз­де­ле Configuration Properties → Linker → Input ука­жи­те, что vcompd.lib при­лин­ко­вы­вать не нуж­но, а vcompd.lib — нуж­но:

За­ме­ня­ем биб­лио­те­ку в свой­ствах про­ек­та

Ри­су­нок 9. За­ме­ня­ем биб­лио­те­ку в свой­ствах про­ек­та

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

Точ­ка оста­но­ва

Ри­су­нок 10. Точ­ка оста­но­ва

По­сле это­го за­пу­сти­те про­грам­му в ре­жи­ме от­лад­ки: Debug → Start Debugging (не за­будь­те вер­нуть те­ку­щую кон­фи­гу­ра­цию «Debug», см. ри­су­нок 8). Про­грам­ма за­пу­стит­ся — и сра­зу же оста­но­вит­ся на точ­ке оста­но­ва. Во вклад­ке «Threads» мы ви­дим, что про­грам­ма дей­стви­тель­но ра­бо­та­ет, ис­поль­зуя два по­то­ка:

От­лад­ка OpenMP-про­грам­мы в Visual Studio Express
От­лад­ка OpenMP-про­грам­мы в Visual Studio Express

Ри­су­нок 11. От­лад­ка OpenMP-про­грам­мы в Visual Studio Express

P.S.

Ес­ли вы не зна­е­те, как со­зда­вать па­ра­л­лель­ные про­грам­мы при по­мо­щи OpenMP, со­ве­тую про­честь спе­цифи­ка­цию, там всё по­дроб­но опи­са­но: http://www.openmp.org/mp-documents/spec30.pdf.