Пример использования сервиса PARSERPLUS

Интернет-сервис PARSERPLUS предлагает два режима работы с исходным кодом программы: автоматический и полуавтоматический. В первом случае исходный код анализируется собственным парсером, который вставляет прагмы OpenMP в явном и в закомментированном виде. Явные прагмы следует воспринимать как рекомендации к исполнению, а закомментированные прагмы скорее служат дополнительной информацией. Во многих случаях автоматически модифицированный код готов к трансляции и часто работает быстрее исходного. Единственное, о чем следует позаботиться пользователю - выполнить трансляцию с ключом использования OpenMP.

В качестве примера автоматической модификации кода приведем исходный код, реализующий простое умножение двух матриц и суммирование элементов матрицы-результата.

#include "stdafx.h"

#include <stdlib.h>

#include <time.h>

#include <omp.h>

int _tmain(int argc, _TCHAR* argv[])

{

clock_t start, finish;

double duration;

double A[200][200];

double B[200][200];

double C[200][200];

int N=200;

start = clock();

double Gs=0;

double ss=0;

for(int cycle=0;cycle<1000;cycle++)

{

for(int i=0;i<N;i++)

{

for(int j=0;j<N;j++)

{

A[i][j]=2.0*rand()/(1.0*RAND_MAX)-1.0;

B[i][j]=2.0*rand()/(1.0*RAND_MAX)-1.0;

}

}

for(int ic=0;ic<N;ic++)

{

for(int jc=0;jc<N;jc++)

{

double sum=0;

for(int isum=0;isum<N;isum++)

{

sum+=A[ic][isum]*B[isum][jc];

}

C[ic][jc]=sum;

}

}

ss=0;

for(int ic=0;ic<N;ic++)

{

for(int jc=0;jc<N;jc++)

{

ss+=C[ic][jc];

}

}

Gs+=ss;

}

finish=clock();

duration = (double)(finish - start) / CLOCKS_PER_SEC;

printf( "\nProgram takes %10.4f seconds.\n", duration );

printf(" sum=%10.4f\n",Gs);

return 0;

}

На двухъядерном компьютере с процессором Intel Core 2 Duo T6600 эта программа, скомпилированная в среде Microsoft VS 2010 с опциями по умолчанию и дополнительным ключом /openmp выполняется приблизительно за 15 секунд. После автоматической модификации кода функции, реализующей умножение двух матриц, получаем следующий код, который выполняется на той же конфигурации за 8 секунд. Модификация заключается в простом добавлении прагмы #pragma omp parallel for перед циклом умножения матриц. Модифицированный участок кода умножения матриц выглядит так:

#pragma omp parallel for

for(int ic=0;ic<N;ic++)

{

for(int jc=0;jc<N;jc++)

{

double sum=0;

for(int isum=0;isum<N;isum++)

{

sum+=A[ic][isum]*B[isum][jc];

}

C[ic][jc]=sum;

}

}

Имеется небольшая тонкость. Если переменная sum, в которую суммируются значения матрицы результата, определена внутри цикла, как сделано в исходном коде то распараллеленная программа работает корректно. Если же переменная sum определена вне цикла, то для корректной работы потребуется дополнительная прагма reduction(+:sum), которая исключает некорректное обращение к внешней переменной из разных потоков. Некорректность заключается в том, что вследствие гонки данных может нарушаться последовательность операций одного потока: чтение из оперативной памяти переменной sum и запись в регистр; сложение в регистре; запись из регистра в оперативную память. Из второго параллельного потока может записаться другое значение в регистр еще до того как результат работы первого потока запишется в память переменной.Прагма reduction(+:sum)создает локальные копии переменной sum и по окончании цикла их все суммирует.

double sum=0;

#pragma omp parallel for reduction(+:sum)

for(int ic=0;ic<N;ic++)

{

for(int jc=0;jc<N;jc++)

{

sum=0;

for(int isum=0;isum<N;isum++)

{

sum+=A[ic][isum]*B[isum][jc];

}

C[ic][jc]=sum;

}

}

Еще одна простая, но важная рекомендация заключается в предпочтительном распараллеливании внешних циклов а не внутренних. Дело в том, что операция распараллеливания связана с накладными расходами и занимает достаточно много ресурсов вычислительной системы, и если параллелить внутренний цикл, время выполнения может превысить время выполнения исходного последовательного кода. Например, следующий код, где распараллелен самый внутренний цикл, выполняется 75 секунд, что в 4.5 раза больше времени выполнения исходного последовательного кода.

for(int ic=0;ic<N;ic++)

{

for(int jc=0;jc<N;jc++)

{

double sum=0;

#pragma omp parallel for reduction(+:sum)

for(int isum=0;isum<N;isum++)

{

sum+=A[ic][isum]*B[isum][jc];

}

C[ic][jc]=sum;

}

}

Примечания.

  1. Все расчеты выполнялись на двухъядерной конфигурации под управлением Windows 7.
  2. Для достоверности результаты измерения времени получены в результате 1000-кратного повторения цикла и усреднены по большому числу повторений (см. исходный код в начале текста).
  3. Модификации с помощью сервиса PARSERPLUS подвергался только участок кода, реализующий умножение матриц.
© 2010-2012, ООО ПАРСЕР , Все права защищены.
Деловая сеть Санкт-Петербург и Ленинградская область. Жёлтые страницы, телефонный справочник и каталог компаний, товаров и услуг.
top.dp.ru
support@parserplus.com