сортировка оболочки в openmp - PullRequest
1 голос
/ 05 мая 2011

Кто-нибудь знаком с openmp, у меня нет отсортированного списка. Что я делаю неправильно. Я использую критическое в конце, поэтому только один поток может получить доступ к этому разделу, когда он будет отсортирован. Я думаю, что мои личные ценности не верны. Должны ли они вообще быть там, или мне лучше всего с #pragma omp for.

void shellsort(int a[])
{
    int i, j, k, m, temp;

    omp_set_num_threads(10);
    for(m = 2; m > 0; m = m/2)
    {
            #pragma omp parallel for private (j, m)
            for(j = m; j < 100; j++)
            {
                    #pragma omp critical
                    for(i = j-m; i >= 0; i = i-m)
                    {
                            if(a[i+m] >= a[i])
                                break;
                            else
                            {
                                temp = a[i];
                                a[i] = a[i+m];
                                a[i+m] = temp;
                            }

                    }
            }
    }
}

Ответы [ 2 ]

3 голосов
/ 06 мая 2011

Итак, здесь есть ряд проблем.

Итак, во-первых, как уже указывалось, i и j (и temp) должны быть частными;м и нужно делиться.Полезно сделать с openmp использование default (none), чтобы вы были вынуждены продумать, что делает каждая переменная, которую вы используете в параллельном разделе, и какой она должна быть.Так что

   #pragma omp parallel for private (i,j,temp) shared(a,m) default(none)

- хорошее начало.В частности, создание m private - это беда, потому что это означает, что m не определено внутри параллельной области.Между прочим, цикл должен начинаться с m = n / 2, а не с m = 2.

Кроме того, вам не нужна критическая область - или вы не должны это делать для сортировки оболочки,Проблема, которую мы увидим через секунду, заключается не в том, что несколько потоков работают над одними и теми же элементами.Поэтому, если вы избавитесь от этих вещей, вы получите что-то, что почти работает, но не всегда.И это подводит нас к более фундаментальной проблеме.

Способ работы сортировки оболочки заключается в том, что вы разбиваете массив на множество (здесь, m) подмассивов и вставляете-сортировать их (очень быстро для небольших массивов), а затем собрать заново;затем продолжайте, разбивая их на все меньшее и меньшее количество подмассивов и сортировку вставок (очень быстро, потому что они частично отсортированы).Сортировка этих множества подмассивов - это нечто, что можно сделать параллельно.(На практике проблема с памятью будет проблемой при таком простом подходе, но все же).

Теперь код, который вы получили, делает это в последовательном режиме, но на него нельзя рассчитывать, если вы просто обернете цикл j в omp parallel for.Причина в том, что каждая итерация в цикле j делает один шаг одного из видов вставки.Итерация цикла j + m делает следующий шаг.Но нет никакой гарантии, что они сделаны одним и тем же потоком или по порядку!Если другой поток уже выполнил j + m-ю итерацию до того, как первая выполнила j-ю, тогда сортировка вставки испорчена, и сортировка не удалась.

Таким образом, способ заставить эту работу переписатьсортировка оболочки, чтобы сделать параллелизм более явным - чтобы не разбивать сортировку вставкой на несколько последовательных шагов.

#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>

void insertionsort(int a[], int n, int stride) {
    for (int j=stride; j<n; j+=stride) {
        int key = a[j];
        int i = j - stride;
        while (i >= 0 && a[i] > key) {
            a[i+stride] = a[i];
            i-=stride;
        }
        a[i+stride] = key;
    }
}

void shellsort(int a[], int n)
{
    int i, m;

    for(m = n/2; m > 0; m /= 2)
    {
            #pragma omp parallel for shared(a,m,n) private (i) default(none)
            for(i = 0; i < m; i++)
                insertionsort(&(a[i]), n-i, m);
    }
}

void printlist(char *s, int a[], int n) {
    printf("%s\n",s);
    for (int i=0; i<n; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");
}

int checklist(int a[], int n) {
    int result = 0;
    for (int i=0; i<n; i++) {
        if (a[i] != i) {
            result++;
        }
    }
    return result;
}

void seedprng() {
    struct timeval t;

    /* seed prng */
    gettimeofday(&t, NULL);
    srand((unsigned int)(1000000*(t.tv_sec)+t.tv_usec));
}

int main(int argc, char **argv) {
    const int n=100;
    int *data;
    int missorted;

    data = (int *)malloc(n*sizeof(int));
    for (int i=0; i<n; i++)
        data[i] = i;

    seedprng();
    /* shuffle */ 
    for (int i=0; i<n; i++) {
        int i1 = rand() % n;
        int i2 = rand() % n;
        int tmp = data[i1];
        data[i1] = data[i2];
        data[i2] = tmp;
    }

    printlist("Unsorted List:",data,n);

    shellsort2(data,n);

    printlist("Sorted List:",data,n);
    missorted = checklist(data,n);
    if (missorted != 0) printf("%d missorted nubmers\n",missorted);

    return 0;
}
1 голос
/ 06 мая 2011

Переменные "j" и "i" должны быть объявлены закрытыми в параллельной области. Как сейчас, я удивлен, что что-то происходит, потому что «м» не может быть приватным. Критическая область позволяет ему работать для цикла «i», но критическая область должна быть в состоянии быть уменьшенной - хотя я некоторое время не выполнял сортировку оболочки.

...