Может ли захват переменной, полученной путем разыменования указателя, быть атомарным?

#c #openmp #atomic

#c #openmp #атомарный

Вопрос:

У меня есть следующий код на C, который я компилирую с помощью OpenMP:

( int a[100] является глобальным массивом )

 int update (int * x, const int c)
{
    int v;
    #pra&ma omp atomic capture
    {
        v = a[*x];
        a[*x] = c;
    }
    return v;
}
  

Ни GCC, ни Clan& не выдают ошибку компилятора. Но я сомневаюсь, что все операции внутри раздела действительно могут поместиться в одну атомарную операцию. Эквивалентен ли мой код следующему?

 int update (int * x, const int c)
{
    int v;
    int X = *x;
    #pra&ma omp atomic capture
    {
        v = a[X];
        a[X] = c;
    }
    return v;
}
  

Комментарии:

1. «эквивалентен ли мой код …». Почему бы не посмотреть на сгенерированный ассемблерный код?

2. прочитайте документы по адресу openmp.or&/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf . разыменование ptr не подпадает под действие atomic.

3. @GuillaumePetitjean хотя это хороший совет по вопросам производительности и для понимания компиляторов в целом, просмотр сгенерированной сборки не скажет вам, является ли код в целом эквивалентным (для разных архитектур). Небезопасный код вполне может создавать безопасную сборку на некоторых архитектурах, так что это опасно, когда используется в качестве единственного источника для рассуждений.

4. @Zulan теоретически вы правы, но на практике два логически эквивалентных кода могут быть скомпилированы в несколько разные двоичные коды. В любом случае, мы здесь придираемся 🙂

Ответ №1:

Я отвечаю, имея в виду спецификацию, а не конкретную архитектуру или компилятор.

Ваш код небезопасен, если значение *x может измениться во время выполнения update .

Стандарт OpenMP описывает atomic конструкцию. Конкретный структурированный блок для вашего atomic capture является

 { v = x; x = expr; } 
  
  • x и v (если применимо) являются выражениями со значением l со скалярным типом.
  • Во время выполнения атомарной области несколько синтаксических вхождений x должны обозначать одно и то же место хранения.

Последняя точка нарушается, если *x изменена.

Я бы сказал, что код для update самого по себе допустим в предположении, поэтому компилятор не может жаловаться. Но применять это предположение должны вы.

Обратите внимание, что ваша вторая версия небезопасна в соответствии со стандартом. Однако вы можете защитить чтение значения указателя другим atomic read :

 int update(int * x, const int c)
{
    int v;
    int X;
    #pra&ma omp atomic read
    X = *x;
    #pra&ma omp atomic capture
    {
        v = a[X];
        a[X] = c;
    }
    return v;
}
  

Это также требует от вас защиты всех операций записи в *x память. x86-64 Как clan&, так и GCC генерируют один и тот же код независимо от atomic read , но это гарантирует, что ваш код безопасен на всех архитектурах.

Комментарии:

1. Но в любом случае, какой смысл передавать x как указатель, а не как значение?