Why does `clEnqueueReadBuffer` break while debugging openCL host code? - opencl

__kernel void kmeans_kernel(__global float* data, int points,
__global float* centroids, int clusters,
int dimensions)
{
//extern __shared__ float storage_space[];
__local float storage_space[];
__local int iterations;
__local float *means;
__local float *index;
__local float *mindist;
__local float *s_data;
iterations = points / ( get_global_size(0)) + 1;
if( get_local_id(0) == 0 ){
s_data[get_local_id(0)] = data[get_local_id(0)];
means=&storage_space[0];
index=&storage_space[get_local_size(0)];
mindist=&storage_space[2*get_local_size(0)];
}
//data = &data[blockDim.x * blockIdx.x + threadIdx.x];
data = &data[get_global_id(0)];
while( iterations )
{
mindist[get_local_id(0)] = 3.402823466e+38F;
index[get_local_id(0)] = 0;
for( short j = 0; j < clusters; j++ )
{
if( get_local_id(0) <= dimensions )
//means[get_local_id(0)] = centroids[get_local_id(0)+j*c_pitch];
means[get_local_id(0)] = centroids[get_local_id(0)+j];
//__syncthreads();
barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);
if( !(data[get_local_id(0)] - s_data[get_local_id(0)] > points - 1) )
{
float dist = distance_gpu_transpose( means, data, dimensions);
if( dist < mindist[get_local_id(0)] )
{
mindist[get_local_id(0)] = dist;
index[get_local_id(0)] = j;
}
}
//__syncthreads();
barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);
}
if( !(data[get_local_id(0)] - s_data[get_local_id(0)] > points - 1) )
data[0] = index[get_local_id(0)];
data += (get_global_size(0));
if( get_local_id(0) == 0 )
--iterations;
//__syncthreads();
barrier(CLK_LOCAL_MEM_FENCE | CLK_GLOBAL_MEM_FENCE);
}
}
I am using AMD processor with ATI 3200 graphics card, it does not support openCL but the rest of the code is running fine on CPU itself.
The problem with my code is quite complicated for me this time. After the kernel got executed, I am unable to read the device memory variable using clEnqueueReadBuffer. while dubugging it's breaking at this point and says,
Unhandled exception at 0x10001098 in CL_kmeans.exe: 0xC000001D: Illegal Instruction.
When i press break here, it gives
No symbols are loaded for any call stack frame. The source code cannot be displayed.
What might be the problem here? please suggest me some solution.
my kernel code is as given above and the statement i use to read data is,
ret = clEnqueueReadBuffer(command_queue, gpu_data, CL_TRUE, 0,sizeof( float ) * instances->cols* 1 , instances->data, 0, NULL, NULL);
How can I check the possible errors here?

Related

OpenCL undefined behavior in parallel reduction algorithm

I am working on a simple parallel reduction algorithm to find the minimum value in an array and am coming across some interesting undefined behavior in my algorithm. I am running Intel's OpenCL 1.2 on Ubuntu 16.04.
The following kernel is what I am trying to run which is currently giving me the wrong answer:
__kernel void Find_Min(int arraySize, __global double* scratch_arr, __global double* value_arr, __global double* min_arr){
const int index = get_global_id(0);
int length = (int)sqrt((double)arraySize);
int start = index*length;
double min_val = INFINITY;
for(int i=start; i<start+length && i < arraySize; i++){
if(value_arr[i] < min_val)
min_val = value_arr[i];
}
scratch_arr[index] = min_val;
barrier(CLK_GLOBAL_MEM_FENCE);
if(index == 0){
double totalMin = min_val;
for(int i=1; i<length; i++){
if(scratch_arr[i] < totalMin)
totalMin = scratch_arr[i];
}
min_arr[0] = totalMin;
}
}
When in put in an array that is {0,-1,-2,-3,-4,-5,-6,-7,-8} it ends up returning -2.
Here is where the undefined behavior comes in. When I run the following kernel with a printf statement before the barrier I get the right answer (-8):
__kernel void Find_Min(int arraySize, __global double* scratch_arr, __global double* value_arr, __global double* min_arr){
const int index = get_global_id(0);
int length = (int)sqrt((double)arraySize);
int start = index*length;
double min_val = INFINITY;
for(int i=start; i<start+length && i < arraySize; i++){
if(value_arr[i] < min_val)
min_val = value_arr[i];
}
scratch_arr[index] = min_val;
printf("setting scratch[%i] to %f\n", index, min_val);
barrier(CLK_GLOBAL_MEM_FENCE);
if(index == 0){
double totalMin = min_val;
for(int i=1; i<length; i++){
if(scratch_arr[i] < totalMin)
totalMin = scratch_arr[i];
}
min_arr[0] = totalMin;
}
}
The only thing I can think of that could be happening is that I am using the barrier command incorrectly and all the printf is doing is causing a delay in the kernel that is somehow synchronizing the calls so they all complete before the final reduction step. But without the printf, the kernel 0 executes the final reduction before the other kernels are finished.
Does anyone else have any suggestions or tips on how to debug this issue?
Thanks in advance!!
The problem was that the kernel was being launched with one thread per workgroup and barriers only work within a work group. See this response to a similar question: Open CL no synchronization despite barrier

