2017-03-27 26 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的封装。)

相关问题