2017-03-27 30 views
2

我正在寫一個「錯誤檢查」功能,每當遇到錯誤時都會拋出std::runtime_error(下面的代碼示例)。

現狀:
比方說,在foo構造函數(文件:下面fooClass.h樣本)我是能夠成功地設置CUDA設備和分配GPU內存,但我對cudaMemcpy(***)調用返回的任何錯誤原因。這意味着我的errchk(***)函數正在拋出異常並將控制權轉移到main()函數(文件中:Start.cpp)。

因爲foo構造並沒有完成它的工作,沒有創建foo對象和~foo()析構函數不叫,因此以前分配GPU資源一直沒有公佈。

我知道trymain()應該以相反的順序取消分配所有資源到它們的初始化(每當拋出異常時)。

問題1:
這是否意味着我經歷了GPU的內存泄漏?或以某種方式throw, trycatch可以處理這種情況?

問題#2(?意見基礎?):
我學習C++異常體系和爲此,我想知道這是否是一個好主意,拋出異常無處不在,抓住他們只在main()功能?
我認爲這是值得考慮的,因爲那時我可以結束我的程序在「自然」的方式 - return EXIT_FAILURE;反對exit(EXIT_FAILURE);如果我的類拋出一個被捕獲的異常,是否會分配GPU內存泄漏?


ErrorCheck.h:(整個文件)

#pragma once 
#ifndef __ERROR_CHECK_H__ 
#define __ERROR_CHECK_H__ 
#include <stdexcept> 

// The function: 
template <typename T> 
bool errchk(const T check, const char* file, unsigned int line, const char* from, const char* func); 

// How To call it: 
#define ERRCHK(_check) \ 
    errchk(_check, __FILE__, __LINE__, __FUNC__, #_check) 

#endif // !__ERROR_CHECK_H__ 

ErrorCheck.cpp:(簡化版)

// Include: 
#include <cuda.h>    // cudaError_t 
#include <cuda_runtime_api.h> 
#include <cufft.h>    // cufftResult_t 
#include <cublas.h>   // cublasStatus_t 
#include <curand_kernel.h>  // curandStatus_t 
#include <cusolver_common.h> // cusolverStatus_t 
#include <cusparse.h>   // cusparseStatus_t 

#include <stdexcept> 
#include "ErrorCheck.h" 

// Functions bellow are overloaded 7 times for every error type from headers included above 
inline const bool testForError(const Type & check) { return check != SuccessValue; }; 
inline const char * getErrorName(const Type & error) { /* ... */ }; 
inline const char * getErrorString(const Type & error) { /* ... */ }; 

// The function: 
template <typename T, T successValue> 
void errchk(const T check, const char* file, unsigned int line, const char* from, const char* func) 
{ 
    if (testForError(check)) { 

     // generate error description in form of a string. 

     throw std::runtime_error(errorDescription); 
    } 
} 

// Instantiations: 
template void errchk <bool   > (const bool    check, const char * file, unsigned int line, const char * from, const char * func); 
template void errchk <cudaError_t  > (const cudaError_t  check, const char * file, unsigned int line, const char * from, const char * func); 
template void errchk <cufftResult_t > (const cufftResult_t check, const char * file, unsigned int line, const char * from, const char * func); 
template void errchk <cublasStatus_t > (const cublasStatus_t check, const char * file, unsigned int line, const char * from, const char * func); 
template void errchk <curandStatus_t > (const curandStatus_t check, const char * file, unsigned int line, const char * from, const char * func); 
template void errchk <cusolverStatus_t> (const cusolverStatus_t check, const char * file, unsigned int line, const char * from, const char * func); 
template void errchk <cusparseStatus_t> (const cusparseStatus_t check, const char * file, unsigned int line, const char * from, const char * func); 

fooClass.h:

#include "ErrorCheck.h" 
class foo 
{ 
private: 
    float * dev_floatArray; 
    float * host_floatArray; 

public: 
    foo() { 
     // Do something... 
     ERRCHK(cudaSetDevice(0)); 
     ERRCHK(cudaMalloc(&dev_floatArray, 10000 * sizeof(float))); 
     // Do something... 
     ERRCHK(cudaMemcpy(host_floatArray, dev_floatArray, 10000 * sizeof(float), cudaMemcpyDeviceToHost)); 
     // Do something... 
    } 
    ~foo() { 
     // Do something... 
     ERRCHK(cudaFree(dev_floatArray)); 
     ERRCHK(cudaDeviceReset()); 
    } 
} 

Start.cpp:

#include <iostream> 
#include "fooClass.h" 
using namespace std; 

int main() { 
    try { 
     foo bar; // constructor of "foo" Class is called. 
    } 
    catch (std::runtime_error error) { 
     cout << error.what() << endl; 
     getchar(); 
     return EXIT_FAILURE; 
    } 
} 
+3

你有沒有聽說過RAII?處理這種問題是一種非常常見的模式。 –

+0

@讓BaptisteYunès 有點不好意思承認,但從來沒有聽說過...但好我們學習生活的全部。 :) 謝謝。我認爲你的回答解決了我的兩個問題。 – cukier9a7b5

