2012-05-31 3 views
1

Я программирую в 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 везде, где могу, поэтому я не верю, что это проблема выравнивания. На самом деле нет других связанных вопросов.

Я понимаю, что это, вероятно, не простой вопрос, но у меня кончились идеи, пока мы не сможем получить новое оборудование в офисе для тестирования. Может кто-нибудь мне помочь?

+0

На самом деле, я думаю, проблема в другом ядре, которое я написал. Я еще не исправил проблему, и проблема возникла еще до того, как я запустил это другое ядро, поэтому наблюдения оцениваются. – Chaosed0

+1

Как общее наблюдение стиля кодирования: вы можете сделать этот код намного короче, используя векторные операции. Это будет легче читать и может также работать быстрее. например 'accel = (float4) (force [i] .x/mass [i], force [i] .y/mass [i], force [i] .z/mass [i], 0.0f);' становится ' accel = force [i]/mass [i]; ' –

+0

А, я даже не знал, что OpenCL поддерживает такую ​​операцию. Благодарю. – Chaosed0

ответ

0

Поскольку никто не ответил, я полагаю, что я просто внес свой вклад в то, что я узнал до сих пор.

У меня нет окончательного вывода, но, по крайней мере, я понял, почему он так часто делает это. Поскольку я запускаю это (и другие подобные ядра) для вычисления оценки для порядка времени, я очищаю списки, изменяя их размер, а затем повторно запускаю вычисления. Однако то, что я не делал, изменило размеры буферов. Это привело к тому, что некоторые неопределенные числа вытягивались потоками.

Однако это не решает QNaN, которые я получаю от запуска программы в течение длительных периодов времени. Они просто появляются спонтанно. Может быть, это аналогичная проблема, которую я пропускаю, но я не могу сказать. Если кто-то имеет дополнительные сведения по этому вопросу, это было бы оценено.