// Ядро, выполняется параллельно на большом числе нитей.
__global__ void sum_kernel ( float * a, float * b, float * c ){
	// Глобальный индекс нити.
	int idx = threadIdx.x + blockIdx.x * blockDim.x;
	// Выполнить обработку соответствующих данной нити данных.
	c [idx] = a [idx] + b [idx];
}

#include <stdio.h>

int sum_host( float * a, float * b, float * c, int n ){

	int nb = n * sizeof ( float );
	float *aDev = NULL, *bDev = NULL, *cDev = NULL;

	// Выделить память на GPU.
	cudaError_t cuerr = cudaMalloc ( (void**)&aDev, nb );
	if (cuerr != cudaSuccess)
	{
		fprintf(stderr, "Cannot allocate GPU memory for aDev: %s\n",
				cudaGetErrorString(cuerr));
		return 1;
	}
	cuerr = cudaMalloc ( (void**)&bDev, nb );
	if (cuerr != cudaSuccess)
	{
		fprintf(stderr, "Cannot allocate GPU memory for bDev: %s\n",
				cudaGetErrorString(cuerr));
		return 1;
	}
	cuerr = cudaMalloc ( (void**)&cDev, nb );
	if (cuerr != cudaSuccess)
	{
		fprintf(stderr, "Cannot allocate GPU memory for cDev: %s\n",
				cudaGetErrorString(cuerr));
		return 1;
	}

	// Задать конфигурацию запуска n нитей.
	dim3 threads = dim3(BLOCK_SIZE, 1);
	dim3 blocks = dim3(n / BLOCK_SIZE, 1);

	// Скопировать входные данные из памяти CPU в память GPU.
	cuerr = cudaMemcpy ( aDev, a, nb, cudaMemcpyHostToDevice );
	if (cuerr != cudaSuccess)
	{
		fprintf(stderr, "Cannot copy data from a to aDev: %s\n",
				cudaGetErrorString(cuerr));
		return 1;
	}
	cuerr = cudaMemcpy ( bDev, b, nb, cudaMemcpyHostToDevice );
	if (cuerr != cudaSuccess)
	{
		fprintf(stderr, "Cannot copy data from b to bDev: %s\n",
				cudaGetErrorString(cuerr));
		return 1;
	}
	// Вызвать ядро с заданной конфигурацией для обработки данных.
	sum_kernel<<<blocks, threads>>> (aDev, bDev, cDev);
	cuerr = cudaGetLastError();
	if (cuerr != cudaSuccess)
	{
		fprintf(stderr, "Cannot launch CUDA kernel: %s\n",
				cudaGetErrorString(cuerr));
		return 1;
	}
	// Ожидать завершения работы ядра.
	cuerr = cudaDeviceSynchronize();
	if (cuerr != cudaSuccess)
	{
		fprintf(stderr, "Cannot synchronize CUDA kernel: %s\n",
				cudaGetErrorString(cuerr));
		return 1;
	}
	// Скопировать результаты в память CPU.
	cuerr = cudaMemcpy ( c, cDev, nb, cudaMemcpyDeviceToHost );
	if (cuerr != cudaSuccess)
	{
		fprintf(stderr, "Cannot copy data from cdev to c: %s\n",
				cudaGetErrorString(cuerr));
		return 1;
	}
	// Освободить выделенную память GPU.
	cudaFree ( aDev );
	cudaFree ( bDev );
	cudaFree ( cDev );

	return 0;
}