Lo que se quiere en última instancia, depende de si los datos de entrada es una 1D o 2D matriz, y si su red y los bloques son 1D o 2D. El caso más simple es ambos 1D:
shmem[threadIdx.x] = gmem[blockDim.x * blockIdx.x + threadIdx.x];
Esto se fusiona. La regla de oro que uso es que la coordenada que varía más rápidamente (el threadIdx) se agrega como compensación al desplazamiento del bloque (blockDim * blockIdx). El resultado final es que la zancada de indexación entre hilos en el bloque es 1. Si la zancada se agranda, entonces pierde la fusión.
La regla simple (en Fermi y GPU posteriores) es que si las direcciones para todos los hilos en un warp caen en el mismo rango alineado de 128 bytes, se producirá una única transacción de memoria (suponiendo que el almacenamiento en caché está habilitado para la carga , que es el valor predeterminado). Si caen en dos rangos alineados de 128 bytes, se producen dos transacciones de memoria, etc.
En GT2xx y GPU anteriores, se vuelve más complicado. Pero puedes encontrar los detalles de eso en la guía de programación.
ejemplos adicionales:
No fusionado:
shmem[threadIdx.x] = gmem[blockDim.x + blockIdx.x * threadIdx.x];
No se fundieron, pero no tan malo en GT200 y más tarde:
stride = 2;
shmem[threadIdx.x] = gmem[blockDim.x * blockIdx.x + stride * threadIdx.x];
No fusionado en absoluto:
stride = 32;
shmem[threadIdx.x] = gmem[blockDim.x * blockIdx.x + stride * threadIdx.x];
Coa lesced, rejilla 2D, bloque 1D:
int elementPitch = blockDim.x * gridDim.x;
shmem[threadIdx.x] = gmem[blockIdx.y * elementPitch +
blockIdx.x * blockDim.x + threadIdx.x];
coalescentes, rejilla 2D y bloque:
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
int elementPitch = blockDim.x * gridDim.x;
shmem[threadIdx.y * blockDim.x + threadIdx.x] = gmem[y * elementPitch + x];
Ninguno de estos puede ser unieron, excepto para el primer bloque de la cuadrícula. Los hilos están numerados en orden principal de columna. – talonmies