CL_DEVICE_NOT_AVAILABLE on casting int to float

I can't seem to find any good info anywhere for what I've run into. I've written a bit of code for Kohonen SOM in OpenCL, on a iMac w/ a ATI Radeon HD 6770M. I'm choosing the GPU device for the context. There is a single line in my code that is causing a CL_DEVICE_NOT_AVAILABLE error. If I comment it out, code compiles fine... but with it, and the variations I've tried, I consistently get the error.
Here's the code, with the offending line commented:
"// THIS line, only, causes CL_DEVICE_NOT_AVAILABLE !!!".
I'm hoping one of you guys has run into this at some point, as I'm a little baffled. convert_float(diff) did not work for me.
There are bound to be computational errors, as I haven't gotten beyond the essential complete-compile step, so feel free to ignore or point those out. Either way, I'm really just trying to get beyond the compile.
inline float _calc_sample_distance(__global float* weights, ulong startIdx, uint nodeWidth, __constant float* sample) {
float accum = 0.0f;
float diff = 0.0f;
uint i = 0;
for(i = 0; i<nodeWidth; i++) {
diff = weights[startIdx+i] - sample[i];
accum += pow(diff,2);
}
accum = pow(accum, .5f);
return accum;
}
inline void _calc_coords(uint dimCount, __constant uint* dimSizes, size_t offset, uint* thisCoords) {
// reversed so, processed as xy, then y
ulong trim = offset, multi = 0;
int i = 0, j = 0;
for(i = dimCount-1; i>=0; i--) {
multi = 1;
for(j=i-1; j>=0; j--) {
multi *= dimSizes[j];
}
thisCoords[i] = trim / multi;
trim = trim % multi;
}
}
inline float _calc_map_coord_distance(uint dimCount, __constant uint* bmuCoords, uint* thisCoords) {
float accum = 0.0f;
uint i = 0;
int diff = 0;
for(i = 0; i < dimCount; i++) {
diff = bmuCoords[i] - thisCoords[i];
diff *= diff;
accum += (float)diff; // THIS line, only, causes CL_DEVICE_NOT_AVAILABLE !!!
}
accum = pow(accum,.5f);
return accum;
}
__kernel void calc_kohonen_som_distances(
// map data
__global float* weights, // weights
uint nodeWidth, // the number of weights per node
uint nodeCount, // the total number of weights
__constant float* sample, // sample, of nodeWidth wide
__global float* output // the output distance of each node to the sample
) {
size_t nodeIndex = get_global_id(0);
ulong startIdx = nodeIndex * nodeWidth;
output[nodeIndex] = _calc_sample_distance(weights,startIdx,nodeWidth,sample);
}
__kernel void calc_kohonen_som_update_weights(
// map data
__global float* weights, // weights
uint nodeWidth, // the number of weights per node
uint dimCount, // the number of dimensions
__constant uint* dimSizes, // the size of each dimension
__constant float *sampleData, // the sample to use for updating the bmu and surrounding units
__constant uint* bmuCoords, // the coordinates of the best matching unit, from which we derive offset
float learningRate, // calculated on the CPU as per step
float radius // calculated on the CPU as per step
) {
size_t nodeIndex = get_global_id(0);
ulong startIdx = nodeIndex * nodeWidth;
uint* thisCoords = (uint*)malloc(sizeof(uint)*dimCount);
memset(thisCoords,0,sizeof(uint)*dimCount);
// determine the coordinates of the offset provided
if(dimCount!=1) {
_calc_coords(dimCount,dimSizes,nodeIndex,thisCoords);
} else {
thisCoords[0] = nodeIndex;
}
float distance = _calc_map_coord_distance(dimCount, bmuCoords, thisCoords);
if(distance<radius) {
float influence = exp( (-1*distance)/(2*pow(radius,2.0f)) );
for(uint i=0;i<dimCount;i++) {
weights[startIdx+i] = weights[startIdx+i] + ( influence * learningRate * (sampleData[i] - weights[startIdx+i]) );
}
}
}

