Nano Hash - криптовалюты, майнинг, программирование

Атрибут данных в задаче OpenMP

Я написал код, связанный с быстрой сортировкой с помощью OpenMP, следующим образом:

#include <iostream>
#include <ctime>
#include <algorithm>
#include <functional>
#include <cmath>
using namespace std;
#include <omp.h>

void ParallelQuickSort(int *begin, int *end)
{
    if (begin+1 < end) 
    {
        --end;
        int *middle = partition(begin, end, bind2nd(less<int>(), *end));
        swap(*end, *middle);
        #pragma omp task shared(begin) firstprivate(middle)
            ParallelQuickSort(begin, middle);
        #pragma omp task shared(end) firstprivate(middle)
            ParallelQuickSort(++middle, ++end); 
    }
}

int main()
{
    int n = 200000000;

    int* a = new int[n];

    for (int i=0; i<n; ++i)
    {
        a[i] = i;
    }

    random_shuffle(a, a+n);
    cout<<"Sorting "<<n<<" integers."<<endl;

    double startTime = omp_get_wtime();
    #pragma omp parallel
    {
        #pragma omp single
            ParallelQuickSort(a, a+n);
    }
    cout<<omp_get_wtime() - startTime<<" seconds."<<endl;

    for (int i=0; i<n; ++i)
    {
        if (a[i] != i) 
        {
            cout<<"Sort failed at location i="<<i<<endl;
        }
    }

    delete[] a;
    return 0;
}

Проблема, с которой я сталкиваюсь в коде, - это атрибут данных в конструкции задачи внутри функции ParallelQuickSort. Переменная middle должна быть firstprivate вместо shared, так как она может быть изменена потоками, выполняющими две задачи. Однако, если я установлю начало и конец переменной как shared, как показано в коде, программа завершится ошибкой. Интересно, почему они (begin и end) должны быть firstprivate вместо shared. На мой взгляд, поскольку потоки, выполняющие две задачи, сохраняют переменные begin и end соответственно, они не будут влиять друг на друга. С другой стороны, функция ParallelQuickSort является рекурсивной, и поэтому в переменной begin или end (например, в родительской функции и в дочерней) присутствует гонка. Я не уверен в этом подозреваемом, поскольку переменные находятся в разных функциях (родительская и дочерняя функции).

19.11.2012

Ответы:


1

Во-первых, переменные, которые определены как private в данном регионе, автоматически становятся firstprivate в явных задачах, поэтому вам не нужно явно объявлять их как firstprivate. Во-вторых, ваш код содержит ++end; и --end;, которые изменяют значение end, влияя на другие задачи, если end равно shared. firstprivate — правильный класс обмена данными здесь — каждая задача просто сохраняет значения begin, end и middle, которые они использовали во время создания задачи.

Ваш ParallelQuickSort должен быть таким простым:

void ParallelQuickSort(int *begin, int *end)
{
    if (begin+1 < end) 
    {
        --end;
        int *middle = partition(begin, end, bind2nd(less<int>(), *end));
        swap(*end, *middle);
        #pragma omp task
            ParallelQuickSort(begin, middle);
        #pragma omp task
            ParallelQuickSort(++middle, ++end); 
    }
}

Обратите внимание, что хотя этот код работает, он намного медленнее, чем однопоточная версия: 88,2 секунды с 2 потоками на большом компьютере Xeon X7350 (Tigerton) против 50,1 секунды с одним потоком. Причина в том, что создание задач продолжается до очень простой задачи замены двух элементов массива. Накладные расходы на использование задач огромны, и вы должны поставить разумный верхний порог, ниже которого задачи должны быть отключены, скажем, когда размер подмассива достиг 1024 элементов. Точное число зависит от реализации OpenMP во время выполнения, типа вашего процессора и скорости памяти, поэтому значение 1024 выбирается более или менее случайно. Тем не менее, оптимальное значение не должно создавать две задачи, обрабатывающие элементы, попадающие в одну и ту же строку кеша, поэтому количество элементов должно быть кратно 16 (64 байта на строку кеша / 4 байта на целое число):

void ParallelQuickSort(int *begin, int *end)
{
    if (begin+1 < end) 
    {
        --end;
        int *middle = partition(begin, end, bind2nd(less<int>(), *end));
        swap(*end, *middle);
        #pragma omp task if((end - begin) > 1024)
            ParallelQuickSort(begin, middle);
        #pragma omp task if((end - begin) > 1024)
            ParallelQuickSort(++middle, ++end); 
    }
}

С этой модификацией код выполняется 34,2 секунды с двумя потоками на одном и том же поле.

19.11.2012
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..