Результат кода Cilk Plus зависит от количества работников

У меня есть небольшой фрагмент кода, который я хотел бы распараллелить по мере увеличения масштаба. Я использовал cilk_for от Cilk Plus для запуска многопоточности. Беда в том, что я получаю разный результат в зависимости от количества воркеров.

Я читал, что это может быть связано с состоянием гонки, но я не уверен, что конкретно в коде вызывает это или как его улучшить. Кроме того, я понимаю, что long и __float128 являются излишними для этой проблемы, но могут быть необходимы при масштабировании.

Код:

#include <assert.h>
#include "cilk/cilk.h"
#include <cstring>
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <string>
#include <vector>

using namespace std;

__float128 direct(const vector<double>& Rpct, const vector<unsigned>& values,     double Rbase, double toWin) {
    unsigned count = Rpct.size();
    __float128 sumProb = 0.0;
    __float128 rProb = 0.0;
    long nCombo = static_cast<long>(pow(2, count));

//  for (long j = 0; j < nCombo; ++j) { //over every combination
    cilk_for (long j = 0; j < nCombo; ++j) { //over every combination
        vector<unsigned> binary;

        __float128 prob = 1.0;
        unsigned point = Rbase;
        
        for (unsigned i = 0; i < count; ++i) { //over all the individual events
            long exp = static_cast<long>(pow(2, count-i-1));
            bool odd = (j/exp) %  2;  
            if (odd) {
                binary.push_back(1);
                point += values[i];
                prob *= static_cast<__float128>(Rpct[i]); 
            } else {
                binary.push_back(0);
                prob *= static_cast<__float128>(1.0 - Rpct[i]);  
            }            
        }

        sumProb += prob;
        if (point >= toWin)         rProb += prob;
        assert(sumProb >= rProb);
    }

    //print sumProb
    cout << " sumProb = " << (double)sumProb << endl;
    assert( fabs(1.0 - sumProb) < 0.01);

    return rProb;
}

int main(int argc, char *argv[]) {
    vector<double> Rpct;
    vector<unsigned> value;

    value.assign(20,1);
    Rpct.assign(20,0.25);

    unsigned Rbase  = 22;
    unsigned win = 30;

    __float128 rProb = direct(Rpct, value, Rbase, win);

    cout << (double)rProb << endl;

    return 0;
}

Пример вывода для export CILK_NWORKERS=1 && ./code.exe:

суммаПроб = 1

0.101812

Пример вывода для export CILK_NWORKERS=4 && ./code.exe:

суммаПроб = 0,948159

Утверждение не выполнено: (fabs(1.0 - sumProb) ‹ 0,01), прямая функция, файл code.c, строка 61.

Ловушка прерывания: 6


person Stershic    schedule 04.08.2016    source источник


Ответы (2)


Это из-за состояния гонки. cilk_for — реализация параллельного алгоритма for. Если вы хотите использовать параллель, вы должны использовать независимую итерацию (независимые данные). Это очень важно. В вашем случае вы должны использовать редукторы cilk: https://www.cilkplus.org/tutorial-cilk-plus-reducers

person sribin    schedule 04.08.2016
comment
Определение sumProb и rProb как cilk::reducer< cilk::op_add<double> > помогло. Я понимаю, что одновременный доступ, редактирование и сохранение этих переменных может привести к ошибочным результатам. Я бы предположил, что если бы я вместо этого сохранил их в вектор, а затем суммировал вектор вне цикла for, это тоже сработало бы. - person Stershic; 06.08.2016

Чтобы уточнить, на sumProb есть как минимум одна гонка. Каждый из параллельных рабочих процессов будет выполнять чтение/изменение/запись в этом месте. Как sribin упоминал выше, для решения подобных проблем и нужны редукторы.

Вполне возможно, что в вашей программе более одной гонки. Единственный способ убедиться в этом — запустить его с помощью детектора рас, поскольку поиск рас — это одна из вещей, в которых компьютеры намного справляются лучше, чем люди. Бесплатной возможностью является детектор гонки Cilkscreen, доступный на cilkplus.org. веб-сайт. К сожалению, он не поддерживает gcc/g++.

person Barry Tannenbaum - Intel    schedule 05.08.2016