2

私はCUDAでいくつかのコードを書いています(正確にはハフマンアルゴリズムですが、ケースとはまったく関係ありません)。2 つ関数含むファイルParallel.cuがありますこれらの関数の本体は次のとおりです。

//I know body of this function looks kinda not-related 
//   to program main topic, but it's just for tests.
__global__ void wrtDict(Node** nodes, unsigned char* str)
{
    int i = threadIdx.x;

    Node* n = nodes[i];
    char c = n->character;

    str[6 * i] = 1;//c;                         !!!
    str[6 * i + 1] = 2;

    str[6 * i + 2] = 0;
    str[6 * i + 3] = 0;
    str[6 * i + 4] = 0;
    str[6 * i + 5] = 0;
}

ここではNodeクラスのオブジェクトnを使用しないため、これらの最初の 2 行が無意味に思えることはわかっていますが、しばらくそのままにしておきます。そして、「!!!」でマークされた超秘密のコメントがあります。ここにWriteDictionaryがあります:

void WriteDictionary(NodeList* nodeList, unsigned char* str)
{
    Node** nodes = nodeList->elements;   
    int N = nodeList->getCount();

    Node** cudaNodes;
    unsigned char* cudaStr;

    cudaMalloc((void**)&cudaStr, 6 * N * sizeof(unsigned char));
    cudaMalloc((void**)&cudaNodes, N * sizeof(Node*));

    cudaMemcpy(cudaStr, str, 6 * N * sizeof(char), cudaMemcpyHostToDevice); 
    cudaMemcpy(cudaNodes, nodes, N * sizeof(Node*), cudaMemcpyHostToDevice);

    dim3 block(1);
    dim3 thread(N);

    std::cout << N << "\n";

    wrtDict<<<block,thread>>>(cudaNodes, cudaStr);

    cudaMemcpy(str, cudaStr, 6 * N * sizeof(unsigned char), cudaMemcpyDeviceToHost);


    cudaFree(cudaNodes);
    cudaFree(cudaStr);
}

ご覧のとおり、関数WriteDictionaryは、CUDA と残りのプログラムの間のプロキシのようなものです。オブジェクトNodeList内に保持されているNode * 配列要素が指す通常のメモリのどこかに、クラスNodeのオブジェクトがたくさんあります。今のところ、 Nodeについて知っていれば十分です。それには public フィールドchar characterがあります。今のところ、 char * strにはいくつかのテスト データが入力されます。char に割り当てられた6 * N 個のメモリが含まれます。ここで、 N = 要素内のすべての要素の数です 配列。そのため、CUDA で 6 * N 文字N ノードポインター用のメモリ空間を割り当てます。次に、そこにノードポインターをコピーします。それらはまだ通常のメモリを指しています。機能を実行しています。関数wrtDict内で、文字をchar c変数に抽出していますが、今回はそれを出力配列strに入れようとはしていません。

したがって、出力配列str ( WriteDictionary関数の外側) の内容を書き込んでいる場合、完全に正しい答えが得られます。つまり、次のようになります。

1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0   1  2  0  0  0  0 
1  2  0  0  0  0

ええ、ここに 39 個の正しい 6文字があります(16 進数で表示)。しかし、次のように、 wrtDict関数内の極秘コメントを少し変更すると、次のようになります。

__global__ void wrtDict(Node** nodes, unsigned char* str)
{
    int i = threadIdx.x;

    Node* n = nodes[i];
    char c = n->character;

    str[6 * i] = c;//1;                         !!!
    str[6 * i + 1] = 2;

    str[6 * i + 2] = 0;
    str[6 * i + 3] = 0;
    str[6 * i + 4] = 0;
    str[6 * i + 5] = 0;
}

私たちは奇妙なものを見るでしょう。私は今、6つごとの最初の文字が、配列が指すNodeからの文字であることを期待しています-それぞれが異なります。または、失敗したとしても、 6文字ごとに最初の文字だけが台無しになると予想していますが、残りはそのまま残されています 2 0 0 0 . _ しかし、いいえ!これを行うと、すべてが完全に台無しになり、出力配列strの内容は次のようになります。

