2014-01-08 52 views
6

在开普勒硬件上分析Visual Profiler中的内核时,我注意到Profiler显示全局加载和存储缓存在L1中。 我很困惑,因为节目指南和开普勒调谐该手动状态:本地存储器访问中的GPU开普勒Kepler全局内存访问和L1缓存

L1高速缓存只保留,诸如寄存器 溢漏和堆栈数据。全局负载仅缓存在L2中(或缓存中的只读数据 )。

没有寄存器溢出(profiler显示L1缓存,即使是原始的2行'add'内核),我不确定'堆栈数据'在这里意味着什么。

GK110白皮书显示,除了一种情况外,全局访问都将通过L1缓存:通过只读缓存(__ldg)加载。 这是否意味着虽然全局访问通过L1硬件,但它们实际上并没有被缓存?这是否也意味着如果我将溢出的寄存器数据缓存在L1中,这些数据可能会因gmem访问而被驱逐?

更新:我已经意识到我可能会误读分析器是给我的信息,所以这里是内核代码以及探查的结果(我用相同的尝试都在Titan和K40结果)。

template<typename T> 
__global__ void addKernel(T *c, const T *a, const T *b) 
{ 
    int i = blockIdx.x * blockDim.x + threadIdx.x; 
    c[i] = a[i] + b[i]; 
} 

... 
// Kernel call 
float* x; 
float* y; 
float* d; 
// ... 
addKernel<<<1024, 1024>>>(d, x, y); 
cudaError_t cudaStatus = cudaDeviceSynchronize(); 
assert(cudaSuccess == cudaStatus); 

视觉探查输出:

Visual Profiler output

L1号码来拨打给L1高速缓存完美感启用GMEM访问。对于负载,我们有:

65536 * 128 = = 2 * 4 * 1024 * 1024

更新2:添加SASS和PTX代码。 SASS代码非常简单,包含从常量内存读取以及从/到全局内存(LD/ST指令)的加载/存储。

Function : _Z9addKernelIfEvPT_PKS0_S3_ 
.headerflags @"EF_CUDA_SM35 EF_CUDA_PTX_SM(EF_CUDA_SM35)" 
                  /* 0x088cb0a0a08c1000 */ 
/*0008*/    MOV R1, c[0x0][0x44];    /* 0x64c03c00089c0006 */ 
/*0010*/    S2R R0, SR_CTAID.X;     /* 0x86400000129c0002 */ 
/*0018*/    MOV32I R5, 0x4;      /* 0x74000000021fc016 */ 
/*0020*/    S2R R3, SR_TID.X;     /* 0x86400000109c000e */ 
/*0028*/    IMAD R2, R0, c[0x0][0x28], R3;  /* 0x51080c00051c000a */ 
/*0030*/    IMAD R6.CC, R2, R5, c[0x0][0x148]; /* 0x910c1400291c081a */ 
/*0038*/    IMAD.HI.X R7, R2, R5, c[0x0][0x14c]; /* 0x93181400299c081e */ 
                  /* 0x08a0a4b0809c80b0 */ 
/*0048*/    IMAD R8.CC, R2, R5, c[0x0][0x150]; /* 0x910c14002a1c0822 */ 
/*0050*/    IMAD.HI.X R9, R2, R5, c[0x0][0x154]; /* 0x931814002a9c0826 */ 
/*0058*/    LD.E R3, [R6];      /* 0xc4800000001c180c */ 
/*0060*/    LD.E R0, [R8];      /* 0xc4800000001c2000 */ 
/*0068*/    IMAD R4.CC, R2, R5, c[0x0][0x140]; /* 0x910c1400281c0812 */ 
/*0070*/    IMAD.HI.X R5, R2, R5, c[0x0][0x144]; /* 0x93181400289c0816 */ 
/*0078*/    FADD R0, R3, R0;      /* 0xe2c00000001c0c02 */ 
                  /* 0x080000000000b810 */ 
