В помощь программисту. Считаем на видеокарте с помощью CUDA и VS C#

4 февраля 2014, вторник 11:48
для раздела Блоги

Многие знают, что на видеокартах можно не только играть в игры, но и выполнять расчеты. Если у вас возникло такое желание и одновременно испытываете трудности с его технической реализацией, надеюсь, эта статья будет полезной. В ней приведен пример сложения двух векторных величин с помощью языка 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++ создадим динамическую библиотеку, содержащую метод для работы с видеокартой. Для этого выполним следующие действия:
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++.
 
 

 
Файл cu1.cu теперь будет компилироваться с помощью nvcc.
9. Остается наполнить файлы содержанием. Естественно, содержание может быть различным и в качестве примера приведем переработанный вариант программы сложения векторов, доступный в Visual Studio 2008 как  своего рода приветствие "Hello World" в исполнении NVIDIA.
//  cu1.h
int Add2(char* s1,char* s2,char* s3,int * a,int * b, int * c,int d);
 
// 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)
   { return  Add2(s1,s2,s3, a, b, c, 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){*s33=*s11;s33++;s11++;}
  while(*s22){*s33=*s22;s33++;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**)&dev_c, size * sizeof(int)); 
cudaStatus = cudaMalloc((void**)&dev_a, size * sizeof(int));
cudaStatus = cudaMalloc((void**)&dev_b, size * sizeof(int)); 
cudaStatus = cudaMemcpy(dev_a, a, size * sizeof(int), cudaMemcpyHostToDevice);
cudaStatus = cudaMemcpy(dev_b, b, size * sizeof(int), cudaMemcpyHostToDevice);  
addKernel<<<1, size>>>(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:\Users\z\Documents\Visual Studio 2010\Projects\Cuda_CLR_Zero_Dll\Debug\ 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) { Test1(); }
}
 
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.

 
Для обсуждения:
http://forums.overclockers.ru/viewtopic.php?f=25&t=503685
 
Оценитe материал

Возможно вас заинтересует

Популярные новости

Сейчас обсуждают