2017-01-30 12 views
0

После this link, я пытаюсь понять операционную коду ядра (есть 2 версии этого ядра кода, один с volatile local float *source и другая с volatile global float *source, т.е. local и global версии). Ниже я взять local версию:Понимание метода для снижения OpenCL на поплавке

float sum=0; 
void atomic_add_local(volatile local float *source, const float operand) { 
    union { 
     unsigned int intVal; 
     float floatVal; 
    } newVal; 

    union { 
     unsigned int intVal; 
     float floatVal; 
    } prevVal; 

    do { 
     prevVal.floatVal = *source; 
     newVal.floatVal = prevVal.floatVal + operand; 
    } while (atomic_cmpxchg((volatile local unsigned int *)source, prevVal.intVal, newVal.intVal) != prevVal.intVal); 
} 

Если я хорошо понимаю, каждая работа, пункт разделяет доступ к source переменной благодаря классификатором «volatile», не так ли?

Впоследствии, если я возьму рабочий элемент, код добавит operand значение в newVal.floatVal переменной. Затем после этой операции я вызываю функцию atomic_cmpxchg, которая проверяет, выполнено ли предыдущее назначение (preVal.floatVal = *source; и newVal.floatVal = prevVal.floatVal + operand;), то есть путем сравнения значения, хранящегося по адресу source, с preVal.intVal.

В течение этого атомарных операций (которая не Бесперебойный по определению), так как значение, которое хранится в source отличается от prevVal.intVal, нового значение, которое хранится в source является newVal.intVal, которая на самом деле с плавающей точкой (потому что он кодируется на 4 байте, как целое число).

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

Но для each work-item нить, есть ли только одна итерация в while loop?

Я думаю, что будет одна итерация, потому что сравнение «*source== prevVal.int ? newVal.intVal : newVal.intVal» всегда присваивает значение newVal.intVal значение, хранящееся в source address, не так ли?

Любая помощь приветствуется, потому что я не понял все тонкости этого трюка для этого кода ядра.

UPDATE 1:

К сожалению, я почти понимаю все subtilities, особенно в while loop: Первый случай

: для данного одного потока, перед вызовом atomic_cmpxchg, если prevVal.floatVal по-прежнему равна *source, тогда atomic_cmpxchg изменит значение, содержащееся в указателе source, и вернет значение, содержащееся в old pointer, которое равно prevVal.intVal, поэтому мы прерываем с while loop.

Второй случай: Если между prevVal.floatVal = *source; инструкцией и зову atomic_cmpxchg, значение *source изменилось (в другом потоке ??), то atomic_cmpxchg возвращает old значение, которое больше не равна prevVal.floatVal, поэтому условие в while loop это правда, и мы остаемся в этом цикле до тех пор, пока предыдущее условие больше не будет проверено.

Мое понимание верно?

Благодаря

+1

Извините, если это очевидно для вас (я думаю, что я еще не полностью понял вопрос), но ... цикл while является стандартным способом достижения атомарности, как и https: //en.wikipedia. org/wiki/Compare-and-swap – Marco13

+0

Это классический цикл обмена обменом, упомянутый Марко. Игнорируйте профсоюзные трюки для ясности, они просто здесь, чтобы набирать текст. Кроме того, если у вас есть OpenCL 2+, встроенные атомы для поплавков. –

+0

: Marco13,: Aldwin ok, спасибо. Давайте рассмотрим простой случай с двумя потоками. Если первый из них находится в цикле while, то до тех пор, пока второй не изменит значение «prevVal.floatVal», цикл while длится для первого потока, не так ли? Но в этом случае операция инкремента «prevVal.floatVal + операнд»; бесконечно (пока второй поток не остановит его), и поэтому значение, хранящееся в адресе «источник», очень велико, потому что я суммируюсь с очень большим количеством значений «операнда».С уважением – youpilat13

ответ

1

Если я хорошо понимаю, каждая работа, пункт разделяет доступ к исходной переменной благодаря классификатором «volatile», не так ли?

volatile является ключевым словом языка C, который предотвращает компилятор от оптимизации доступов к определенному местоположению в памяти (другими словами, заставить загрузки/хранения при каждой операции чтения/записи указанной ячейки памяти). Это не влияет на владение базовым хранилищем. Здесь он используется, чтобы заставить компилятор перечитать source из памяти на каждой итерации цикла (иначе компилятору будет разрешено перемещать эту нагрузку за пределы цикла, что нарушает алгоритм).

do { 
    prevVal.floatVal = *source; // Force read, prevent hoisting outside loop. 
    newVal.floatVal = prevVal.floatVal + operand; 
} while(atomic_cmpxchg((volatile local unsigned int *)source, prevVal.intVal, newVal.intVal) != prevVal.intVal) 

После удаления классификаторов (для простоты), и переименование параметров, подпись atomic_cmpxchg является следующее:

int atomic_cmpxchg(int *ptr, int expected, int new) 

Что это делает:

atomically { 
    int old = *ptr; 

    if (old == expected) { 
     *ptr = new; 
    } 

    return old; 
} 

Итак, каждый поток , в отдельности:

  1. нагрузки текущего значения *source из памяти в preVal.floatVal
  2. Вычислить требуемое значение *source в newVal.floatVal
  3. Выполнить атомный сравнивать обмен описан выше (с использованием значений типа-каламбурил)
  4. Если результат atomic_cmpxchg == newVal.intVal, его означает, что обмен обмена был успешным, ломался. В противном случае обмен не произошел, перейдите к 1 и повторите попытку.

выше цикл в конечном счете заканчивается, потому что в конечном счете , каждый поток удается сделать их atomic_cmpxchg.

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

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

+0

Когда вы говорите: «Если результат atomic_cmpxchg == newVal.intVal, это означает, что обмен обмена был успешным, break« with »do {} while (atomic_cmpxchg ((volatile local unsigned int *) source, prevVal.intVal, newVal.intVal)! = prevVal.intVal) ", но я думаю, что мы должны написать вместо этого:" do {} while (atomic_cmpxchg ((volatile local unsigned int *) source, prevVal.intVal, newVal.intVal) == prevVal. intVal) ", потому что мы прерываем цикл while, когда результат atomic_cmpxchg является newVal.intVal, если" * source == preVal.floatVal ", не так ли? – youpilat13

+0

Нет, 'atomic_cmpxchg' возвращает значение * old *' * ptr', как показано выше, поэтому вы хотите разбить его, когда оно равно 'prevVal', потому что оно преуспело. С помощью того, что вы предлагаете, цикл немедленно завершится, если поток завершит свой атомный обмен. –

+0

Я думаю, что понял, вы могли видеть, пожалуйста, мою интерпретацию в моем UPDATE 1 выше? спасибо – youpilat13