локальные указатели в OpenMP

Локальные переменные должны быть автоматически закрыты для каждого streamа. Как насчет локального указателя, указывающего на какой-то адрес за пределами параллельной области, например

A * a = new A[10]; int i, j; for (i = 0; i < 10; i++){ A * local_i = &a[i]; // do sth ... #pragma omp parallel for for (j = 0; j x = 1.0f; // ... } } delete[]a; 

следует ли делать local_a private и fisrtprivate? Я новичок в OpenMP и C на самом деле.

Это зависит от того, что вы хотите сделать с массивом. Если каждый stream обращается к одному и тому же массиву, вам не нужно указывать указатель на threadprivate.

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

Если каждый stream должен иметь свою собственную копию массива, тогда вам нужно выделять и удалять память для каждого streamа отдельно. Если ваш компилятор поддерживает вызов правильного конструктора для созданных объектов C ++ для streamов-частных объектов, вы можете использовать std :: vector в вашем случае.

 std::vector a; #pragma omp parallel for firstprivate(a) for (j = 0; j < 10; j++) { A * local_j = &a[j]; local_j->x = 1.0f; // ... } 

В противном случае вам необходимо выделить для каждого streamа отдельный блок памяти.

 const int num_threads = omp_get_num_threads(); A** a; a = new A*[num_threads]; for (int i=0; ix = 1.0f; // ... } for (int i=0; i 

Важно знать, что OpenMP рассматривает статические и динамические массивы по-разному. В примере, который вам дал статический массив, более уместно. Давайте посмотрим, что произойдет, когда вы используете общие, частные и firstprivate на статических и динамических массивах. Я буду печатать номер streamа, адрес a, значение a и значения массива для каждого случая.

Статические массивы:

 int a[10]; for(int i=0; i<10; i++) a[i]=i; #pragma omp parallel { #pragma omp critical { printf("ithread %d %p %p :", omp_get_thread_num(), &a, a); for(int i=0; i<10; i++) printf("%d ", a[i]); printf("\n"); } } //ithread 1 0x7fff3f43f9b0 0x7fff3f43f9b0 :0 1 2 3 4 5 6 7 8 9 //ithread 3 0x7fff3f43f9b0 0x7fff3f43f9b0 :0 1 2 3 4 5 6 7 8 9 

Обратите внимание, что каждый stream имеет тот же адрес для a . Теперь давайте попробуем передать как личное.

 #pragma omp parallel private(a) //ithread 0 0x7fffc7897d60 0x7fffc7897d60 :4 0 -1393351936 2041147031 4 0 0 0 4196216 0 //ithread 1 0x7fa65f275df0 0x7fa65f275df0 :0 0 0 0 0 0 1612169760 32678 1596418496 32678 

Теперь каждый stream имеет свой единственный частный a и что каждая приватная версия указывает на другой адрес памяти. Однако значения массива НЕ были скопированы. Теперь попробуем firstprivate(a)

 #pragma omp parallel firstprivate(a) //ithread 0 0x7ffffb5ba860 0x7ffffb5ba860 :0 1 2 3 4 5 6 7 8 9 //ithread 3 0x7f50a8272df0 0x7f50a8272df0 :0 1 2 3 4 5 6 7 8 9 

Единственная разница в том, что значения ARE скопированы.

Динамические массивы:

 int *a = new int[10]; for(int i=0; i<10; i++) a[i]=i; 

Давайте сначала рассмотрим передачу как общего

 #pragma omp parallel //ithread 2 0x7fff86a02cc8 0x9ff010 :0 1 2 3 4 5 6 7 8 9 //ithread 0 0x7fff86a02cc8 0x9ff010 :0 1 2 3 4 5 6 7 8 9 

Каждый stream имеет то же самое, что и статический массив. Разница возникает, когда мы используем private.

 #pragma omp parallel private(a) //segmentation fault 

Каждый stream получает свой собственный частный, как то, что статический массив, но адрес памяти, на который указывает каждая из версий, - это нераспределенная случайная память. Когда мы пытаемся его прочитать, мы получаем ошибку сегментации. Мы можем исправить это, используя firstprivate(a)

  #pragma omp parallel firstprivate(a) //ithread 0 0x7fff2baa2b48 0x8bd010 :0 1 2 3 4 5 6 7 8 9 //ithread 1 0x7f3031fc5e28 0x8bd010 :0 1 2 3 4 5 6 7 8 9 

Теперь мы видим, что каждый stream имеет свой собственный частный a , в отличие от статических массивов, каждый из которых по-прежнему указывает на адрес SAME. Таким образом, указатели являются частными, но адреса, на которые они указывают, одинаковы. Это фактически означает, что память по-прежнему используется совместно.

Как распределить личную версию динамических массивов

Чтобы получить частные версии динамических массивов для каждого streamа, я не рекомендую выделять их за пределами параллельной области. Причина в том, что если вы не будете осторожны, легко вызвать ложный обмен. См. Этот вопрос / ответ о ложном совместном использовании, вызванном распределением памяти за пределами параллельной реализации OpenMP . Вы можете использовать двойной указатель, но это не обязательно устраняет проблему ложного обмена, и это не решит проблему в многопроцессорных системах. В этих системах важно, чтобы сокеты не разделяли одну и ту же страницу (или вы получаете другой вид ложного обмена). Если вы позволяете каждому streamу выделять свою память, вам не нужно беспокоиться об этом.

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