Это мой первый вопрос. Я пытаюсь распараллелить с openMP функцию преобразования 2d haar в C. Я получил ее здесь и соответствующим образом изменен. Программа берет черно-белое изображение, помещает его в матрицу и вычисляет один уровень вейвлет-преобразования Хаара. В конце концов, он нормализует значения и записывает преобразованное изображение на диск.
Это результирующее изображение 1 уровня HDT
Моя проблема в том, что распараллеленная версия работает медленнее, чем последовательная. А пока я прилагаю отрывок из основной части, которую хочу распараллелить (позже я могу поместить весь окружающий код):
void haar_2d ( int m, int n, double u[] )
// m & n are the dimentions (every image is a perfect square)
//u is the input array in **(non column-major!)** row-major order</del>
int i;
int j;
int k;
double s;
double *v;
int tid, nthreads, chunk;
s = sqrt ( 2.0 );
v = ( double * ) malloc ( m * n * sizeof ( double ) );
for ( j = 0; j < n; j++ )
{
for ( i = 0; i < m; i++ )
{
v[i+j*m] = u[i+j*m];
}
}
/*
Determine K, the largest power of 2 such that K <= M.
*/
k = 1;
while ( k * 2 <= m )
{
k = k * 2;
}
/* Transform all columns. */
while ( n/2 < k ) // just 1 level of transformation
{
k = k / 2;
clock_t begin = clock();
#pragma omp parallel shared(s,v,u,n,m,nthreads,chunk) private(i,j,tid)
{
tid = omp_get_thread_num();
printf("Thread %d starting...\n",tid);
#pragma omp for schedule (dynamic)
for ( j = 0; j < n; j++ )
{
for ( i = 0; i < k; i++ )
{
v[i +j*m] = ( u[2*i+j*m] + u[2*i+1+j*m] ) / s;
v[k+i+j*m] = ( u[2*i+j*m] - u[2*i+1+j*m] ) / s;
}
}
#pragma omp for schedule (dynamic)
for ( j = 0; j < n; j++ )
{
for ( i = 0; i < 2 * k; i++ )
{
u[i+j*m] = v[i+j*m];
}
}
}//end parallel
clock_t end = clock();
double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
printf ( "Time for COLUMNS: %f ms\n", time_spent * 1000);
}//end while
// [...]code for rows
free ( v );
return;}
Сроки примерно такие:
Time for COLUMNS: 160.519000 ms // parallel
Time for COLUMNS: 62.842000 ms // serial
Я попытался переупорядочить прагмы множеством разных способов, например, с помощью статического расписания, с разделами, задачами и так далее, а также переупорядочил области данных переменных и динамическое размещение внутри параллельных областей. Я думал, что будет просто распараллелить двухуровневую версию, но вот уже два дня я борюсь. Ищу вашей помощи, ребята, я уже проверил почти все связанные вопросы здесь, но все еще не могу продолжить или, по крайней мере, понять причины. Заранее спасибо. (ЦП Intel Core i3-4005U CPU @ 1,70 ГГц × 4 потока, 2 ядра)
ОБНОВЛЕНИЕ:
1) Что касается m & n, предполагается, что однажды он будет реализовывать также прямоугольные изображения, поэтому я просто оставил это там.
2) Я понял, что u на самом деле является обычным массивом с линеаризованной матрицей внутри, то есть строка за строкой (я использую изображения PGM).
3) Memcpy - лучший вариант, поэтому теперь я использую его.
Что касается основной темы, я попытался разделить работу на n, создав задачу для каждого фрагмента, и в результате получился немного быстрее, чем последовательный код. Теперь я знаю, что входная матрица u находится в хорошем строковом порядке, 2 for, похоже, действуют соответственно, но я не уверен насчет таймингов: используя как omp_get_wtime (), так и clock (), я не знаю, как измерить ускорение. Я проводил тесты с разными размерами изображений, от 16x16 до 4096x4096, и параллельная версия кажется медленнее с clock () и быстрее с omp_get_wtime () и gettimeofday (). У вас есть предложения, как правильно с этим справиться с OpenMP или, по крайней мере, как правильно измерить ускорение?
while ( n/2 < k )
{
k = k / 2;
double start_time = omp_get_wtime();
// clock_t begin = clock();
#pragma omp parallel shared(s,v,u,n,m,nthreads,chunk) private(i,j,tid) firstprivate(k)
{
nthreads = omp_get_num_threads();
#pragma omp single
{
printf("Number of threads = %d\n", nthreads);
int chunk = n/nthreads;
printf("Chunks size = %d\n", chunk);
printf("Thread %d is starting the tasks.\n", omp_get_thread_num());
int h;
for(h=0;h<n;h = h + chunk){
printf("FOR CYCLE i=%d\n", h);
#pragma omp task shared(s,v,u,n,m,nthreads,chunk) private(i,j,tid) firstprivate(h,k)
{
tid = omp_get_thread_num();
printf("Thread %d starts at %d position\n", tid , h);
for ( j = h; j < h + chunk; j++ )
{
for ( i = 0; i < k; i++ )
{
v[i +j*m] = ( u[2*i+j*m] + u[2*i+1+j*m] ) / s;
v[k+i+j*m] = ( u[2*i+j*m] - u[2*i+1+j*m] ) / s;
}
}
}// end task
}//end launching for
#pragma omp taskwait
}//end single
}//end parallel region
// clock_t end = clock();
// double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
// printf ( "COLUMNS: %f ms\n", time_spent * 1000);
double time = omp_get_wtime() - start_time;
printf ( "COLUMNS: %f ms\n", time*1000);
for ( j = 0; j < n; j++ )
{
for ( i = 0; i < 2 * k; i++ )
{
u[i+j*m] = v[i+j*m];
}
}
}//end while
clock()
будет делать только то, что вы хотите, со средой выполнения MSVC C. Обычно используютomp_get_wtime()
. - person Z boson   schedule 12.07.2016