OpenCL: Inserting local atomic_inc to reduction kernel

I am trying to include a local atomic similar to that described by DarkZeros here within a working reduction kernel. The kernel finds a largest value within a set of points; the aim of the local atomic is to allow me to filter selected point_ids into an output array without any gaps.
At present when I use the local atomic to increment the addition to a local array the kernel runs but produces a wrong overall highest point. If the atomic line is commented out then a correct result returns.
What is going on here and how do I fix it?
Simplified kernel code:
__kernel void reduce(__global const float4* dataSet, __global const int* input, const unsigned int items, //points and index
__global int* output, __local float4* shared, const unsigned int n, //finding highest
__global int* filtered, __global const float2* tri_input, const unsigned int pass, //finding filtered
__global int* global_count //global count
){
//set everything up
const unsigned int group_id = get_global_id(0) / get_local_size(0);
const unsigned int local_id = get_local_id(0);
const unsigned int group_size = items;
const unsigned int group_stride = 2 * group_size;
const int local_stride = group_stride * group_size;
__local float4 *zeroIt = &shared[local_id];
zeroIt->x = 0; zeroIt->y = 0; zeroIt->z = 0; zeroIt->w = 0;
volatile __local int local_count_set_1;
volatile __local int global_val_set_1;
volatile __local int filter_local[64];
if(local_id==0){
local_count_set_1 = 0;
global_val_set_1 = -1;
}
barrier(CLK_LOCAL_MEM_FENCE);
int i = group_id * group_stride + local_id;
while (i < n){
//load up a pair of points using the index to locate them within a massive dataSet
int ia = input[i];
float4 a = dataSet[ia-1];
int ib = input[i + group_size];
float4 b = dataSet[ib-1];
//on the first pass kernel increment a local count
if(pass == 0){
filter_local[atomic_inc(&local_count_set_1)] = 1; //including this line causes an erroneous highest point result
//filter_local[local_id] = 1; //but including this line does not
//atomic_inc(&local_count_set_1); //and neither does this one
}
//find the highest of the pair
float4 result;
if(a.z>b.z) result = a;
else result = b;
//load up the previous highest result locally
float4 s = shared[local_id];
//if the previous highest beat this, stick, else twist
if(s.z>result.z){ result = s; }
shared[local_id] = result;
i += local_stride;
}
barrier(CLK_LOCAL_MEM_FENCE);
if (group_size >= 512){
if (local_id < 256) {
__local float4 *a = &shared[local_id];
__local float4 *b = &shared[local_id+256];
if(b->z>a->z){ shared[local_id] = shared[local_id+256]; }
}}
//repeat barrier ops in increments down to group_size>=2 - this filters the highest result in shared
//finally, return the filtered highest result of shared to the global level
barrier(CLK_LOCAL_MEM_FENCE);
if(local_id == 0){
__local float4 *v = &shared[0];
int send = v->w ;
output[group_id] = send+1;
}}
[UPDATE]: When the atomic_inc line is included the 'wrong' highest point result is always a point near the end of the test dataset. I'm guessing that this means that the atomic_inc is affecting a latter comparison, but I'm not sure exactly what or where yet.
[UPDATE]: Edited code to simplify/clarify/update with debugging tweaks. Still not working and it is driving me loopy.
Total face-palm moment. In the setup phase of the kernel there are the lines:
if(local_id==0){
local_count_set_1 = 0;
global_val_set_1 = -1;
}
barrier(CLK_LOCAL_MEM_FENCE);
When these are split and the local_count_set_1 is included within the while loop, the error does not occur. i.e:
if(local_id==0) global_val_set_1 = -1;
barrier(CLK_LOCAL_MEM_FENCE);
while (i < n){
if(local_id==0) local_count_set_1 = 0;
barrier(CLK_LOCAL_MEM_FENCE);
....
if(pass = 0){
filter_local[atomic_inc(&local_count_set_1)] = 1;
}
....
I'm hoping this fixes the issue // will update if not.
Aaaand that's a weekend I'll never get back.

Optimal workgroup size for sum reduction in OpenCL

I am using the following kernel for sum reduciton.
__kernel void reduce(__global float* input, __global float* output, __local float* sdata)
{
// load shared mem
unsigned int tid = get_local_id(0);
unsigned int bid = get_group_id(0);
unsigned int gid = get_global_id(0);
unsigned int localSize = get_local_size(0);
unsigned int stride = gid * 2;
sdata[tid] = input[stride] + input[stride + 1];
barrier(CLK_LOCAL_MEM_FENCE);
// do reduction in shared mem
for(unsigned int s = localSize >> 2; s > 0; s >>= 1)
{
if(tid < s)
{
sdata[tid] += sdata[tid + s];
}
barrier(CLK_LOCAL_MEM_FENCE);
}
// write result for this block to global mem
if(tid == 0) output[bid] = sdata[0];
}
It works fine, but I don't know how to choose the optimal workgroup size or number of workgroups if I need more than one workgroup (for example if I want to calculate the sum of 1048576 elements). As far as I understand, the more workgroups I use, the more subresults I will get, which also means that I will need more global reductions at the end.
I've seen the answers to the general workgroup size question here. Are there any recommendations that concern reduction operations specifically?
This question is a possible duplicate of one I answered a while back:
What is the algorithm to determine optimal work group size and number of workgroup.
Experimentation will be the best way to know for sure for any given device.
Update:
I think you can safely stick to 1-dimensional work groups, as you have done in your sample code. On the host, you can try out the best values.
For each device:
1) query for CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE.
2) loop over a few multiples and run the kernel with that group size. save the execution time for each test.
3) when you think you have an optimal value, hard code it into a new kernel for use with that specific device. This will give a further boost to performance. You can also eliminate your sdata parameter in the device-specific kernel.
//define your own context, kernel, queue here
int err;
size_t global_size; //set this somewhere to match your test data size
size_t preferred_size;
size_t max_group_size;
err = clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, sizeof(size_t), preferred_size, NULL);
//check err
err = clGetKernelWorkGroupInfo(kernel, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), max_group_size, NULL);
//check err
size_t test_size;
//your vars for hi-res timer go here
for (unsigned int i=preferred_size ; i<=max_group_size ; i+=preferred_size){
//reset timer
test_size = (size_t)i;
err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &global_size, &test_size, 0, NULL, NULL);
if(err){
fail("Unable to enqueue kernel"); //implement your own fail function somewhere..
}else{
clfinish(queue);
//stop timer, save value
//output timer value and test_size
}
}
The device-specific kernel can look like this, except the first line should have your optimal value substituted:
#define LOCAL_SIZE 32
__kernel void reduce(__global float* input, __global float* output)
{
unsigned int tid = get_local_id(0);
unsigned int stride = get_global_id(0) * 2;
__local float sdata[LOCAL_SIZE];
sdata[tid] = input[stride] + input[stride + 1];
barrier(CLK_LOCAL_MEM_FENCE);
for(unsigned int s = LOCAL_SIZE >> 2; s > 0; s >>= 1){
if(tid < s){
sdata[tid] += sdata[tid + s];
}
barrier(CLK_LOCAL_MEM_FENCE);
}
if(tid == 0) output[get_group_id(0)] = sdata[0];
}

