如何使用 shared_ptr 实现 CUDA API 类型 cudaEvent_t 的 RAII
How to implement RAII of CUDA API type cudaEvent_t using shared_ptr
CUDA API 具有需要调用 create() 和 destroy() 的类型,类似于内存分配新建和删除。本着 RAII 的精神,我不必调用 cudaEventCreate( &event) 和 cudaEventDestory( event ),而是为 cudaEvent_t.
编写了以下包装器
我的问题:这个代码是否可以接受,没有任何明显的错误?
它是为我构建的,我还没有发现问题。但我特别不喜欢 reinterpret_cast<> 用于通过 shared_ptr.
的自定义分配器和删除器获取 cudaEvent_t 变量的技巧
一些相关帖子:
CUDA: Wrapping device memory allocation in C++
class CudaEvent {
private:
struct Deleter {
void operator()(cudaEvent_t * ptr) const {
checkCudaErrors( cudaEventDestroy( reinterpret_cast<cudaEvent_t>(ptr) ));
}
};
shared_ptr<cudaEvent_t> Allocate( ){
cudaEvent_t event;
checkCudaErrors( cudaEventCreate( &event ) );
shared_ptr<cudaEvent_t> p( reinterpret_cast<cudaEvent_t*>(event), Deleter() );
return p;
}
shared_ptr<cudaEvent_t> ps;
public:
cudaEvent_t event;
CudaEvent( )
: ps( Allocate( ) ),
event( *(ps.get()) )
{ }
};
您将两个独立的机制混为一谈:用于 CUDA 事件的 RAII class 和使用共享指针的生命周期管理。这些应该是完全分开的。
另一个问题是不清楚你的 "checkCudaErrors" 应该做什么。
最后一个问题是提到的 one talonmies,如果 scope/lifetime 错误,就会发生这种情况。例如 - 您在发布对此事件的最后一次引用之前重置了设备。或者 - 您将此事件排入流中,然后将点拖放到它。因此,使用共享指针并不能真正保证安全——您必须像只有 id 一样跟踪事物。事实上,这可能会使事情变得更加困难。
最后,请注意,您可以将 CUDA 运行时 API 与 modern-C++ 包装器一起使用,特别是使用 RAII 而不是 createXYZ() 和 destroyXYZ():
https://github.com/eyalroz/cuda-api-wrappers
具体可以看看:
-
cuda::event_t
class Doxygen documentation.
- example program 使用和管理事件。
应有的披露:我是这个库的作者。
你假设 cudaEvent_t 可以与 (void *) 相互转换:
shared_ptr<cudaEvent_t> Allocate( ){
cudaEvent_t event;
checkCudaErrors( cudaEventCreate( &event ) );
shared_ptr<cudaEvent_t> p( reinterpret_cast<cudaEvent_t*>(event), Deleter() );
return p;
}
“正确的”强制转换会更糟,因为它会像 int *Allocate() { int i; return &i; }
.
一样创建悬空引用
正确的 C++ 模式是将 cudaEvent_t 与 class 的生命周期相关联(并实现 move/copy 构造函数)或者直接使用指向 cudaEvent_t.
std::shared_ptr<cudaEvent_t> event (
new cudaEvent_t,
[](cudaEvent_t *e){ cudaEventDestroy(*e); });
cudaEventCreate( event.get() );
CUDA API 具有需要调用 create() 和 destroy() 的类型,类似于内存分配新建和删除。本着 RAII 的精神,我不必调用 cudaEventCreate( &event) 和 cudaEventDestory( event ),而是为 cudaEvent_t.
编写了以下包装器我的问题:这个代码是否可以接受,没有任何明显的错误?
它是为我构建的,我还没有发现问题。但我特别不喜欢 reinterpret_cast<> 用于通过 shared_ptr.
的自定义分配器和删除器获取 cudaEvent_t 变量的技巧一些相关帖子:
CUDA: Wrapping device memory allocation in C++
class CudaEvent {
private:
struct Deleter {
void operator()(cudaEvent_t * ptr) const {
checkCudaErrors( cudaEventDestroy( reinterpret_cast<cudaEvent_t>(ptr) ));
}
};
shared_ptr<cudaEvent_t> Allocate( ){
cudaEvent_t event;
checkCudaErrors( cudaEventCreate( &event ) );
shared_ptr<cudaEvent_t> p( reinterpret_cast<cudaEvent_t*>(event), Deleter() );
return p;
}
shared_ptr<cudaEvent_t> ps;
public:
cudaEvent_t event;
CudaEvent( )
: ps( Allocate( ) ),
event( *(ps.get()) )
{ }
};
您将两个独立的机制混为一谈:用于 CUDA 事件的 RAII class 和使用共享指针的生命周期管理。这些应该是完全分开的。
另一个问题是不清楚你的 "checkCudaErrors" 应该做什么。
最后一个问题是提到的 one talonmies,如果 scope/lifetime 错误,就会发生这种情况。例如 - 您在发布对此事件的最后一次引用之前重置了设备。或者 - 您将此事件排入流中,然后将点拖放到它。因此,使用共享指针并不能真正保证安全——您必须像只有 id 一样跟踪事物。事实上,这可能会使事情变得更加困难。
最后,请注意,您可以将 CUDA 运行时 API 与 modern-C++ 包装器一起使用,特别是使用 RAII 而不是 createXYZ() 和 destroyXYZ():
https://github.com/eyalroz/cuda-api-wrappers
具体可以看看:
-
cuda::event_t
class Doxygen documentation. - example program 使用和管理事件。
应有的披露:我是这个库的作者。
你假设 cudaEvent_t 可以与 (void *) 相互转换:
shared_ptr<cudaEvent_t> Allocate( ){
cudaEvent_t event;
checkCudaErrors( cudaEventCreate( &event ) );
shared_ptr<cudaEvent_t> p( reinterpret_cast<cudaEvent_t*>(event), Deleter() );
return p;
}
“正确的”强制转换会更糟,因为它会像 int *Allocate() { int i; return &i; }
.
正确的 C++ 模式是将 cudaEvent_t 与 class 的生命周期相关联(并实现 move/copy 构造函数)或者直接使用指向 cudaEvent_t.
std::shared_ptr<cudaEvent_t> event (
new cudaEvent_t,
[](cudaEvent_t *e){ cudaEventDestroy(*e); });
cudaEventCreate( event.get() );