должен ли мьютекс использоваться для "получения" значений из массива? - PullRequest
2 голосов
/ 01 ноября 2011

я знаю, что если бы я был , присваивая значения из нескольких потоков одному и тому же месту в массиве (или увеличивая это значение и т. Д.), Мне бы пришлось использовать мьютекс, чтобы значение в эта часть массива останется последовательной.

(пример):

for(ix = 0; ix < nx; ix++)
{
  x = x_space[ix];
  for(iy = 0; iy < ny; iy++)
  {
    y = y_space[iy];

    mutex_lock[&mut];
    sum = sum + f(x,y);
    mutex_unlock[&mut];
  }
}

но также необходимо ли использовать мьютекс вокруг раздела кода, для которого оба потока могут получать значение из массива?

(пример):

for(ix = 0; ix < nx; ix++)
{
  mutex_lock[&xmut];
  x = x_space[ix];
  mutex_unlock[&xmut];

  for(iy = 0; iy < ny; iy++)
  {
    mutex_lock[&ymut];
    y = y_space[iy];
    mutex_unlock[&ymut];

    mutex_lock[&mut];
    sum = sum + f(x,y);
    mutex_unlock[&mut];
  }
}

Ответы [ 8 ]

2 голосов
/ 01 ноября 2011

Нет.Вы можете думать об этом так: многие люди могут смотреть на стакан воды одновременно, но только один за раз может выпить.

Пока вы только читаете (чтобы сделатькопия или что-то еще), было бы хорошо.Однако, если вы имеете дело с типами данных, у которых нет атомарных операций (или с каким-либо базовым типом данных, который не выполняет атомарных операций по причинам выравнивания памяти или чего-то еще), и возможно, что кто-то еще можно было бы записать в эту память, вы могли бы посмотреть на часть данных в «полуизмененном» состоянии, где кто-то еще находится в процессе изменения.Поэтому вам может понадобиться мьютекс, в зависимости от вашей ситуации.

1 голос
/ 01 ноября 2011

Ответ, это зависит ... Если ваши целые числа правильно выровнены на большинстве архитектур, вы получите атомарное чтение и запись и, следовательно, не должны блокироваться.

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

0 голосов
/ 03 ноября 2011

Если вы используете OpenMP 3.1, есть замечательная новая функция с атомарным доступом, в данном случае вы хотите

#pragma omp atomic read
some_private_var = some_shared_var[some_index];

В этом есть две приятные вещи, одна из них подразумеваемая очистка, котораякак, например,

#pragma omp flush(some_shared_var[some_index])

перед чтением, но openmp не позволяет сбрасывать разыменованные значения.Хотя вы можете очистить без списка, все сбрасывается, поэтому это может быть дорого, если в самом внутреннем цикле какого-либо вычисления.

Другая хорошая вещь, конечно, атомарная природа чтения.Обратите внимание, что some_shared_var [some_index] может иметь произвольный размер (возможно, это структура или какой-то объект в C ++).Если какой-то другой поток хочет записать это, что, например, может произойти путем копирования всех примитивных данных внутри объекта, он не может прервать атомарное чтение.

С точки зрения издержек, для меня в любом случае это намного быстрее, чемблокирует, и если some_shared_var [some_index] является примитивным типом данных, чтение, вероятно, происходит в любом случае атомарно, но теперь мы получаем сброс.

Некоторые другие мысли:

Если не критично, что большинствопоследние значения читаются, вы можете рисковать без использования атомарного чтения.Это дает возможность читать из кэшированного значения, которое быстрее (например, регистр процессора).Просто посмотрите, не является ли some_shared_var [some_index] большим объектом, так как он мог бы быть частично записан другим потоком.

Я думаю, что атомарное чтение должно происходить откуда-то в памяти, доступной для всех процессоров,поэтому он все еще может находиться в кэш-памяти на кристалле (например, совместно используемой кэш-памяти L3), поэтому вам не нужно читать, скажем, из DRAM.Я не уверен на 100%, что это всегда так, но я подтвердил это для своего компьютера, запланировав некоторые эксперименты, в которых использование памяти ниже и выше встроенного в мой кэш процессора.

0 голосов
/ 01 ноября 2011

Это в основном зависит от того, что вы должны делать с этой суммой и что представляет собой сам массив (и что представляет собой и сумма).Допустимо ли, что некоторые элементы массива меняются после того, как вы их суммировали (но вы все еще заканчиваете сумму)?Если ответ да , вам не нужно блокировать.

Если ответ «нет», то вам нужно заблокировать весь массив на все время вычисления суммы, иснять блокировку только после того, как сумма сыграет свою роль.

0 голосов
/ 01 ноября 2011

Нет необходимости использовать мьютекс , если вы на 100% уверены , что массив не изменен или удален из другого потока.

0 голосов
/ 01 ноября 2011

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

0 голосов
/ 01 ноября 2011

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

0 голосов
/ 01 ноября 2011

Пока не будет никаких записей / перераспределений массива, являющегося read , нет необходимости блокировать их.

Могу ли я также от всей души предложить менее мелкозернистую блокировку? Это не будет хорошо работать, это мое предположение

Образец На основе OpenMP

#pragma omp parallel for reduction (+:sum)
for(ix = 0; ix < nx; ix++)
{
    x = x_space[ix];
    for(iy = 0; iy < ny; iy++)
    {
        y = y_space[iy];
        sum += f(x,y);
    }
}

// sum is automatically 'collected' from the parallel team threads
...