Edit: Compiling for AMD GPU and for Intel CPU gives the same error.
It is all the code, I just deleted a few blank lines for posting it here.
My kernel
__kernel void distances(constant float* data, int n, int D, int k, global float* centroids, global int* cluster, global float* distances) {
local float l_distances[64];
private int idg = get_global_id(0);
private int idl = get_local_id(0);
private double sqdist = 0;
for(int i=0; i<k;i++){
sqdist=0;
for (int j=0; j<D;j++){
sqdist = fma((double)(centroids[i*D]-data[D*idg+j]), (double)(centroids[i*D]-data[D*idg+j]),sqdist);
}
l_distances[k*idl+i] = sqrt(sqdist);
}
private float d_min=0;
for(int i=0;i<k;i++){
d_min = (d_min<l_distances[k*idl+i]) ? l_distances[k*idl+i] : d_min;
cluster[idg] = (d_min==l_distances[k*idl+i]) ? i : cluster[idg];
}
distances[idg] = l_distances[idl];
}
gives the following errors:
...Temp\OCL1304T1.cl", line 35: error: expected a
declaration
]=l_distances[idl];
^
...Temp\OCL1304T1.cl", line 41: error: expected a
declaration
}
^
why?
In this line: distances[idg] = l_distances[idl]; you are assigning to distances which is both a variable and the name the of your function. I recommend changing one of them.
Related
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
everyone.
I got this kernel:
__kernel void FuncionCL(__global char* in, __global char* out, __global int* S2)
{
__private int op1, op2, op3;
__private int C;
__private uint WorkDim, C2;
op1 = 1;
op2 = 2;
WorkDim = get_global_size(0);
__private int ID;
ID = get_global_id(0);
for(C = 0; C < 1000000; C++)
{
for(C2 = ID; C2 < 1000; C2 += WorkDim)
{
op3 = op1 + op2;
}
}
out[0] = 90;
out[1] = 89;
*S2 = (int) WorkDim;
}
It crashes not only the application, the graphic controller too. I i change the for increment for the constant value '16' (the get_global_size() function returns) then the code runs fine. What's the problem?
If i run the code with:
WorkDim = 16;
in the line 8 instead of:
WorkDim = get_global_size(0);
The code runs 400 times faster, that's the problem. Why if the value is the same?
**EDIT: ** Well, now i know why, the code is so slow and there are multiply reasons:
1.- The occupancy.
2.- All the threads do the same iterations in the first loop, the right code looks like this:
__kernel void FuncionCL(__global char* in, __global char* out, __global int* S2)
{
__private int op1, op2, op3;
__private int C;
__private uint WorkDim, C2;
op1 = 1;
op2 = 2;
WorkDim = get_global_size(0);
__private int ID;
ID = get_global_id(0);
for(C = ID; C < 1000000; C += WorkDim)
{
for(C2 = ID; C2 < 1000; C2 += WorkDim)
{
op3 = op1 + op2;
}
}
out[0] = 90;
out[1] = 89;
*S2 = (int) WorkDim;
}
Now my code runs 6.1 times faster on the GPU than CPU.
Each item there is doing 1000000*1000 = 1Gop. Just too much, takes too long to do that and the driver restarts the GPU. (I am guessing global size is 1 in your example)
It is a total waste of resources to run a CL kernel with so little work items, it will make the GPU do almost-serial computation and take too long.
At least 1024 global items are needed in new GPUs to fully use their resources.
EDIT: The loop is probably optimized by the compiler when it has a static value. Therefore giving an "amazing" speedup.
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]) );
}
}
}
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.
I have an ATI Firepro V4800 graphics card which does not support cl_khr_int64_base_atomics. I am trying to adapt the RadixSort algo for long integers. The algo uses atomic_inc, the 64-bit of which is atom_inc, which I cannot use in the kernel. So, my question is, is there a piece of code which performs the same function as atomic_inc which can be used? The piece of kernel code is given below:
__kernel void histogram(__global uint* unsortedData,
__global uint* buckets,
uint shiftCount,
__local uint* sharedArray)
{
size_t localId = get_local_id(0);
size_t globalId = get_global_id(0);
size_t groupId = get_group_id(0);
size_t groupSize = get_local_size(0);
uint numGroups = get_global_size(0) / get_local_size(0);
// Initialize shared array to zero //
sharedArray[localId] = 0;
barrier(CLK_LOCAL_MEM_FENCE);
// Calculate thread-histograms //
uint value = unsortedData[globalId];
value = value >> shiftCount & 0xFFU;
atomic_inc(sharedArray+value);
barrier(CLK_LOCAL_MEM_FENCE);
// Copy calculated histogram bin to global memory //
uint bucketPos = groupId * groupSize + localId ;
//uint bucketPos = localId * numGroups + groupId ;
buckets[bucketPos] = sharedArray[localId];
}
Any suggestions? Thank you.
Edit:
Another way for the same is given in this blogsite: http://suhorukov.blogspot.in/2011/12/opencl-11-atomic-operations-on-floating.html. This gives a very generic implementation of the Atomic Inc.
You could try something like this:
void atomInc64 (__local uint *counter)
{
uint old, carry;
old = atomic_inc (&counter [0]);
carry = old == 0xFFFFFFFF;
atomic_add (&counter [1], carry);
}
Where counter is an array of two 32-bit integers. While the two halves don't increment at exactly the same time, the total should be correct when the program completes.