/*0088*/    ST.E [R4], R0;      /* 0xe4800000001c1000 */ 
/*0090*/    EXIT ;        /* 0x18000000001c003c */ 
/*0098*/    BRA 0x98;       /* 0x12007ffffc1c003c */ 
/*00a0*/    NOP;         /* 0x85800000001c3c02 */ 
/*00a8*/    NOP;         /* 0x85800000001c3c02 */ 
/*00b0*/    NOP;         /* 0x85800000001c3c02 */ 
/*00b8*/    NOP;         /* 0x85800000001c3c02 */ 

PTX:

.visible .entry _Z9addKernelIfEvPT_PKS0_S3_(
.param .u64 _Z9addKernelIfEvPT_PKS0_S3__param_0, 
.param .u64 _Z9addKernelIfEvPT_PKS0_S3__param_1, 
.param .u64 _Z9addKernelIfEvPT_PKS0_S3__param_2 
) 
{ 
.reg .s32 %r<5>; 
.reg .f32 %f<4>; 
.reg .s64 %rd<11>; 

ld.param.u64 %rd1, [_Z9addKernelIfEvPT_PKS0_S3__param_0]; 
ld.param.u64 %rd2, [_Z9addKernelIfEvPT_PKS0_S3__param_1]; 
ld.param.u64 %rd3, [_Z9addKernelIfEvPT_PKS0_S3__param_2]; 
cvta.to.global.u64 %rd4, %rd1; 
.loc 1 22 1 
mov.u32 %r1, %ntid.x; 
mov.u32 %r2, %ctaid.x; 
mov.u32 %r3, %tid.x; 
mad.lo.s32 %r4, %r1, %r2, %r3; 
cvta.to.global.u64 %rd5, %rd2; 
mul.wide.s32 %rd6, %r4, 4; 
add.s64 %rd7, %rd5, %rd6; 
cvta.to.global.u64 %rd8, %rd3; 
add.s64 %rd9, %rd8, %rd6; 
.loc 1 23 1 
ld.global.f32 %f1, [%rd9]; 
ld.global.f32 %f2, [%rd7]; 
add.f32 %f3, %f2, %f1; 
add.s64 %rd10, %rd4, %rd6; 
.loc 1 23 1 
st.global.f32 [%rd10], %f3; 
.loc 1 24 2 
ret; 
} 
+0

看看[这篇文章](http://stackoverflow.com/questions/19627702/l2-cache-in-kepler)。 L1缓存和只读缓存是不同的。在评论中,罗伯特曾提到“开普勒通常不会为普通的全球负载启用L1”。 – Farzad

+0

感谢您的链接。我明白L1和纹理缓存是不同的。除了编程指南中提到的内容(即寄存器溢出和堆栈数据)之外,我对Robert的“正常”意义很感兴趣。 –

+0

你可以显示你的.PTX输出吗?我在想你的短内核可能会有低级指令,它们利用L1高速缓存来保存中间结果。 – Farzad

回答

5

费米和开普勒架构的所有通用的,全球性的,局部的,和共享内存操作由L1高速缓存处理。共享内存访问不需要标记查找,也不会使缓存行无效。所有本地和全局内存访问都需要查找标签。未高速缓存的全局内存存储和读取操作会使高速缓存行无效。在计算能力3.0和3.5上,除CCG 3.5上的LDG之外的所有全局内存读取都将被解除缓存。 LDG指令遍历纹理缓存。

+0

你能澄清一点吗?如果访问是“由L1缓存处理”,那么它如何被解除缓存?另外,为什么未缓存的内存访问使缓存行无效? – einpoklum

+0

在开普勒架构上,L1单元包含数据RAM和标签RAM。共享内存,本地,全局和表面访问全部通过Kepler L1缓存作为通向SRAM(缓存操作或共享内存)的路径,或作为内存子系统下一级的数据路径。大多数L1支持缓存和非缓存访问。未缓存的访问通常必须使缓存行无效以避免数据一致性问题。 –