[英]cuFFT cannot recover after an error
在上一次启动失败后,我找不到启动 cuFFT 处理的方法。
这是一个最小的例子。 主要思想如下:我们创建一个简单的 cuFTT 处理器,可以管理其资源(设备 memory 和 cuFFT 计划)。 我们检查该处理器是否进行 FFT。 然后我们要求创建太多计划,因此我们强制执行 cuFFT 错误。 然后我们释放所有资源并尝试重复启动成功。 但是,处理器在失败后无能为力。
首先,这是一个相当长的序言:
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
#include <vector>
using std::vector;
#include "cuda_runtime.h"
#include "cufft.h"
// cuFFT API errors
static char* _cufftGetErrorEnum( cufftResult_t error )
{
switch ( error )
{
case CUFFT_SUCCESS:
return "CUFFT_SUCCESS";
case CUFFT_INVALID_PLAN:
return "cuFFT was passed an invalid plan handle";
case CUFFT_ALLOC_FAILED:
return "cuFFT failed to allocate GPU or CPU memory";
// No longer used
case CUFFT_INVALID_TYPE:
return "CUFFT_INVALID_TYPE";
case CUFFT_INVALID_VALUE:
return "User specified an invalid pointer or parameter";
case CUFFT_INTERNAL_ERROR:
return "Driver or internal cuFFT library error";
case CUFFT_EXEC_FAILED:
return "Failed to execute an FFT on the GPU";
case CUFFT_SETUP_FAILED:
return "The cuFFT library failed to initialize";
case CUFFT_INVALID_SIZE:
return "User specified an invalid transform size";
// No longer used
case CUFFT_UNALIGNED_DATA:
return "CUFFT_UNALIGNED_DATA";
case CUFFT_INCOMPLETE_PARAMETER_LIST:
return "Missing parameters in call";
case CUFFT_INVALID_DEVICE:
return "Execution of a plan was on different GPU than plan creation";
case CUFFT_PARSE_ERROR:
return "Internal plan database error";
case CUFFT_NO_WORKSPACE:
return "No workspace has been provided prior to plan execution";
case CUFFT_NOT_IMPLEMENTED:
return "CUFFT_NOT_IMPLEMENTED";
case CUFFT_LICENSE_ERROR:
return "CUFFT_LICENSE_ERROR";
}
return "<unknown>";
}
// check cuda runtime calls
bool cudaCheck( cudaError_t err )
{
if ( err != cudaSuccess )
{
cudaDeviceSynchronize();
cerr << cudaGetErrorString( cudaGetLastError() ) << endl;
return false;
}
return true;
}
// check cuFFT calls
bool cufftCheck( cufftResult_t err )
{
if ( err != CUFFT_SUCCESS )
{
cerr << _cufftGetErrorEnum( err ) << endl;
return false;
}
return true;
}
接下来,我们定义一个简单的 cuFFT 处理器,它可以管理其资源(设备 memory 和 cuFFT 计划)
class CCuFFT_Processor
{
vector<cufftHandle> _plans;
cufftComplex *_data;
size_t _data_bytes;
// Release resouces
bool ReleaseAll();
bool ReleaseMemory();
bool ReleasePlans();
public:
CCuFFT_Processor() :
_data( NULL ),
_data_bytes( 0 )
{
_plans.reserve( 32 );
_plans.clear();
}
~CCuFFT_Processor()
{
ReleaseAll();
}
bool Run();
bool Alloc( size_t data_len, size_t batch_len );
};
下面是我们将如何释放资源:
bool CCuFFT_Processor::ReleaseMemory()
{
bool chk = true;
if ( _data != NULL )
{
chk = cudaCheck( cudaFree( _data ) );
_data = NULL;
_data_bytes = 0;
}
return chk;
}
bool CCuFFT_Processor::ReleasePlans()
{
bool chk = true;
for ( auto & p : _plans )
chk = chk && cufftCheck( cufftDestroy( p ) );
_plans.clear();
return chk;
}
bool CCuFFT_Processor::ReleaseAll()
{
bool chk = true;
chk = chk && cudaCheck( cudaDeviceSynchronize() );
chk = chk && ReleaseMemory();
chk = chk && ReleasePlans();
chk = chk && cudaCheck( cudaDeviceReset() );
return chk;
}
下面是主要功能的实现:
bool CCuFFT_Processor::Alloc( size_t data_len, size_t batch_len )
{
bool chk = true;
size_t bytes = sizeof( cufftComplex ) * data_len * batch_len;
// CUDA resources
if ( _data_bytes < bytes )
chk = chk && ReleaseMemory();
if ( _data == NULL )
{
chk = chk && cudaCheck( cudaMalloc( (void **)&_data, bytes ) );
_data_bytes = bytes;
}
// cuFFT resources
chk = chk && ReleasePlans();
for ( size_t b = 1; chk && ( b <= batch_len ); b *= 2 )
{
cufftHandle new_plan;
chk = cufftCheck(
cufftPlan1d( &new_plan, int(data_len), CUFFT_C2C, int(b) ) );
if ( chk )
_plans.push_back( new_plan );
}
if ( !chk )
ReleaseAll();
return chk;
}
bool CCuFFT_Processor::Run()
{
bool chk = true;
chk = cufftCheck(
cufftExecC2C( *_plans.rbegin(), _data, _data, CUFFT_FORWARD ) );
if ( !chk )
ReleaseAll();
chk = chk && cudaCheck( cudaDeviceSynchronize() );
return chk;
}
最后,程序
int main()
{
size_t batch = 1 << 5;
size_t length = 1 << 21;
CCuFFT_Processor proc;
// Normal run
if ( proc.Alloc( length, batch ) )
proc.Run();
// Run with error
length *= 4;
if ( proc.Alloc( length, batch ) )
proc.Run();
// Normal run : check recovery
length /= 4;
if ( proc.Alloc( length, batch ) )
proc.Run();
return EXIT_SUCCESS;
}
如果我使用较小的length = 1 << 18
,则不会发生错误。 但是,对于较大的length = 1 << 21
会出现两个错误:
cuFFT failed to allocate GPU or CPU memory
Failed to execute an FFT on the GPU
第一个错误是预料之中的,我们是故意这样做的。 但是第二个不是。 尽管设备已重置并且新资源已成功分配,但 cuFFT 无法执行 FFT。
我使用 GTX 970。我尝试了以下所有组合:cuda 6.5、cuda 7.5、32 位平台、64 位平台等,但均未成功。
这显然是一个仅限于旧版本 cuFFT 的内存不足错误恢复行为的问题,并在 CUDA 8 发布周期中得到纠正。 如果(6 年后)您仍在使用 CUDA 8 之前的 cuFFT 版本,请更新到更现代的版本,此问题将得到解决。
[根据评论收集的答案并添加为社区 wiki 条目,以便将问题从 CUDA 和 cuFFT 标签的未回答列表中删除]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.