Я программирую в OpenCL с помощью привязок C++. У меня есть проблема, когда на аппаратном обеспечении NVidia мой код OpenCL самопроизвольно производит очень большие числа, а затем (в следующем прогоне) «1. # QNaN». Мой код представляет собой довольно простое физическое моделирование с использованием уравнения x = vt * .5at^2. Единственное, что я заметил в этом необычном, это то, что скорости частиц внезапно взлетают примерно до 6e + 34, что, как я предполагаю, является максимальным значением с плавающей запятой на этой машине. Тем не менее, скорости/силы до этого довольно малы, часто со значениями меньше 1.OpenCL производит QNaN на аппаратных средствах NVidia
Конкретный GPU, который я использую, - это Tesla M2050 с последними драйверами. Я прототип на своем ноутбуке, используя мой процессор AMD Fusion в качестве платформы (у него нет выделенного графического процессора), и проблема там не возникает. Я не уверен, что это проблема драйвера NVidia, проблема с моим вычислением или что-то еще.
Вот код ядра (Примечание: Я достаточно уверен, что масса всегда отлична от нуля):
_kernel void update_atom(__global float4 *pos, __global float4 *vel, __global float4 *force,
__global const float *mass, __global const float *radius, const float timestep, const int wall)
{
// Get the index of the current element to be processed
int i = get_global_id(0);
float constMult;
float4 accel;
float4 part;
//Update the position, velocity and force
accel = (float4)(force[i].x/mass[i],
force[i].y/mass[i],
force[i].z/mass[i],
0.0f);
constMult = .5*timestep*timestep;
part = (float4)(constMult*accel.x,
constMult*accel.y,
constMult*accel.z, 0.0f);
pos[i] = (float4)(pos[i].x + vel[i].x*timestep + part.x,
pos[i].y + vel[i].y*timestep + part.y,
pos[i].z + vel[i].z*timestep + part.z,
0.0f);
vel[i] = (float4)(vel[i].x + accel.x*timestep,
vel[i].y + accel.y*timestep,
vel[i].z + accel.z*timestep,
0.0f);
force[i] = (float4)(force[i].x,
force[i].y,
force[i].z,
0.0f);
//Do reflections off the wall
//http://www.3dkingdoms.com/weekly/weekly.php?a=2
float4 norm;
float bouncePos = wall - radius[i];
float bounceNeg = radius[i] - wall;
norm = (float4)(0.0f, 0.0f, 0.0f, 0.0f);
if(pos[i].x >= bouncePos)
{
//Normal is unit YZ vector
pos[i].x = bouncePos;
norm.x = 1.0f;
}
else if(pos[i].x <= bounceNeg)
{
pos[i].x = bounceNeg;
norm.x = -1.0f;
}
if(pos[i].y >= bouncePos)
{
//Normal is unit XZ vector
pos[i].y = bouncePos;
norm.y = 1.0f;
}
else if(pos[i].y <= bounceNeg)
{
//etc
pos[i].y = bounceNeg;
norm.y = -1.0f;
}
if(pos[i].z >= bouncePos)
{
pos[i].z = bouncePos;
norm.z = 1.0f;
}
else if(pos[i].z <= bounceNeg)
{
pos[i].z = bounceNeg;
norm.z = -1.0f;
}
float dot = 2 * (vel[i].x * norm.x + vel[i].y * norm.y + vel[i].z * norm.z);
vel[i].x = vel[i].x - dot * norm.x;
vel[i].y = vel[i].y - dot * norm.y;
vel[i].z = vel[i].z - dot * norm.z;
}
А вот как хранить информацию в ядро. PutData просто использует функцию std :: vector :: push_back по позициям, скоростям и силам атомов в соответствующие векторы, а ядро - это просто класс оболочки, который я написал для библиотек OpenCL (вы можете доверять мне, что я правильно параметры в нужные места для функций enqueue).
void LoadAtoms(int kernelNum, bool blocking)
{
std::vector<cl_float4> atomPos;
std::vector<cl_float4> atomVel;
std::vector<cl_float4> atomForce;
for(int i = 0; i < numParticles; i++)
atomList[i].PutData(atomPos, atomVel, atomForce);
kernel.EnqueueWriteBuffer(kernelNum, posBuf, blocking, 0, numParticles*sizeof(cl_float4), &atomPos[0]);
kernel.EnqueueWriteBuffer(kernelNum, velBuf, blocking, 0, numParticles*sizeof(cl_float4), &atomVel[0]);
kernel.EnqueueWriteBuffer(kernelNum, forceBuf, blocking, 0, numParticles*sizeof(cl_float4), &atomForce[0]);
}
void LoadAtomTypes(int kernelNum, bool blocking)
{
std::vector<cl_float> mass;
std::vector<cl_float> radius;
int type;
for(int i = 0; i < numParticles; i++)
{
type = atomList[i].GetType();
mass.push_back(atomTypes[type].mass);
radius.push_back(atomTypes[type].radius);
}
kernel.EnqueueWriteBuffer(kernelNum, massBuf, blocking, 0, numParticles*sizeof(cl_float), &mass[0]);
kernel.EnqueueWriteBuffer(kernelNum, radiusBuf, blocking, 0, numParticles*sizeof(cl_float), &radius[0]);
}
В моем коде, как всегда, есть больше, но это то, что связано с ядром.
Я видел this question, что похоже, но я использую cl_float4 везде, где могу, поэтому я не верю, что это проблема выравнивания. На самом деле нет других связанных вопросов.
Я понимаю, что это, вероятно, не простой вопрос, но у меня кончились идеи, пока мы не сможем получить новое оборудование в офисе для тестирования. Может кто-нибудь мне помочь?
На самом деле, я думаю, проблема в другом ядре, которое я написал. Я еще не исправил проблему, и проблема возникла еще до того, как я запустил это другое ядро, поэтому наблюдения оцениваются. – Chaosed0
Как общее наблюдение стиля кодирования: вы можете сделать этот код намного короче, используя векторные операции. Это будет легче читать и может также работать быстрее. например 'accel = (float4) (force [i] .x/mass [i], force [i] .y/mass [i], force [i] .z/mass [i], 0.0f);' становится ' accel = force [i]/mass [i]; ' –
А, я даже не знал, что OpenCL поддерживает такую операцию. Благодарю. – Chaosed0