2016-08-03 25 views
1

512スレッドの線形ブロックでカーネルを起動しています。各スレッドには、合計512×6 * 8 = 24576バイトの6つの倍精度値(2つの3要素ベクトル)が共有メモリに格納されます。私は次のようにすべてのベクトルをラインアップするために、共有の中間の要素へのポインタを作成したいと思います:CUDA:中間共有メモリの場所へのポインタ予期しない動作

__global__ void my_kernel(double *global_data) { 
    extern __shared__ double shr[]; 

    id = threadIdx.x; 
    double *X = &shr[id*3]; 
    double *Y = &shr[(id+1)*3]; 
    // Some arithmetic to set X[0:3] ad Y[0:3] 
    // Now I have a small for loop to compute something for each thread  

    for (int i = 0; i < 3; i++) { 
     for (int j=0; j < 3; j++) { 
      // Some computations involving the X and Y vectors 
    } 
} 

私の問題は、ループインデックスを使用して、XとYの値にアクセスしています。最初のループ反復中に以下の動作を説明することができません:

(cuda-gdb) cuda thread 
thread (0,0,0) 
(cuda-gdb) p shr[0] 
$1 = 0.62293193093894383 
(cuda-gdb) p &shr[0] 
$2 = (@shared double *) 0x0 
(cuda-gdb) p X[0] 
$3 = 0.62293193093894383 
(cuda-gdb) p &X[0] 
$4 = (@generic double *) 0x1000000 
(cuda-gdb) p X 
$5 = (@generic double * @register) 0x1000000 

これは正常です。しかし、:

i == 0のときにX [0]にアクセスできますが、X [i]にアクセスできないのはなぜですか?

編集:ここに私の問題を実証する完全な作業例です:

import pycuda.gpuarray as gpuarray 
import pycuda.driver as cuda 
import pycuda.autoinit 
import numpy as np 
from pycuda.compiler import SourceModule 
from math import pi 

mydat = np.arange(12).astype(np.float64) 
mydat_gpu = gpuarray.to_gpu(mydat) 

mod = SourceModule(""" 
__global__ void my_kernel(double *mydat) { 
     extern __shared__ double shr[]; 
     int id = threadIdx.x; 

     double *X = &shr[(id * 6)]; 
     double *Y = &shr[(id * 6) + 3]; 

     X[0] = mydat[0]; 
     X[1] = mydat[1];   
     X[2] = mydat[2];   
     Y[0] = mydat[3]; 
     Y[1] = mydat[4]; 
     Y[2] = mydat[5]; 


     __syncthreads();   

     double result; 

     for (int i = 0; i < 3; i++) { 
       result += X[i] + Y[i]; 
     } 
} 
""") 

my_kernel = mod.get_function("my_kernel") 
blk = (1,1,1) 
grd = (1,1,1) 

my_kernel(mydat_gpu, grid=grd, block=blk, shared=(8*6)) 

私はデバッグセッションを起動し、この時点で:

cuda-gdb --args python -m pycuda.debug minimal_working_example.py 

(cuda-gdb) b my_kernel 
Function "my_kernel" not defined. 
Make breakpoint pending on future shared library load? (y or [n]) y 

Breakpoint 1 (my_kernel) pending. 
(cuda-gdb) run 

[Switching focus to CUDA kernel 0, grid 1, block (0,0,0), thread (0,0,0), device 0, sm 0, warp 0, lane 0] 

Breakpoint 1, my_kernel(double * @generic)<<<(1,1,1),(1,1,1)>>> (mydat=0x13034a0000) 
at kernel.cu:5 
5  int id = threadIdx.x; 
(cuda-gdb) n 
7  double *X = &shr[(id * 6)]; 
(cuda-gdb) p id 
$1 = 0 
(cuda-gdb) p id * 6 
$2 = 0 
(cuda-gdb) n 
8  double *Y = &shr[(id * 6) + 3]; 
(cuda-gdb) p (id * 6) + 3 
$3 = 3 
(cuda-gdb) n 
10  X[0] = mydat[0]; 
(cuda-gdb) n 
11  X[1] = mydat[1];  
(cuda-gdb) n 
12  X[2] = mydat[2];  
(cuda-gdb) n 
13  Y[0] = mydat[3]; 
(cuda-gdb) n 
14  Y[1] = mydat[4]; 
(cuda-gdb) n 
15  Y[2] = mydat[5]; 
(cuda-gdb) p X 
$4 = (@generic double * @register) 0x1000000 
(cuda-gdb) p X[0] 
$5 = 0 
(cuda-gdb) p X[1] 
$6 = 1 
(cuda-gdb) p Y[0] 
$7 = 3 
(cuda-gdb) p Y[1] 
$8 = 4 
(cuda-gdb) n 
18  __syncthreads();  
(cuda-gdb) n 
22  for (int i = 0; i < 3; i++) { 
(cuda-gdb) n 
23   result += X[i] + Y[i]; 
(cuda-gdb) p i 
$9 = 0 
(cuda-gdb) p X[0] 
$10 = 0 
(cuda-gdb) p X[i] 
Error: Failed to read global memory at address 0x0 on device 0 sm 0 warp 0 lane 0 (error=7). 
+2

??これは: 'extern __shared__ shr [];'は有効ではありませんCUDA C/C++。 –

+0

申し訳ありません@RobertCrovella私はこれをちょっと急いで昨夜入力しました。問題を引き起こしているコードでは、 'extern __shared__ double shr [];と宣言されています。これを反映するようにOPを更新しています。 – FaceInvader

+1

一般に、表示されていないコードの実行時の動作を説明するのは非常に難しいでしょう。 XとYのインデックス計算は間違っています(スレッドID = 0のYはスレッドID = 1のXと同じになります)が、他に何が起こっていないかは[MCVE] 。もしあなたがそれを提供できないなら、私はあなたが答えを得ることは疑うでしょう – talonmies

答えて

0

ここで起こっていることのすべては、あなたが強化しているということです実際に実行中のカーネルにコンパイルされていないソース命令を介して実行されます。検査しようとしている変数はすでにスコープから外れており、デバッガはそれらを表示できなくなります。

これは、デバイスコードコンパイラでの積極的な最適化によるものです。あなたの例では、集計ループは、グローバルメモリまたは共有メモリへの書き込みに影響する出力を生成しないので、コンパイラはそれを単に削除します。最適化されたコードをステップ実行すると、ソースデバッガはソースと実行の間に1対1の関係を示すように最善を尽くしましたが、必ずしもそうであるとは限りません。

あなたはNVCCを使用して、コードを検査PTXにカーネルコードをコンパイルすることによって自分自身のためにこれを確認することができます。

// .globl _Z9my_kernelPd 
.visible .entry _Z9my_kernelPd(
    .param .u64 _Z9my_kernelPd_param_0 
) 
{ 
    .reg .b32 %r<3>; 
    .reg .f64 %fd<7>; 
    .reg .b64 %rd<6>; 


    ld.param.u64 %rd1, [_Z9my_kernelPd_param_0]; 
    cvta.to.global.u64 %rd2, %rd1; 
    mov.u32  %r1, %tid.x; 
    mul.lo.s32 %r2, %r1, 6; 
    mul.wide.s32 %rd3, %r2, 8; 
    mov.u64  %rd4, shr; 
    add.s64  %rd5, %rd4, %rd3; 
    ld.global.nc.f64 %fd1, [%rd2]; 
    ld.global.nc.f64 %fd2, [%rd2+8]; 
    ld.global.nc.f64 %fd3, [%rd2+16]; 
    ld.global.nc.f64 %fd4, [%rd2+24]; 
    ld.global.nc.f64 %fd5, [%rd2+32]; 
    ld.global.nc.f64 %fd6, [%rd2+40]; 
    st.shared.f64 [%rd5], %fd1; 
    st.shared.f64 [%rd5+8], %fd2; 
    st.shared.f64 [%rd5+16], %fd3; 
    st.shared.f64 [%rd5+24], %fd4; 
    st.shared.f64 [%rd5+32], %fd5; 
    st.shared.f64 [%rd5+40], %fd6; 
    bar.sync 0; 
    ret; 
} 

をあなたは、最後のPTX命令を見ることができる__syncthreads()デバイス機能命令である、barです放出する。合計のループは存在しません。

私はあなたがこのようにソース変更する場合:

__global__ void my_kernel2(double *mydat, double *out) { 
    extern __shared__ double shr[]; 
    int id = threadIdx.x; 

    double *X = &shr[(id * 6)]; 
    double *Y = &shr[(id * 6) + 3]; 

    X[0] = mydat[0]; 
    X[1] = mydat[1];   
    X[2] = mydat[2];   
    Y[0] = mydat[3]; 
    Y[1] = mydat[4]; 
    Y[2] = mydat[5]; 


    __syncthreads();   

    double result; 

    for (int i = 0; i < 3; i++) { 
     result += X[i] + Y[i]; 
    } 
    *out = result; 
} 

ようresultは今グローバルメモリに保存されているのとPTXにそれをコンパイルします。

.visible .entry _Z10my_kernel2PdS_(
    .param .u64 _Z10my_kernel2PdS__param_0, 
    .param .u64 _Z10my_kernel2PdS__param_1 
) 
{ 
    .reg .b32 %r<3>; 
    .reg .f64 %fd<20>; 
    .reg .b64 %rd<8>; 


    ld.param.u64 %rd3, [_Z10my_kernel2PdS__param_0]; 
    ld.param.u64 %rd2, [_Z10my_kernel2PdS__param_1]; 
    cvta.to.global.u64 %rd4, %rd3; 
    mov.u32  %r1, %tid.x; 
    mul.lo.s32 %r2, %r1, 6; 
    mul.wide.s32 %rd5, %r2, 8; 
    mov.u64  %rd6, shr; 
    add.s64  %rd1, %rd6, %rd5; 
    ld.global.f64 %fd1, [%rd4]; 
    ld.global.f64 %fd2, [%rd4+8]; 
    ld.global.f64 %fd3, [%rd4+16]; 
    ld.global.f64 %fd4, [%rd4+24]; 
    ld.global.f64 %fd5, [%rd4+32]; 
    ld.global.f64 %fd6, [%rd4+40]; 
    st.shared.f64 [%rd1], %fd1; 
    st.shared.f64 [%rd1+8], %fd2; 
    st.shared.f64 [%rd1+16], %fd3; 
    st.shared.f64 [%rd1+24], %fd4; 
    st.shared.f64 [%rd1+32], %fd5; 
    st.shared.f64 [%rd1+40], %fd6; 
    bar.sync 0; 
    ld.shared.f64 %fd7, [%rd1]; 
    ld.shared.f64 %fd8, [%rd1+24]; 
    add.f64  %fd9, %fd7, %fd8; 
    add.f64  %fd10, %fd9, %fd11; 
    ld.shared.f64 %fd12, [%rd1+8]; 
    ld.shared.f64 %fd13, [%rd1+32]; 
    add.f64  %fd14, %fd12, %fd13; 
    add.f64  %fd15, %fd10, %fd14; 
    ld.shared.f64 %fd16, [%rd1+16]; 
    ld.shared.f64 %fd17, [%rd1+40]; 
    add.f64  %fd18, %fd16, %fd17; 
    add.f64  %fd19, %fd15, %fd18; 
    cvta.to.global.u64 %rd7, %rd2; 
    st.global.f64 [%rd7], %fd19; 
    ret; 
} 

は、あなたが見ることができる(urolled)ループ現在はPTXに存在しており、デバッガの動作は、試してみると予想どおりに近いはずです。

コメントで示唆したように、あなたは今までので、コンパイラの最適化によって引き起こされる合併症のため、ブロックまたはグローバル状態を変更しない任意のコードを解析しようとしている時間を費やすべきではありません。

関連する問題