Объединение памяти происходит, когда последовательные потоки обращаются к данным по последовательным адресам глобальной памяти в пределах 128-байтового выровненного сегмента. Затем обращения к памяти объединяются в один, что значительно снижает общую задержку.
В 2D-диапазоне объединение происходит только в направлении get_global_id(1)
или j
в вашем случае. В строке output[i*height + j] = input[j*width + i];
, input[j*width + i];
- это неверно выровненное (не объединенное) чтение, а output[i*height + j]
- объединенная запись. Доступ к объединенной памяти обычно намного быстрее, чем доступ с несогласованным доступом, но снижение производительности для объединенного / несогласованного чтения может сильно отличаться от объединенного / несогласованного чтения. На большинстве настольных архитектур графических процессоров комбинация несовмещенного чтения и объединенной записи выполняется быстрее, чем наоборот, см. Диаграмму ниже. Таким образом, ваша реализация уже должна быть более быстрым вариантом.
Since coalesced access is only possible along the j
index, if you have a range of (x=256,y=1)
(i
along x
-direction, j
along y
-direction), you do not get any coalescing. For (x=8,y=32)
, j
is coalesced in groups of 32 8 times per thread block, so memory bandwidth is fairly saturated and performance is good.
If you want maximum possible performance, I'd suggest you go with 1D indexing. This way you have full control about coalescing and coalescing happens over the entire thread block. Your matrix transpose kernel then would look like this:
#define width 6400
__kernel void transpose(__global float *input, __global float *output) {
const int n = get_global_id(0);
int i = n/width;
int j = n%width;
output[i*height + j] = input[j*width + i];
}
Вы можете записать width
в Ccode OpenCL во время выполнения C ++ и до времени компиляции OpenCL через конкатенация строк.