Something More for Research

Explorer of Research #HEMBAD

CUDA Tutorial 05


Your first CUDA program

Before diving into the specifics of how to use your graphics card efficiently, this session is meant to show you just how simple getting started with CUDA really is. With the advent of CUDA 6.0, even more barriers standing in the way of beginners fall. If you are familiar with any programming language like C, getting CUDA devices to work is ridiculously easy.

In this tutorial, we will program a function that creates an array of numbers and than increments each number in that array. This might not be a very useful utilization of computing power, but it depicts how CUDA devices work and what purposes they are able to fulfill.

1  How to build programs

As in all tutorial session, the source code presented here can be found as an archive in the header of this document. You may download and build it using the Makefile or you copy (and adjust to your liking) the source code from this document and follow the build instructions.

2  The really simple version

Please consider the following source code:

</pre>
#include <cuda.h>

#include <cuda_runtime.h>

#include <stdio.h>

&nbsp;

<em>// this is the program that is to be run on the device for a </em>

<em>// large number of threads, in our example 100</em>

<em>// each thread takes care of one entry in the number array,</em>

<em>// so in order for the thread to know which number to manipulate,</em>

<em>// a scheme has to be utilized in order to assign each thread a</em>

<em>// unique number</em>

&nbsp;

__global__ void incrementArrayViaCUDAdevice(int *numberArray, int N)

{

<em>// this is the assignment of a unique identifier.</em>

<em>// blockIdx.x is the unique number of the block, in which the</em>

<em>// thread is positioned, blockDim.x holds the number of threads</em>

<em>// for each block and threadIdx.x is the number of the thread in</em>

<em>// this block.</em>

int idx = blockIdx.x*blockDim.x + threadIdx.x;

&nbsp;

<em>// this tells the thread to manipulate the assigned number in </em>

<em>// the array stored in device memory and increment it</em>

if (idx<N)

numberArray[idx] = numberArray[idx] + 1;

}

&nbsp;

<em>// this is the "normal" function to be run on the CPU</em>

<em>// it does the exact same thing as the CUDA function above</em>

