В помощь программисту. Считаем на видеокарте с помощью CUDA и VS C#
реклама
Многие знают, что на видеокартах можно не только играть в игры, но и выполнять расчеты. Если у вас возникло такое желание и одновременно испытываете трудности с его технической реализацией, надеюсь, эта статья будет полезной. В ней приведен пример сложения двух векторных величин с помощью языка 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.
Для обсуждения:
https://forums.overclockers.ru
реклама
Лента материалов
Соблюдение Правил конференции строго обязательно!
Флуд, флейм и оффтоп преследуются по всей строгости закона!
Комментарии, содержащие оскорбления, нецензурные выражения (в т.ч. замаскированный мат), экстремистские высказывания, рекламу и спам, удаляются независимо от содержимого, а к их авторам могут применяться меры вплоть до запрета написания комментариев и, в случае написания комментария через социальные сети, жалобы в администрацию данной сети.
Сейчас обсуждают