70 21 67 b7 70 21  67 b7  0  0  0  0 
 0  0  0  0 18 d7  85  8 b8 d7 85  8 
78 d7 85  8 38 d9  85  8 d8 d7 85  8 
f8 d5 85  8 58 d6  85  8 d8 d5 85  8 
78 d6 85  8 b8 d6  85  8 98 d7 85  8 
98 d6 85  8 38 d6  85  8 d8 d6 85  8 
38 d5 85  8 18 d6  85  8 f8 d6 85  8 
58 d9 85  8 f8 d7  85  8 78 d9 85  8 
98 d9 85  8 d8 d4  85  8 b8 d8 85  8 
38 d8 85  8 38 d7  85  8 78 d8 85  8 
f8 d8 85  8 d8 d8  85  8 18 d5 85  8 
61 20 75 6c 74 72  69 63 65 73 20 6d 
6f 6c 65 73 74 69  65 20 73 69 74 20 
61 6d 65 74 20 69  64 20 73 61 70 69 
65 6e 2e 20 4d 61  75 72 69 73 20 73 
61 70 69 65 6e 20  65 73 74 2c 20 64 
69 67 6e 69 73 73  69 6d 20 61 63 20 
70 6f 72 74 61 20  75 74 2c 20 76 75 
6c 70 75 74 61 74  65 20 61 63 20 61 
6e 74 65 2e 20 46 

私は今尋ねています-なぜですか?CUDA GPU 内から通常のメモリにアクセスしようとしたためでしょうか。おそらくまさにこのケースについて、次のような警告が表示されます。

Cannot tell what pointer points to, assuming global memory space

私はこれについてグーグルで検索しましたが、これだけが見つかりました.CUDAは正確に通常のメモリに到達しているため、どこに到達するかを見つけることができませんでした.99.99%のこの警告は無視する必要があります. それで大丈夫だろうと思って無視しているのですが、そうではありません-私のケースはその0.01%以内ですか?

どうすればこの問題を解決できますか? Nodes へのポインターではなく、単にNodesを CUDA にコピーできることはわかっていますが、それらをコピーすると、内部で行われていることを並列化するよりも時間がかかると思います。すべてのNodeから文字を抽出し、それらをすべて配列に入れてから CUDA にコピーすることもできますが、前のステートメントと同じ問題です。

どうすればいいのかまったくわからず、さらに悪いことに、私の大学での CUDA プロジェクトの締め切りは今日 apx です。午後17時(もっと早く作るには十分な時間がありません、くそー…)。

PS。それが役立つ場合:私は非常に単純な(スイッチなし)コマンドを使用してコンパイルしています:

nvcc -o huff ArchiveManager.cpp IOManager.cpp Node.cpp NodeList.cpp Program.cpp Paraleller.cu
4

2 に答える 2

4

これはひどい質問です。talonmies のコメントを参照してください。

  1. すべてのCUDA API 呼び出しのエラー値を確認してください。cudaMemcpyカーネルの起動後、起動失敗メッセージが表示されます
  2. エラーをデバッグするために実行cuda-memcheckします (これは基本的にセグメンテーション違反です)。
  3. (マップされていない) ポインターを GPU からホスト メモリに逆参照していることに注意してください。ノードへのポインターだけでなく、ノードをコピーする必要があります。
于 2012-05-30T11:23:33.387 に答える
1

cuda-gdb内からプログラムを実行することもできます。cuda-gdbは、発生しているエラーを表示します。また、cuda-gdbの最初で、「set cuda memcheck on」を実行すると、cuda-gdb内でmemcheckがオンになります。

最新のcuda-gdbバージョン(現在は5.0)では、API呼び出しからのリターンコードをチェックしておらず、それらのAPI呼び出しが失敗した場合にも、警告が表示されます。

于 2012-05-31T01:12:17.040 に答える