Что именно Овин помнит, Браузер делает?

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

Вы работаете в 2 потоках, а массив - int input[4] = {1, 2, 3, 4};. Вы правильно инициализируете sum до 0 и готовы к запуску цикла. На первой итерации вашего цикла поток 0 и поток 1 читают sum из памяти как 0, а затем добавьте их соответствующий элемент в sum и запишите его обратно в память. Однако это означает, что поток 0 пытается записать sum = 1 в память (первый элемент - 1 и sum = 0 + 1 = 1), а поток 1 пытается записать sum = 2 в память (второй элемент - 2 и sum = 0 + 2 = 2). Конечный результат этого кода зависит от того, какой из последних заканчивается последним, и поэтому записывает в память последним, что является условием гонки. Не только это, но и в этом конкретном случае ни один из ответов, которые мог бы произвести код, правильный! Есть несколько способов обойти это; Ниже я расскажу о трех основных:

#pragma omp critical:

В OpenMP существует так называемая директива critical. Это ограничивает код так, что только один поток может что-то делать за раз. Например, ваш for -loop может быть записан:

#pragma omp parallel for schedule(static)
for(i = 0; i < snum; i++) {
    int *tmpsum = input + i;
#pragma omp critical
    sum += *tmpsum;
}

Это устраняет условие гонки, когда только один поток обращается и записывается в sum за раз. Тем не менее, директива critical очень плоха для производительности и, вероятно, убьет большую часть (если не все) выгоды, получаемой от использования OpenMP в первую очередь.

#pragma omp atomic:

Директива atomic очень похожа на директиву critical. Основное отличие состоит в том, что, хотя директива critical применяется ко всему, что вы хотели бы сделать по одному потоку за раз, директива atomic применяется только к операциям чтения / записи памяти. Поскольку все, что мы делаем в этом примере кода, - это чтение и запись для суммирования, эта директива будет работать отлично:

#pragma omp parallel for schedule(static)
for(i = 0; i < snum; i++) {
    int *tmpsum = input + i;
#pragma omp atomic
    sum += *tmpsum;
}

Производительность atomic обычно значительно лучше, чем у critical. Тем не менее, это еще не лучший вариант в вашем конкретном случае.

reduction:

Метод, который вы должны использовать, и метод, который уже был предложен другими, - это reduction. Вы можете сделать это, изменив for -loop на:

#pragma omp parallel for schedule(static) reduction(+:sum)
for(i = 0; i < snum; i++) {
    int *tmpsum = input + i;
    sum += *tmpsum;
}

Команда reduction сообщает OpenMP, что, хотя цикл работает, вы хотите, чтобы каждый поток отслеживал свои собственные sum и добавить их все в конце цикла. Это самый эффективный метод, так как весь цикл теперь выполняется параллельно, при этом единственные служебные данные находятся прямо в конце цикла, когда необходимо добавить значения sum каждого из потоков.

17
задан Chris Moschini 25 August 2015 в 15:31
поделиться