第一个问题。 CUDA C编程指南的编写如下。
相同的片上存储器用于L1和共享存储器:它可以 配置为48 KB的共享内存和16 KB的L1缓存或16 共享内存KB和48 KB L1缓存
但是,设备查询显示“每个块可用的寄存器总数:32768”。 我用GTX580。(CC是2.0) 该指南说默认缓存大小为16KB,但32768表示32768 * 4(字节)= 131072字节= 128 KB。实际上,我不知道哪个是正确的。
第二个问题。 我设置如下,
dim3 grid(32, 32); //blocks in a grid
dim3 block(16, 16); //threads in a block
kernel<<<grid,block>>>(...);
然后,每个块的线程数是256. =&gt;我们每个块需要256 * N个寄存器。 N表示每个线程所需的寄存器数。 (256 * N)*块是每个SM的寄存器数。(不是字节) 因此,如果默认大小为16KB且threads / SM为MAX(1536),则N不能超过2.由于“每个多处理器的最大线程数:1536”。 16KB / 4Bytes = 4096个寄存器,4096/1536 = 2.66666 ......
如果较大的缓存为48KB,则N不能超过8。 48KB / 4Bytes = 12288个寄存器,12288/1536 = 8
这是真的吗?其实我很困惑。
实际上,我几乎完整的代码就在这里。 我认为,当块尺寸为16x16时,内核会被优化。 但是,在8x8的情况下,快于16x16或类似。 我不知道为什么。
每个线程的寄存器数为16,共享存储器为80 + 16字节。
我曾问过同样的问题,但我无法得到确切的答案: The result of an experiment different from CUDA Occupancy Calculator
#define WIDTH 512
#define HEIGHT 512
#define TILE_WIDTH 8
#define TILE_HEIGHT 8
#define CHANNELS 3
#define DEVICENUM 1
#define HEIGHTs HEIGHT/DEVICENUM
__global__ void PRINT_POLYGON( unsigned char *IMAGEin, int *MEMin, char a, char b, char c){
int Col = blockIdx.y*blockDim.y+ threadIdx.y; //Col is y coordinate
int Row = blockIdx.x*blockDim.x+ threadIdx.x; //Row is x coordinate
int tid_in_block = threadIdx.x + threadIdx.y*blockDim.x;
int bid_in_grid = blockIdx.x + blockIdx.y*gridDim.x;
int threads_per_block = blockDim.x * blockDim.y;
int tid_in_grid = tid_in_block + threads_per_block * bid_in_grid;
float result_a, result_b;
__shared__ int M[15];
for(int k = 0; k < 5; k++){
M[k] = MEMin[a*5+k];
M[k+5] = MEMin[b*5+k];
M[k+10] = MEMin[c*5+k];
}
int result_a_up = (M[11]-M[1])*(Row-M[0]) - (M[10]-M[0])*(Col-M[1]);
int result_b_up = (M[6] -M[1])*(M[0]-Row) - (M[5] -M[0])*(M[1]-Col);
int result_down = (M[11]-M[1])*(M[5]-M[0]) - (M[6]-M[1])*(M[10]-M[0]);
result_a = (float)result_a_up / (float)result_down;
result_b = (float)result_b_up / (float)result_down;
if((0 <= result_a && result_a <=1) && ((0 <= result_b && result_b <= 1)) && ((0 <= (result_a+result_b) && (result_a+result_b) <= 1))){
IMAGEin[tid_in_grid*CHANNELS] += M[2] + (M[7]-M[2])*result_a + (M[12]-M[2])*result_b; //Red Channel
IMAGEin[tid_in_grid*CHANNELS+1] += M[3] + (M[8]-M[3])*result_a + (M[13]-M[3])*result_b; //Green Channel
IMAGEin[tid_in_grid*CHANNELS+2] += M[4] + (M[9]-M[4])*result_a + (M[14]-M[4])*result_b; //Blue Channel
}
}
struct DataStruct {
int deviceID;
unsigned char IMAGE_SEG[WIDTH*HEIGHTs*CHANNELS];
};
void* routine( void *pvoidData ) {
DataStruct *data = (DataStruct*)pvoidData;
unsigned char *dev_IMAGE;
int *dev_MEM;
unsigned char *IMAGE_SEG = data->IMAGE_SEG;
HANDLE_ERROR(cudaSetDevice(5));
//initialize array
memset(IMAGE_SEG, 0, WIDTH*HEIGHTs*CHANNELS);
cudaDeviceSetCacheConfig(cudaFuncCachePreferL1);
printf("Device %d Starting..\n", data->deviceID);
//Evaluate Time
cudaEvent_t start, stop;
cudaEventCreate( &start );
cudaEventCreate( &stop );
cudaEventRecord(start, 0);
HANDLE_ERROR( cudaMalloc( (void **)&dev_MEM, sizeof(int)*35) );
HANDLE_ERROR( cudaMalloc( (void **)&dev_IMAGE, sizeof(unsigned char)*WIDTH*HEIGHTs*CHANNELS) );
cudaMemcpy(dev_MEM, MEM, sizeof(int)*35, cudaMemcpyHostToDevice);
cudaMemset(dev_IMAGE, 0, sizeof(unsigned char)*WIDTH*HEIGHTs*CHANNELS);
dim3 grid(WIDTH/TILE_WIDTH, HEIGHTs/TILE_HEIGHT); //blocks in a grid
dim3 block(TILE_WIDTH, TILE_HEIGHT); //threads in a block
PRINT_POLYGON<<<grid,block>>>( dev_IMAGE, dev_MEM, 0, 1, 2);
PRINT_POLYGON<<<grid,block>>>( dev_IMAGE, dev_MEM, 0, 2, 3);
PRINT_POLYGON<<<grid,block>>>( dev_IMAGE, dev_MEM, 0, 3, 4);
PRINT_POLYGON<<<grid,block>>>( dev_IMAGE, dev_MEM, 0, 4, 5);
PRINT_POLYGON<<<grid,block>>>( dev_IMAGE, dev_MEM, 3, 2, 4);
PRINT_POLYGON<<<grid,block>>>( dev_IMAGE, dev_MEM, 2, 6, 4);
HANDLE_ERROR( cudaMemcpy( IMAGE_SEG, dev_IMAGE, sizeof(unsigned char)*WIDTH*HEIGHTs*CHANNELS, cudaMemcpyDeviceToHost ) );
HANDLE_ERROR( cudaFree( dev_MEM ) );
HANDLE_ERROR( cudaFree( dev_IMAGE ) );
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
cudaEventElapsedTime( &elapsed_time_ms[data->deviceID], start, stop );
cudaEventDestroy(start);
cudaEventDestroy(stop);
elapsed_time_ms[DEVICENUM] += elapsed_time_ms[data->deviceID];
printf("Device %d Complete!\n", data->deviceID);
return 0;
}
答案 0 :(得分:2)
blockDim 8x8比16x16更快,因为当你增加块大小时,你的内存访问中的地址差异会增加。
使用15个SM在GTX480上收集的指标。
metric 8x8 16x16
duration 161µs 114µs
issued_ipc 1.24 1.31
executed_ipc .88 .59
serialization 54.61% 28.74%
指令重放的次数使我们觉得我们可能有不良的内存访问模式。
achieved occupancy 88.32% 30.76%
0 warp schedulers issues 8.81% 7.98%
1 warp schedulers issues 2.36% 29.54%
2 warp schedulers issues 88.83% 52.44%
16x16似乎使warp调度程序忙。但是,它会让调度程序忙于重新发出指令。
l1 global load trans 524,407 332,007
l1 global store trans 401,224 209,139
l1 global load trans/request 3.56 2.25
l1 global store trans/request 16.33 8.51
首要任务是减少每个请求的事务。 Nsight VSE源视图可以显示每条指令的内存统计信息。内核中的主要问题是交错的U8加载并存储IMAGEin [] + = value。在16x16时,每个请求产生16.3个事务,但8x8配置只产生8.3个事务。
更改 IMAGEin [(i * HEIGHTs + j)* CHANNELS] + = ...
连续将16x16的性能提高3倍。我想将通道增加到4并在内核中处理打包将提高缓存性能和内存吞吐量。
如果您修复了每个请求的内存事务数,那么您可能需要查看执行依赖项并尝试增加ILP。
答案 1 :(得分:1)
块大小为8x8时速度更快,因为它是32的较小倍数,因为它在下图中可见,有32个CUDA核心绑定在一起,两个不同的warp调度程序实际上安排了同样的事情。因此,在每个执行周期中,在这32个内核上执行相同的指令。
为了更好地澄清这一点,在第一种情况下(8x8),每个块由两个warp(64个线程)组成,因此它只在两个执行周期内完成,但是,当你使用(16x16)作为块大小时,每个经线需要8个经线(256个线程),因此执行周期增加4倍,导致化合物变慢。
然而,在某些情况下,填充更多warp的SM会更好,当内存访问很高并且每个warp可能进入内存停顿(即从内存中获取其操作数)时,它将被另一个warp替换直到内存操作完成。因此导致SM占用更多。
当然,您应该在计算中输入每个SM的块数和SM总数,例如,为单个SM分配超过8个块可能会减少其占用率,但可能在您的情况下,您不是面对这些问题,因为256通常比64更好,因为它会在SM之间平衡你的块,而使用64个线程将导致更多的块在同一个SM中执行。
注册池与共享内存/缓存不同,直到他们架构的最底层!
注册由Flip-flops组成,L1缓存可能是SRAM。
只是想知道,看看下面代表FERMI架构的图片,然后更新你的问题以进一步说明你所面临的问题。
作为一个注释,您可以通过将选项--ptxas-options = -v
传递给nvcc来查看函数占用了多少寄存器和共享内存(smem)。