1

CUDAストリームを使用して次のクラスを実装しました

class CudaStreams
{
    private:
        int             nStreams_;
        cudaStream_t*   streams_;
        cudaStream_t    active_stream_;

    public:

        // default constructor
        CudaStreams() { }

        // streams initialization
        void InitStreams(const int nStreams = 1) {
            nStreams_ = nStreams;
            // allocate and initialize an array of stream handles
            streams_ = (cudaStream_t*) malloc(nStreams_*sizeof(cudaStream_t));
            for(int i = 0; i < nStreams_; i++) CudaSafeCall(cudaStreamCreate(&(streams_[i]))); 

            active_stream_ = streams_[0];}

        // default destructor
        ~CudaStreams() {     
            for(int i = 0; i<nStreams_; i++) CudaSafeCall(cudaStreamDestroy(streams_[i])); }

}; 

この単純なコードを実行すると

void main( int argc, char** argv) 
{
    streams.InitStreams(1);
    streams.~CudaStreams();

    cudaDeviceReset();
}

通話後cudaDeviceReset()、次のメッセージが表示されます。

test.exe の未処理の例外 0x772f15de: 0x00000000。

使用時にこの問題を回避するには、デストラクタを呼び出す前に何をすべきcudaDeviceReset()ですか?

編集

デストラクタを追加するfree(streams_);と、つまり

~CudaStreams() {     
    for(int i = 0; i<nStreams_; i++) CudaSafeCall(cudaStreamDestroy(streams_[i])); // * 
    free(streams_); }

次のエラー メッセージが表示されます

cudaSafeCall() failed at C:\Users\Documents\Project\Library\CudaStreams.cuh:79 : unknown error

行は、デストラクタで79によって示されるものです。*

さらに、コンストラクタとデストラクタの同じ命令をコード内で直接使用すると、つまり

void main( int argc, char** argv) 
{
    int nStreams_ = 3;
    cudaStream_t* streams_ = (cudaStream_t*) malloc(nStreams_*sizeof(cudaStream_t));
    for(int i = 0; i < nStreams_; i++) CudaSafeCall(cudaStreamCreate(&(streams_[i]))); 
    for(int i = 0; i<nStreams_; i++) CudaSafeCall(cudaStreamDestroy(streams_[i])); 
    free(streams_);

cudaDeviceReset();
}

すべてがうまく機能します。おそらく、クラスの不適切な使用に関連する何かですか?

4

1 に答える 1

7

ここには 2 つの問題があり、どちらもクラスとスコープのデストラクタに関連しています。

main()まず、正しく動作するあなたのバージョンから始めましょう:

int main( int argc, char** argv) 
{
    {
        CudaStreams streams;
        streams.InitStreams(1);
    }

    cudaDeviceReset();

    return 0;
}

これは、デストラクタ forstreamsが 1 回だけ呼び出され (streamsスコープ外になった場合)、before cudaDeviceResetが呼び出されるため、正しく機能します。

オリジナルmain()(またはそのコンパイル可能なバージョンですが、それについては後で説明します...) は 2 つの理由で失敗します。もう一度見てみましょう。

int main( int argc, char** argv) 
{
    CudaStreams streams;
    streams.InitStreams(1);
    streams.~CudaStreams();

    cudaDeviceReset();

    return 0;
}

ここでは、デストラクタを明示的に呼び出しますstreams(これはほとんど行うべきではありません)。その後、スコープ外になると、return ステートメントでcudaDeviceResetデストラクタが再度呼び出されます。streamsコンテキストが破棄された後のデストラクタの自動呼び出しが、segfault/例外の原因です。呼び出しは、cudaStreamDestroy有効な CUDA コンテキストなしでストリームで機能しようとしています。したがって、解決策は、コンテキストがない場合に CUDA API 呼び出しをスコープ外にする (またはデストラクタを明示的に呼び出す) クラスを持たないことです。

次のような 3 番目のバージョンを作成した場合:

int main( int argc, char** argv) 
{
    {
        CudaStreams streams;
        streams.InitStreams(1);
        streams.~CudaStreams();
    }

    cudaDeviceReset();

    return 0;
}

CUDA ランタイム エラーが発生します。デストラクタが2 回呼び出されるためです。初めて(明示的に)動作します。2 番目 (暗黙的、範囲外) は実行時エラーを生成します: 有効なコンテキストがありますが、存在しないストリームを破棄しようとしています。

最後のコメント/質問: 元の質問で示したコードの実際のコンパイル可能なバージョンを投稿するのはどれくらい大変でしたか? 他の誰かが実際にコンパイルして実行できる適切な再現ケースにするために、文字通り5行余分に必要でした。私は、他の人が基本的にデバッグに関する質問に答える努力をすることを期待するのは少し不合理だと思います. 考えてみてください。【暴言終わり】

于 2013-06-07T11:00:10.163 に答える