С++ OpenMP записывает в определенный элемент общего массива/вектора

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

Учитывая, что моделирование является стохастическим, у меня есть следующая структура данных, и мне нужно зафиксировать количество посеянных агентов для определенного возраста [Отредактировано: отредактирован некоторый код]:

class CAgent {
    int ageGroup;
    bool isSeed;
    /* some other stuff */
};

class Simulator {
    std::vector<int> seed_by_age;
    std::vector<CAgent> agents;
    void initEnv();
    /* some other stuff */
};

void Simulator::initEnv() {
     std::fill(seed_by_age.begin(), seed_by_age.end(), 0);

     #pragma omp parallel
     {
          #pragma omp for
          for (size_t i = 0; i < agents.size(); i++)
          {
               agents[i].setup(); // (a)
               if (someRandomCondition())
               {
                   agents[i].isSeed = true;
                   /* (b) */
                   seed_by_age[0]++; // index = 0 -> overall
                   seed_by_age[ agents[i].ageGroup - 1 ]++;
               }
          }
     } // end #parallel
} // end Simulator::initEnv()

Поскольку переменная seed_by_age является общей для нескольких потоков, я знаю, что должен защищать ее должным образом. Итак, в (b) я использовал #pragma omp flush(seed_by_age[agents[i].ageGroup]), но компилятор жалуется на «ошибка: ожидается ')' перед токеном '['"

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

Большое спасибо, и я ценю любые предложения.

  • Блок разработки: 2-ядерный процессор, целевая платформа 4-6 ядер
  • Платформа: Windows 7, 64 бита
  • MinGW 4.7.2 64 бита (сборка rubenvb)

person YamHon.CHAN    schedule 28.06.2013    source источник


Ответы (3)


Вы можете использовать flush только с переменными, а не с элементами массивов и определенно не с элементами контейнерных классов C++. Оператор индексации для std::vector приводит к вызову operator[], встроенной функции, но все же функции.

Поскольку в вашем случае std::vector::operator[] возвращает ссылку на простой скалярный тип, вы можете использовать конструкцию atomic update для защиты обновлений:

#pragma omp atomic update
seed_by_age[0]++; // index = 0 -> overall
#pragma omp atomic update
seed_by_age[ agents[i].ageGroup - 1 ]++;

Что касается отказа от редукции, каждый поток касается seed_by_age[0], когда выполняется условие внутри цикла, тем самым делая недействительной ту же строку кэша во всех других ядрах. Доступ к другим элементам вектора также приводит к инвалидации взаимного кеша, но если предположить, что агенты более или менее равномерно распределены по возрастным группам, это будет не так серьезно, как в случае с первым элементом в векторе. Поэтому я бы предложил вам сделать что-то вроде:

int total_seed_by_age = 0;

#pragma omp parallel for schedule(static) reduction(+:total_seed_by_age)
for (size_t i = 0; i < agents.size(); i++)
{
    agents[i].setup(); // (a)
    if (someRandomCondition())
    {
        agents[i].isSeed = true;
        /* (b) */
        total_seed_by_age++;
        #pragma omp atomic update
        seed_by_age[ agents[i].ageGroup - 1 ]++;
    }
}

seed_by_age[0] = total_seed_by_age;
person Hristo Iliev    schedule 28.06.2013

#pragma omp flush(seed_by_age[agents[i]].ageGroup)

попробуйте закрыть всю вашу скобку, это исправит ошибку компилятора.

person alexbuisson    schedule 28.06.2013
comment
ageGroup не является членом seed_by_age (который является вектором) - person YamHon.CHAN; 28.06.2013

Я боюсь, что вашего заявления #pragma omp flush недостаточно для защиты ваших данных и предотвращения состояния гонки здесь. Если someRandomCondition() истинно только в очень ограниченном числе случаев, вы можете использовать критическую секцию для обновления вашего вектора без потери слишком большой скорости. В качестве альтернативы, если размер вашего вектора seed_by_age не слишком велик (что я предполагаю), было бы эффективно иметь частную версию вектора для каждого потока, который вы объединяете прямо перед выходом из параллельного блока.

person pTz    schedule 28.06.2013