OpenCL C — ошибка управления памятью для массива после потока 64

#c #multithreading #memory #kernel #opencl

#c #многопоточность #память #ядро #opencl

Вопрос:

Я столкнулся с этой очень странной проблемой при работе с OpenCL C . Проблема в том, что у меня есть 100 потоков, которые обращаются к одному элементу каждого из массива размером 100. От 0 до 63 проблем нет, и каждый поток правильно вычисляет и обновляет значение элемента массива. Но когда он попадает в поток 64, он запутывается и обновляет значения некоторыми другими значениями…

Вот как я вызываю ядро:

 kernelGA(cl::EnqueueArgs(queue[iter],
                        cl::NDRange(200 / numberOfDevices)),
                        d_value,
                        d_doubleParameters,
                        buf_half_population, and so on...)
  

На стороне ядра я обращаюсь к каждому потоку с помощью:

 __kernel void kernelGA (__global double * value,
                        __global double * doubleParameters,
                        __global double * population,
                        __global double * scores, and so on...)

int idx = get_global_id(0); // This gives me 100 threads for each device. (I have two devices)
int size_a = 50;
double tempValue[size_a];

// Copying the global "value" into local array so each thread has its own copy.
for (int i = 0; i < size_a; i  ) {
    tempValue[i] = value[i];
}
  

На данный момент каждый поток теперь имеет свой собственный массив tempValue[] с теми же значениями. Затем я применяю некоторые вычисления и формулы к значениям массива tempValue[] для каждого потока…

 // Applying some computations on tempValue and changing the values for each  copy of tempValue for each thread.
tempValue[i] = some calculations for each thread...
  

После этого я получаю доступ к каждому элементу массива tempValue[] для каждого потока и непрерывно помещаю их обратно в массив большего размера (количество потоков * size_a). Имея в виду, что индексация для массива выглядит следующим образом: 0,1, 2, 3, … и так далее…

 for (int i = 0; i < size_a; i  ) {
    totalArray[(idx * size_a)   i] = tempvalue[i];
} 
  

Поэтому, когда я получаю ответы totalArray вне ядра и печатаю их, первые 64 (из 0-63) потоков правильно поместили свои значения в totalArray[] . Но начиная с 64 года индексация нарушена. Я имею в виду не совсем индексацию, потому что я распечатал только индексы, и к индексам правильно обращаются для всех потоков. Но значения, похоже, перепутаны…

Например: значение 3-го, 4-го, 5-го и 6-го элементов потока 0-63 равно 50, 60, 70 и 80 соответственно. Но для потока 64 и далее значения 3-го, 4-го, 5-го и 6-го элементов равны 80, 90, 100, 110. Как будто значения были сдвинуты на несколько элементов в обратном направлении. Почему? Что здесь происходит?

Ответ №1:

Существует проблема, если несколько устройств работают с одним и тем же массивом,

Вы помещаете

 cl::NDRange(200 / numberOfDevices)
  

как диапазон

но вы не помещаете

 cl::NDRange((200 / numberOfDevices)*deviceIndex)
  

как смещение для каждого устройства.

Все устройства пытаются выполнить запись в одну и ту же позицию вместо соседних групп.

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

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

1. Как проверить, меньше ли общее количество потоков, чем длина массива?

Ответ №2:

Итак, я нашел решение своей проблемы:

Проблема заключалась в том, что, хотя у каждого потока была своя собственная копия value[] массива, хранящаяся в tempValue[] array:

 // Copying the global "value" into local array so each thread has its own copy.
for (int i = 0; i < size_a; i  ) {
    tempValue[i] = value[i];
}
  

Значения в массиве были перепутаны после потока 64. Итак, что я сделал, я создал больший массив значений снаружи в коде хоста ( sizeOf(value) * 100 ), а затем скопировал первую часть массива в остальные 99 частей и отправил его на устройство. А затем я заставил каждый поток обращаться к своей части массива value [] с помощью индексации.

Это решило проблему!