2012-04-14 2 views
0

Я использую openMP для параллелизации нескольких утверждений. Я использую параллель для конструкции. Распараллеленные цикл выглядит следующим образом:openMP - необходимость в атомарных или сокращающих предложениях

double solverFunction::apply(double* parameters_ , Model* varModel_) 
{ 
    double functionEvaluation = 0; 
    Command* command_ = 0; 
    Model* model_ = 0; 

    #pragma omp parallel for shared (functionEvaluation) private (model_,command_) 
    for (int i=rowStart;i<rowEnd+1;i++) 
    { 
     model_ = new Model(varModel_); 
     model_->addVariable("i", i); 
     model_->addVariable("j", 1); 
     command_ = formulaCommand->duplicate(model_); 
     functionEvaluation += command_->execute().toDouble(); 
    } 
} 

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

model_ = new Model(varModel_); 

, другие?), Он разбился. Вызов стека заканчивается в классах, таких как qAtomicBasic (это программа, написанная на C++/Qt), QHash, и у меня есть идея, что он выходит из строя из-за одновременного доступа для чтения/записи в памяти.

HOWEVER, model_ и command_ являются частными, так что каждый поток имеет дело с копией каждого. В переменной model_ я копирую varModel_, так что указатель, переданный в аргументе, не изменяется нитями. Аналогично, command_ является копией переменной-члена formulaCommand (дубликат - это грубоватый конструктор копирования).

Возможные недостатки в моем коде я идентифицированные в

  • functionEvaluation может быть модифицирован несколькими потоками одновременно

  • конструктор копирования в заявлении

    model_ = новая модель (varModel_);

читает элементы для varModel_ в памяти для создания нового экземпляра (model_). Параллельный доступ к членам данных varModel_ может произойти, хотя это не касается изменения их значения здесь, а только их чтения (затрагивающих их для других переменных).

Кроме того, я вижу два усовершенствования только (что я не могу проверить, пока несколько дней, но я прошу совета в любом случае):

  • добавить атомное положение, так что functionEvalution не одновременно написано в

  • добавить сокращение оператора (+, functionEvaluation), так что параллелизм в отношении доступа к functionEvaluation рассматривается автоматически

ли ю Решения ese, похоже, точно решают проблему и более эффективны в целом? Где проблема может быть связана с кодом, который я написал? Что такое решения?

Большое спасибо!

ответ

1

Первое замечание состоит в том, что, как вы заметили себя, изменение functionEvaluation одновременно - плохая идея. Это будет сбой.

Доступ только для чтения varModel_, с другой стороны, не представляет проблемы. Также не вызывает вызов конструктора копирования (но где он? Ваш код не показывает его).

Несвязанное использование статьи private в C++ - плохая идея. Просто объявите поточно-частные переменные внутри параллельный блок (в данном случае цикл for).

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

Следующий модифицированный код должен работать (я также взял на себя смелость унификации стиля кодирования ... почему трейлинг подчеркивает?):

double solverFunction::apply(double parameters, Model const& varModel) 
{ 
    double result = 0; 

    #pragma omp parallel for reduction(+:result) 
    for (int i = rowStart; i < rowEnd + 1; ++i) 
    { 
     Model model(varModel); 
     mode.addVariable("i", i); 
     mode.addVariable("j", i); 
     Command command = formulaCommand->duplicate(model); 
     result += command.execute().toDouble(); 
    } 

    return result; 
} 

Обратите внимание, что, из-за присущие точечные неточности плавающих, этот код может давать разные - результат последовательного кода. Это неизбежно.

+0

Большое спасибо! Konrad, я не вижу, как обойтись без указателей: вызов для копирования конструктора 'Model()' принимает аргумент, который является указателем на модель, а 'duplicate()' одинаково принимает указатель на команду. Является ли это более непрерывным или непрерывным, чтобы использовать указатели, даже если я удалю их внутри цикла? – octoback

+0

@dlib Первый вопрос: почему эти функции принимают указатели в качестве аргументов? Но если это невозможно изменить, вы можете сделать именно то, что вы написали в своем комментарии. Использование распределения кучи здесь просто не выгодно для распределения стека, делает вещи более сложными (ручное управление памятью) и добавляет небольшие накладные расходы. Кроме того, см. Мой обновленный ответ, я бы забыл еще одну большую (!) Ошибку в вашем коде раньше. –

+0

Это не ошибка, это недоразумение копирования (стыд), я изменил выше. Я не вижу, как использовать распределение стека здесь. Model (& model_) не подходит, поскольку я хочу скопировать varModel внутри новой модели экземпляра_; Я не могу работать без конструктора копирования, то есть с новым. – octoback

0

Одновременно изменяя functionEvaluation, определенно проблема в вашем коде, и лучший способ справиться с этим - это статья reduction.

Еще одна проблема заключается в том, что вы распределяете память кучи, вызывая параллельное соединение new, что для многих итераций никогда не является хорошей идеей, так как существует системная блокировка вызовов до new. Рассмотрим переход на распределение стека, так как стеки являются частными для каждого потока, а куча разделяется.

+0

Спасибо! Я не вижу, как обойтись без указателей: call to copy constructor Model() принимает аргумент, который является указателем на модель, а duplicate() аналогично принимает указатель на команду. – octoback

+0

@dlib: Вы не можете изменить их, чтобы принимать непосредственно значения вместо указателей? – Tudor

+0

нет, я не могу! Это большое программное обеспечение, некоторые вещи не мои. – octoback

Смежные вопросы