Многие знают, что на видеокартах можно не только играть в игры, но и выполнять расчеты. Если у вас возникло такое желание и одновременно испытываете трудности с его технической реализацией, надеюсь, эта статья будет полезной. В ней приведен пример сложения двух векторных величин с помощью языка CUDA. Программы очень просты и их объяснение выйдет за рамки заявленной темы, автор также предполагает, что вы знакомы с навыками работы на языках CUDA, C# и естественном С.
Из технических средств потребуется компьютер с установленной видеокартой NVIDIA, программные средства: Visual Studio 2010 Express C++, Visual Studio 2010 Express C#, NVIDIA CUDA TOOLKIT 5.5. Все программные средства предоставляются Microsoft и NVIDIA бесплатно. Для установки Visual Studio нужно зарегистрирова.
4 февраля 2014, вторник 11:48
blog_user_Zubov [ ] для раздела Блоги
MSI Gaming 4060 Ti на 4000р дешевле 3060 Ti
iPhone 14 256Gb — цена снижена на порядок
MSI Ventus 4060 Ti на 5000р дешевле 3060 Ti
RTX 3070 за копейки в Регарде — смотри
120 «/ 254 см лазерный TV Hisence дешево — смотри
-10000р на MSI 3070 = цена в рублях падает
iPhone 14 Pro Max — цена в рублях пошла вниз
— 18 000р на новейшую RTX 4060 Ti
Разработка на CUDA
За пост начислено вознаграждение
Многие знают, что на видеокартах можно не только играть в игры, но и выполнять расчеты. Если у вас возникло такое желание и одновременно испытываете трудности с его технической реализацией, надеюсь, эта статья будет полезной. В ней приведен пример сложения двух векторных величин с помощью языка CUDA. Программы очень просты и их объяснение выйдет за рамки заявленной темы, автор также предполагает, что вы знакомы с навыками работы на языках CUDA, C# и естественном С.
Из технических средств потребуется компьютер с установленной видеокартой NVIDIA, программные средства: Visual Studio 2010 Express C++, Visual Studio 2010 Express C#, NVIDIA CUDA TOOLKIT 5.5. Все программные средства предоставляются Microsoft и NVIDIA бесплатно. Для установки Visual Studio нужно зарегистрироваться на сайте Microsoft, скачать программы и установить их.
В меню Справка выбрать пункт «Зарегистрировать Продукт», сообщить учетные данные своей регистрации на сайте, получить по почте ключ продукта и наконец ввести этот ключ. Желательно скачать версию Visual Studio 2010 Professional, это бесплатная ознакомительная 30-дневная версия профессионального продукта и, при ее использовании , можно установить документацию на продукт доступную также и из версии Express. Документация содержится в папке, включающей в себя файл HelpContentSetup.msha. Сначала следует установить Visual Studio, за ней пакет NVIDIA CUDA TOOLKIT 5.5, который при установке внесет необходимые изменения в настройках Visual Studio для работы с компилятором nvcc. На этом подготовительный этап закончен.
С помощью Visual Studio 2010 Express C++ создадим динамическую библиотеку, содержащую метод для работы с видеокартой. Для этого выполним следующие действия:
Your First CUDA C Program
1. Вызовем программу Visual Studio 2010 Express C++;
2. Выполним пункт «Создать Проект»;
3. В качестве типа шаблона выбираем — CLR, подтипом — Пустой Проект. Вводим имя решения, предположим, Cuda_CLR_Zero_Dll.
4. В Обозревателе Решений щелкаем правой кнопкой мыши по имени проекта (вторая строчка сверху). В раскрывшемся меню выбираем пункт «Настройки построения. «, и в окне Файлы настройки построения отмечаем опцию CUDA 5.5;
5. В главном меню выбираем пункт Проект и подпункт Свойства.
Устанавливаем пункты Тип конфигурации в Динамическая библиотека и Поддержка общеязыковой среды в Поддержка CLR.
6. В этой же таблице выбираем пункт Компоновщик, подпункт Ввод и в правой части таблицы в опцию Дополнительные зависимости вручную дописываем cudart.lib; (не забудьте поставить точку с запятой). Возможно при первом вызове строка будет пустой, тогда нажмите Наследовать от родителя, закройте окно, снова откройте, должен появится список зависимых библиотек. К нему и присоедините это дополнение.
7. В обозревателе решений в частях проекта Заголовочные файлы и Файлы исходного кода создайте три пустых файла, предположим, с указанными именами.
Файлы добавляются по нажатию правой кнопки мыши на части проекта, далее следует выбрать пункты Добавить и Создать элемент, ввести имя файла. При необходимости следует вручную переименовать расширение файла.
8. В Обозревателе решений щелкнем мышкой по файлу cu1.cu, выберем пункт меню Свойства и в раскрывшемся окне укажем Тип элемента как CUDA C/C++.
// ccc.cpp
#include «cu1.h»
extern «C»
__declspec (dllexport) int __cdecl Add(char* s1,char* s2,char* s3,int * a,int * b, int * c,int d)
// cu1.cu
#include «cuda_runtime.h»
#include «device_launch_parameters.h»
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);
__global__ void addKernel(int *c, const int *a, const int *b)
int i = threadIdx.x;
c[i] = a[i] + b[i] ;
>
////////////////////////////////////////////////////
int Add2(char* s1,char* s2,char* s3,int * aa,int * bb, int * cc,int dd)
char* s11;char* s22;char* s33;
s11=s1; s22=s2;s33=s3;
while(*s11)
while(*s22)
*s33=0;
cudaError_t cudaStatus = addWithCuda(cc, aa, bb, dd );
cudaStatus = cudaDeviceReset();
return 0;
>;
/////////////////////////////////////////////////
cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
int *dev_a = 0;
int *dev_b = 0;
int *dev_c = 0;
cudaError_t cudaStatus;
cudaStatus = cudaSetDevice(0);
cudaStatus = cudaMalloc((void**)
cudaStatus = cudaMalloc((void**)
cudaStatus = cudaMalloc((void**)
cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);
addKernel>>(dev_c, dev_a, dev_b);
cudaStatus = cudaDeviceSynchronize();
cudaStatus = cudaMemcpy(c, dev_c, size * sizeof(int), cudaMemcpyDeviceToHost);
cudaFree(dev_c); cudaFree(dev_a); cudaFree(dev_b);
return cudaStatus;
>
10. В главном меню вызываем Отладка, Построить решение, что приведет к построению библиотеки. Динамическая библиотека Cuda_CLR_Zero_Dll.dll расположится в папке этого проекта, в подпапке Debug или Release в зависимости от того строился отладочный или окончательный вариант и готова к использованию. Путь к файлу примерно такой:
C:UserszDocumentsVisual Studio 2010ProjectsCuda_CLR_Zero_DllDebug Cuda_CLR_Zero_Dll.dll
Для создания основной исполняемой программы воспользуемся пакетом Visual Studio 2010 Express C#.
1. Вызываем пакет, в раскрывшемся окне жмем на пункт Создать проект, выбираем шаблон Пустой проект, вводим имя, предположим, FromCudaDll, нажимаем кнопку Ok.
2. В Обозревателе решений щелкаем правой кнопкой мыши по названию проекта. В раскрывшемся меню последовательно выбираем Добавить и Создать элемент. В качестве шаблона применим Файл с текстом программы, зададим файлу имя, пусть, Program.
3. В Обозревателе решений добавился пункт Program.cs, щелкаем по нему мышкой и вводим текст программы.
using System;
using System.Windows.Forms;
using System.Reflection;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
public class Program
[DllImport(«Cuda_CLR_Zero_Dll.dll», CallingConvention = CallingConvention.Cdecl)]
unsafe public extern static System.Int32 Add(char* s1,char* s2,char* s3,int* a, int* b, int* c, int d);
static ListBox listBox1;
///////////////////////////////////////////////////////////
public static void Main()
Form my_form = new Form1();
Application.Run(my_form);
>
public class Form1 : Form
public Form1()
InitializeComponent();
>
void InitializeComponent()
MetodsButton MB1 = new MetodsButton();
MB1.InicializeButton(this, 1, «Незанятая кнопка»);
MB1.InicializeButton(this, 2, «Сумма векторов»);
listBox1 = new ListBox(); listBox1.Size = new System.Drawing.Size(400, 400);
listBox1.Location = new System.Drawing.Point(150, 10);
this.Controls.Add(listBox1);
>
public void Button1(object sender, EventArgs e)
public void Button2(object sender, EventArgs e)
>
public class MetodsButton
delegate void my_delegate_button_type(object sender, EventArgs e);
MethodInfo buttons_metod_info;
public void InicializeButton(System.Windows.Forms.Form My_Form1, int i, string name)
Button My_Button = new System.Windows.Forms.Button();
My_Button.Location = new Point(0, 27 * (i — 1));
My_Button.Text = name;
My_Button.Width = 140;
My_Form1.Controls.Add(My_Button);
string Button_Number = «Button»;
Button_Number += i.ToString();
buttons_metod_info = (typeof(Form1)).GetMethod(Button_Number);
Delegate My_Event = Delegate.CreateDelegate(typeof(my_delegate_button_type), null, buttons_metod_info);
My_Button.Click += new EventHandler((my_delegate_button_type)My_Event);
>
>// public class MetodsButton
unsafe static void Test1()
int[] a = new int[10]; a[0] = 10; a[1] = 20; a[2] = 30; a[3] = 40; a[4] = 50;
int[] b = < 10, 20, 30, 40, 50 >;
int[] c = new int[10];
string str1 = «Сумма «;
string str2 = «векторов: «;
char[] str3 = new char[255];
string str4;
IntPtr stringPointer1 = (IntPtr)Marshal.StringToHGlobalAnsi(str1);
fixed (int* pa = a, pb = b, pc = c)
fixed (char* s1=str1,s2=str2,s3=str3)
char* stringPointer2 = (char *)Marshal.StringToHGlobalAnsi(str2);
int i = Add((char *)stringPointer1,stringPointer2,s3, pa, pb, pc, 5);
str4 =c[0].ToString() + «, » + c[1].ToString() + «, » + c[2].ToString() + «, »
+ c[3].ToString() + «, » + c[4].ToString() ;
String myString = Marshal.PtrToStringAnsi((IntPtr)s3);
listBox1.Items.Add(myString + str4);
>
>
>
4. В окне Список ошибок появляется несколько сообщений и это естественно, так как проект не настроен. В обозревателе решений нажимаем Ссылки, выбираем пункт Добавить ссылку и в раскрывшемся окне вызываем меню .NET.
Поочередно добавим к проекту ссылки на компоненты System, System.Windows.Forms, System.Drawing.
Далее, в том же окне выбираем меню Обзор, вспоминаем где расположена динамическая библиотека и пройдя путь до нее, также добавим к проекту файл Cuda_CLR_Zero_Dll.dll.
4. Этот пункт следует выполнить при условии наличия в приложении ключевого слова unsafe, что означает применении небезопасного кода для работы с указателями.
В Обозревателе решений правой кнопкой выбираем имя проекта, нажимам пункт Свойства, в раскрывшемся окне выбираем пункт Построение и ставим галочку в опции Разрешить небезопасный код.
Удаляем с экрана окно Свойств.
5. В главном меню выбираем пункт Отладка и подпункт Начать отладку. После построения появляется пользовательская программа, все что остается, так это нажать клавишу Сумма векторов и в окне прочитать результат сложения a и b.
Источник: overclockers.ru
Пример программы на cuda
В качестве примера рассмотрим задачу умножения квадратной матрицы на вектор.
A[n][n] – матрица размерности n x n;
b[n] – вектор, состоящий из n элементов.
c[n] – вектор из n элементов.
//Последовательный алгоритм умножения матрицы на вектор
c[i]=A[i][j]*b[j];
Теперь рассмотрим решение этой задачи на видеокарте. Следующий код иллюстрирует пример вызова функции CUDA:
// инициализация CUDA
int Size = 1000;
// обычные массивы в оперативной памяти
float *h_a,*h_b,*h_c;
h_a = new float[Size*Size];
h_b = new float[Size];
h_c = new float[Size];
// указатели на массивы в видеопамяти
float *d_a,*d_b,*d_c;
// выделение видеопамяти
// копирование из оперативной памяти в видеопамять
CUDA_SAFE_CALL(cudaMemcpy(d_a, h_a, sizeof(float)*Size*Size,
cudaMemcpyHostToDevice) );
CUDA_SAFE_CALL(cudaMemcpy(d_b, h_b, sizeof(float)*Size,
cudaMemcpyHostToDevice) );
// установка количества блоков
dim3 grid((Size+255)/256, 1, 1);
// установка количества потоков в блоке
dim3 threads(256, 1, 1);
// вызов функции
MatrVectMul>> (d_c, d_a, d_b,Size);
// копирование из видеопамяти в оперативную память
CUDA_SAFE_CALL(cudaMemcpy(h_c, d_c, sizeof(float)*Size,
cudaMemcpyDeviceToHost) );
// освобождение памяти
CUDA_SAFE_CALL(cudaFree(d_a));
CUDA_SAFE_CALL(cudaFree(d_b));
CUDA_SAFE_CALL(cudaFree(d_c));
В принципе структуру любой программы с использованием CUDA можно представить аналогично рассмотренному выше примеру. Таким образом, можно предложить следующую последовательность действий:
- инициализация CUDA
- выделение видеопамяти для хранения данных программы
- копирование необходимых для работы функции данных из оперативной памяти в видеопамять
- вызов функции CUDA
- копирование возвращаемых данных из видеопамяти в оперативную
- освобождение видеопамяти
Пример функции, исполнимой на видеокарте extern «C» __global__ void MatrVectMul(float *d_c, float *d_a, float *d_b, int Size)int i = blockIdx.x*blockDim.x+threadIdx.x;int k;d_c[i]=0;for (k=0;k d_c[i]+=d_a[i*Size+k]*d_b[k];>> Здесь: threadIdx.x – идентификатор потока в блоке по координате x, blockIdx.x – идентификатор блока в гриде по координате x, blockDim.x – количество потоков в одном блоке. Пока же следует запомнить, что таким образом получается уникальный идентификатор потока (в данном случае i), который можно использовать в программе, работая со всеми потоками как с одномерным массивом. Важно помнить, что функция, предназначенная для исполнения на видеокарте, не должна обращать к оперативной памяти. Подобное обращение приведет к ошибке. Если необходимо работать с каким-либо объектом в оперативной памяти, предварительно его надо скопировать в видеопамять, и обращаться из функции CUDA к этой копии. Среди основных особенностей CUDA следует отметить отсутствие поддержки двойной точности (типа double). Также для функций CUDA установлено максимальное время исполнения, отсутствует рекурсия, нельзя объявить функцию с переменным числом аргументов. Следует отметить следующее, что функция, работающая на видеокарте, должна выполняться не более 1 секунды. Иначе, функция будет не завершена, и программа завершится с ошибкой. Для синхронизации потоков в блоке существует функция __syncthreads(), которая ждет, пока все запущенные потоки отработают до этой точки. Функция __syncthreads() необходима, когда данные, обрабатываемые одним потоком, затем используются другими потоками.
Источник: studfile.net