Optimizing kernel shuffled keys code - OpenCL

I have just started getting into OpenCL and going through the basics of writing a kernel code. I have written a kernel code for calculating shuffled keys for points array. So, for a number of points N, the shuffled keys are calculated in 3-bit fashion, where x-bit at depth d (0
xd = 0 if p.x < Cd.x
xd = 1, otherwise
The Shuffled xyz key is given as:
x1y1z1x2y2z2...xDyDzD
The Kernel code written is given below. The point is inputted in a column major format.
__constant float3 boundsOffsetTable[8] = {
{-0.5,-0.5,-0.5},
{+0.5,-0.5,-0.5},
{-0.5,+0.5,-0.5},
{-0.5,-0.5,+0.5},
{+0.5,+0.5,-0.5},
{+0.5,-0.5,+0.5},
{-0.5,+0.5,+0.5},
{+0.5,+0.5,+0.5}
};
uint setBit(uint x,unsigned char position)
{
uint mask = 1<<position;
return x|mask;
}
__kernel void morton_code(__global float* point,__global uint*code,int level, float3 center,float radius,int size){
// Get the index of the current element to be processed
int i = get_global_id(0);
float3 pt;
pt.x = point[i];pt.y = point[size+i]; pt.z = point[2*size+i];
code[i] = 0;
float3 newCenter;
float newRadius;
if(pt.x>center.x) code = setBit(code,0);
if(pt.y>center.y) code = setBit(code,1);
if(pt.z>center.z) code = setBit(code,2);
for(int l = 1;l<level;l++)
{
for(int i=0;i<8;i++)
{
newRadius = radius *0.5;
newCenter = center + boundOffsetTable[i]*radius;
if(newCenter.x-newRadius<pt.x && newCenter.x+newRadius>pt.x && newCenter.y-newRadius<pt.y && newCenter.y+newRadius>pt.y && newCenter.z-newRadius<pt.z && newCenter.z+newRadius>pt.z)
{
if(pt.x>newCenter.x) code = setBit(code,3*l);
if(pt.y>newCenter.y) code = setBit(code,3*l+1);
if(pt.z>newCenter.z) code = setBit(code,3*l+2);
}
}
}
}
It works but I just wanted to ask if I am missing something in the code and if there is an way to optimize the code.
Try this kernel:
__kernel void morton_code(__global float* point,__global uint*code,int level, float3 center,float radius,int size){
// Get the index of the current element to be processed
int i = get_global_id(0);
float3 pt;
pt.x = point[i];pt.y = point[size+i]; pt.z = point[2*size+i];
uint res;
res = 0;
float3 newCenter;
float newRadius;
if(pt.x>center.x) res = setBit(res,0);
if(pt.y>center.y) res = setBit(res,1);
if(pt.z>center.z) res = setBit(res,2);
for(int l = 1;l<level;l++)
{
for(int i=0;i<8;i++)
{
newRadius = radius *0.5;
newCenter = center + boundOffsetTable[i]*radius;
if(newCenter.x-newRadius<pt.x && newCenter.x+newRadius>pt.x && newCenter.y-newRadius<pt.y && newCenter.y+newRadius>pt.y && newCenter.z-newRadius<pt.z && newCenter.z+newRadius>pt.z)
{
if(pt.x>newCenter.x) res = setBit(res,3*l);
if(pt.y>newCenter.y) res = setBit(res,3*l+1);
if(pt.z>newCenter.z) res = setBit(res,3*l+2);
}
}
}
//Save the result
code[i] = res;
}
Rules to optimize:
Avoid Global memory (you were using "code" directly from global memory, I changed that), you should see 3x increase in performance now.
Avoid Ifs, use "select" instead if it is possible. (See OpenCL documentation)
Use more memory inside the kernel. You don't need to operate at bit level. Operation at int level would be better and could avoid huge amount of calls to "setBit". Then you can construct your result at the end.
Another interesting thing. Is that if you are operating at 3D level, you can just use float3 variables and compute the distances with OpenCL operators. This can increase your performance quite a LOT. BUt also requires a complete rewrite of your kernel.

Resources