+0

不要尷尬,你不知道,但現在你做... –

回答

1

這是否意味着我經歷了GPU的內存泄漏?

是的,它的確如此。您不能確保資源最終將被釋放,否則不得分配資源;和如果cudaMemCpy()失敗,拋出一個異常 - 你沒有做這樣的保證。

或以某種方式拋出,try和catch可以處理這種情況呢?

其實,是的,那種..如@讓BaptisteYunès表明,RAII是關鍵。請仔細閱讀本:

What destructors are run when the constructor throws an exception?

所以,如果你能推你的內存分配和釋放到您的foo類的RAII成員,你會完成它的建設,因此它的析構函數 - 這會釋放 - 本來在退出foo()範圍,即使有例外運行。

在這一點上,我會說,你是重新發明輪子一些你寫的代碼。您可以在我的cuda-api-wrappers庫*中找到用於封裝CUDA錯誤的機制和異常情況以及針對您分配的內存的獨特指針式RAII持有者。所以,你會碰到這樣的:

class foo { 
public: 
    using element_type = float; 
    enum : size_t { num_elements }; 
protected: 
    struct { 
     cuda::memory::device::unique_ptr<element_type> device; 
     cuda::memory::host::unique_ptr<element_type> host; 
    } data; 

public: 
    foo() : data({ 
     cuda::memory::device::make_unique<element_type[]>(
      cuda::device::default_device_id, 
      num_elements 
     ), 
     cuda::memory::host::make_unique(num_elements) 
    }) 
    { 
     // Do something... 
     cuda::memory::copy(
      data.host.get(), data.device.get(), num_elements * sizeof(element_type) 
     ); 
     // Do something... 
    } 
    ~foo() { 
     // Do something... 

     // ERRCHK(cudaFree(dev_floatArray)); 
     // No need to free anything! It's magic! 

     // ERRCHK(cudaDeviceReset()); 
     // Don't reset your device you really need to - and 
     // you don't need to. 
    } 
} 

另一種方法,你可以考慮,而不是爲保持內存RAII類,是安德烈Alexandrescu的的「保護範圍」機制。他在這部影片解釋它(當然,它的最新版本):

CppCon 2015: Andrei Alexandrescu - Declarative Control Flow

它是一個好主意,到處拋出異常,並抓住他們只在main()函數?

做出這個單獨的問題,因爲答案不是簡單的是/否。事實上,我認爲在這裏有足夠的問題和答案。

* - 其他圖書館也可以提供類似的東西,例如也許是推力;但是這一次你沒有綁定到複雜抽取,只是CUDA運行時API的封裝。)

相關問題