Reputation: 1791
Basically, I sloppily coded an OpenCL program for an assignment using these global variables:
int devType = CL_DEVICE_TYPE_GPU;
cl_int err; /* Error code returned from api calls. */
size_t global; /* Global domain size for our calculation. */
size_t local; /* Local domain size for our calculation. */
cl_platform_id cpPlatform; /* openCL platform. */
cl_device_id device_id; /* Compute device id. */
cl_context context; /* Compute context. */
cl_command_queue commands; /* Compute command queue. */
cl_program program; /* Compute program. */
cl_kernel kernel; /* Compute kernel. */
/* Create data for the run. */
float *data = NULL; /* Original data set given to device. */
float *results = NULL; /* Results returned from device. */
unsigned int correct; /* Number of correct results returned. */
cl_mem input; /* Device memory used for the input array. */
cl_mem output; /* Device memory used for the output SUM. */
int rc = EXIT_FAILURE;
Now I'm trying to make them all local in order to tidy the program up.
I converted a global variable N by just moving it away from the variables above into the main() function. I then updated every function header that used N to have 'int N' as a parameter, and passed N into any function calls that needed it as an argument. The program worked as expected.
So I suppose what I'm asking is, for the rest of these variables, will it be that simple? I understand the concepts of passing by reference and value and realise some functions may change variables, so I'll need to use pointer referencing/dereferencing. My concern is that my pointer theory is a little rough and I'm worried I'll run into problems. I also am unsure whether my defined functions can take all of these cl variables.
Also, is there anything wrong with using the same variable names within the functions?
EDIT:
As I feared, a problem does occur in the following functions when trying to localise device_id:
void deviceSetup(int devType) {
cl_platform_id cpPlatform; /* openCL platform. */
/* Connect to a compute device. */
if (CL_SUCCESS != clGetPlatformIDs (1, &cpPlatform, NULL))
die ("Error: Failed to find a platform!");
/* Get a device of the appropriate type. */
if (CL_SUCCESS != clGetDeviceIDs (cpPlatform, devType, 1, &device_id, NULL))
die ("Error: Failed to create a device group!");
}
/* Create a compute context. */
void createContext(cl_int err){
context = clCreateContext (0, 1, &device_id, NULL, NULL, &err);
if (!context || err != CL_SUCCESS)
die ("Error: Failed to create a compute context!");
}
/* Create a command commands. */
void createCommandQueue(cl_int err) {
commands = clCreateCommandQueue (context, device_id, 0, &err);
if (!commands || err != CL_SUCCESS)
die ("Error: Failed to create a command commands!");
}
void createAndCompile(cl_int err){
/* Create the compute program from the source buffer. */
program = clCreateProgramWithSource (context, 1,
(const char **) &KernelSource,
NULL, &err);
if (!program || err != CL_SUCCESS)
die ("Error: Failed to create compute program!");
/* Build the program executable. */
err = clBuildProgram (program, 0, NULL, NULL, NULL, NULL);
if (err != CL_SUCCESS)
{
size_t len;
char buffer[2048];
clGetProgramBuildInfo (program, device_id, CL_PROGRAM_BUILD_LOG,
sizeof (buffer), buffer, &len);
die ("Error: Failed to build program executable!\n%s", buffer);
}
}
Upvotes: 2
Views: 2183
Reputation: 7798
You've answered your own question really. Yes, that really is all there is to it. You may want to consider combining a large number of related variables into a struct and pass just a pointer to that struct if you find you've generated massive parameter lists for your functions but that's about it. (There is a tiny degree of performance consideration relating to the number of parameters you pass to any function, but I think for now that's an unnecessary level of complication you could do without!)
There's no getting away from understanding pointers in C though (the only way to pass by reference) so a small project like this might well be an ideal time to strengthen that knowledge!
OK, let's have an example, life's always better explained that way.
We have:
int cheddar;
int montereyjack;
int brie;
void print_cheeses(void)
{
printf("I have %d cheddar %d montereyjack and %d brie\n", cheddar, montereyjack, brie);
}
void add_cheeses(void)
{
cheddar = cheddar + 1;
montereyjack = montereyjack + 1;
brie = brie + 1;
print_cheeses();
}
int main(int argc, char *argv[])
{
add_cheeses();
printf ("Now I have %d cheddars %d jacks %d bries\n", cheddar, montereyjack, brie);
}
What we need to get to is:
// By value here because we're not changing anything
void print_cheeses(int cheds, int jacks, int bries)
{
printf("I have %d cheddar %d montereyjack and %d brie\n", cheds, jacks, bries);
}
// Pointers here because we need to change the values in main
void add_cheeses(int *cheese_one, int *cheese_two, int *cheese_three)
{
*cheese_one = *cheese_one + 1; // We're following the pointer to get to the data we want to change
*cheese_two = *cheese_two + 1;
*cheese_three = *cheese_three + 1;
print_cheeses(*cheese_one, *cheese_two, *cheese_three); // We're following the pointer to get to the data we want to print
}
int main(int argc, char *argv[])
{
int cheddar = 0;
int montereyjack = 0;
int brie = 0;
add_cheeses(&cheddar, &montereyjack, &brie);
printf ("Now I have %d cheddars %d jacks %d bries\n", cheddar, montereyjack, brie);
}
But it can be a pain passing all three values each time, and since they're related you could bundle them together in one struct and just pass a pointer to that struct about.
Upvotes: 5