void incrementArray(int *numberArray, int N){

&nbsp;

<em>// go through every number in the array consecutively</em>

<em>// and increment it</em>

for(int i=0; i

{

numberArray[i] = numberArray[i] + 1;

}

}

&nbsp;

int main(int argc, const char* argv[] )

{

<em>// some arbitrary array length</em>

int numberOfNumbers = 100;

&nbsp;

<em>// declare some arrays for storing numbers</em>

int *numbers1, *numbers2;

&nbsp;

<em>// reserve (allocate) some working space for the numbers in device memory</em>

cudaMallocManaged(&numbers1, sizeof(int)*numberOfNumbers);

cudaMallocManaged(&numbers2, sizeof(int)*numberOfNumbers);

&nbsp;

<em>// fill the input array with some numbers</em>

for(int i=0;i

{

numbers1[i] = i;    <em>// this will be manipulated by the CUDA device (GPU)</em>

numbers2[i] = i;    <em>// this will be manipulated by the CPU (as any standard C program would do)</em>

}

&nbsp;

<em>// tell the device (GPU) to do its magic</em>

incrementArrayViaCUDAdevice<<<1, numberOfNumbers>>>(numbers1, numberOfNumbers);

&nbsp;

<em>// wait for the device to finish working</em>

cudaDeviceSynchronize();

&nbsp;

<em>// compute the same function "normally" on the CPU</em>

incrementArray(numbers2, numberOfNumbers);

&nbsp;

<em>// check if the GPU did the same as the CPU</em>

bool workedCorrectly = true;

for(int i=0;i<numberOfNumbers;i++)

{

if (numbers1[i] != numbers2[i])

workedCorrectly = 0;

}

&nbsp;

if (workedCorrectly == 1)

printf("The device performed well!\n");

else

printf("Something went wrong. The output numbers are not what was to be expected...\n");

&nbsp;

<em>// free the space that has been used by our arrays so that</em>

<em>// other programs might use it</em>

cudaFree(numbers1);

cudaFree(numbers2);

&nbsp;

return 0;

}
<pre>

As in all C programs, execution starts at the main function. After defining an array length (numberOfNumbers = 100), we tell the CUDA framework that we would like to have two arrays (numbers1 and numbers2) in unified memory. Unified memory is a construct introduced in CUDA 6.0, which simply states that the graphics processing unit (GPU – your graphics card) as well as the central processing unit (CPU) shall have access to the entries stored in it. Do not bother about this for now, the next tutorial will tell you more about it. For now, just know that this command (cudaMallocManaged) gives you some space in memory to work with.

Now we have to initialize our arrays with some number so we can start working on them. The loop just fills the arrays with incrementing numbers.

The task to fulfill is to increment each and every number in the arrays, so we tell the GPU to do that with the array numbers1 and the CPU do do the same with array numbers2.

Note that the function calls are very similar. The CUDA function (also called a kernel) needs some more information, which is passed to it inside the <<<>>>-brackets, namely the number of blocks we wish to invoke (here: 1) and the number of threads per block (here: numberOfNumbers = 100). Again, do not bother right now, just use one block with as many threads as you like.

If you take a look at the top of the file you will find the two functions we just called. The CUDA version (the kernel) is marked with the keyword __global__. Note that both these functions are very similar. The CPU version contains a for-loop, which iterates through all the elements consecutively.

You will notice that the CUDA kernel only manipulates one element of the array, namely that at position idx. This is because the kernel is invoked numberOfNumber(100) times, once for each element. This means that for each element, one thread is utilized to do the work. The first line in the kernel tells the thread which element in the array it has to work with. The identifiers blockIdx.x, blockDim.x and threadIdx are given by the CUDA framework an tell the thread, in which block it is positioned, how wide the block is and which position inside the block it occupies, respectively.

After performing operations on both our arrays, the main function checks if both arrays are identical and tells you, if that is indeed true.

So let’s go on an see how this program performs.

3  Compile and run the program

Save the source code from the previous section in a file called “main_really_simple.cu”, open a terminal and navigate to the folder where you stored it. As we have two different processing units involved, we need two compilers in order to build our programs. The “normal” C/C++-compiler on Unix-like systems is called gcc (Gnu Compiler Collection). As for CUDA devices, NVIDIA created a compiler called nvcc (NVIDIA CUDA compiler). The following two lines create our program from source code:

nvcc -c main_really_simple.cu -arch=sm_20 gcc main_really_simple.o -o main_really_simple -L/usr/local/cuda-6.0/lib64 -lcudart -lcuda

Or, if you downloaded the archive and extracted it, you might use make (by typing exactly that in the terminal).

The only thing left to do is to run that program:

./main_really_simple

And that’s it! You just passed beyond the most complicated aspect of developing CUDA programs: You convinced yourself that it might be worth looking at.

Now give yourself a pat on the shoulder and go on to the next lesson. Or try changing the value of numberOfNumbers and run the program again. For which numbers does it work? When does it fail? Why is that? Hint: I told you in the very first lesson…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Extracts from a Personal Diary

dedicated to the life of a silent girl who eventually learnt to open up

Num3ri v 2.0

I miei numeri - seconda versione

ThuyDX

Just another WordPress.com site

Algunos Intereses de Abraham Zamudio Chauca

Matematica, Linux , Programacion Serial , Programacion Paralela (CPU - GPU) , Cluster de Computadores , Software Cientifico

josephdung

thoughts...

Tech_Raj

A great WordPress.com site

Travel tips

Travel tips

Experience the real life.....!!!

Shurwaat achi honi chahiye ...

Ronzii's Blog

Just your average geek's blog

Karan Jitendra Thakkar

Everything I think. Everything I do. Right here.

VentureBeat

News About Tech, Money and Innovation

Chetan Solanki

Helpful to u, if u need it.....

ScreenCrush

Explorer of Research #HEMBAD

managedCUDA

Explorer of Research #HEMBAD

siddheshsathe

A great WordPress.com site

Ari's

This is My Space so Dont Mess With IT !!

%d bloggers like this: