OpenCL -передать массивы в кернел.

Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий
Boris Egorov
466
Boris Egorov  
Коллеги! Упорно не понимаю как передать в кернел массивы. Подскажите пож-та примером. 

Предположим есть два массива double x[], double w[] с заранее известной длиной n.
Нужно перемножить x[]*w[] и найти сумму произведений

const string cl_src=
                    "__kernel void Func(int n,                          \r\n"
                    //Что вот тут написать?????         
                    "                   __global float *out)            \r\n"
                    "{                                                  \r\n"
                    "  int n = get_global_id( 0 );                      \r\n"
                    "  for( int i = 0; i < n; i ++ )                    \r\n"
                    "  {                                                \r\n"
                    "     out = out+x[i]*w[i];                          \r\n"
                    "  }                                                \r\n"
                    "}                                                  \r\n";
Как передать массивы  x[] и w[] в кернел? Желательно подправить мой код.
Serhii Shevchuk
6437
Serhii Shevchuk  
Boris Egorov:
Коллеги! Упорно не понимаю как передать в кернел массивы. Подскажите пож-та примером. 

Предположим есть два массива double x[], double w[] с заранее известной длиной n.
Нужно перемножить x[]*w[] и найти сумму произведений

Как передать массивы  x[] и w[] в кернел? Желательно подправить мой код.
Точно так же, как out
__kernel void Func(     int n,
                        __global float *x,
                        __global float *w,
                        __global float *out)
{
...
}
Boris Egorov
466
Boris Egorov  
Serhii Shevchuk:
Точно так же, как out

то есть вот этот код полностью правилен? 

const string cl_src=
                    "__kernel void Func(      int n,                    \r\n"
                    "                    __global float *x,             \r\n"
                    "                    __global float *w,             \r\n"
                    "                    __global float *out)           \r\n"
                    "{                                                  \r\n"
                    "  int   n = get_global_id( 0 );                    \r\n"
                    "  float x = get_global_id( 1 );                    \r\n"
                    "  float w = get_global_id( 2 );                    \r\n"
                    "  for( int i = 0; i < n; i ++ )                    \r\n"
                    "  {                                                \r\n"
                    "     out = out+x[i]*w[i];                          \r\n"
                    "  }                                                \r\n"
                    "}                                                  \r\n";
Serhii Shevchuk
6437
Serhii Shevchuk  
Boris Egorov:

то есть вот этот код полностью правилен? 

Нет. Вот это лишнее:

                    "  float x = get_global_id( 1 );                    \r\n"
                    "  float w = get_global_id( 2 );                    \r\n"

Кроме того, если out  у вас не массив (пардон, не обратил внимания сразу), то придётся избавляться от одновременного доступа при помощи атомарных функций. Но в этом случае придётся уходить от float, так как атомарные функции работают только с целочисленными типами (по крайней мере, в OpenCl 1.2 было так).

Сейчас занят, не могу написать код, может ближе к вечеру (если вам не предложат решение или сами не разберетесь).

P.S. Посмотрите мою статью про поиск паттернов, может помочь понять, как всё это работает

P.P.S. И цикл там не нужен

Выложите код на mql, как оно должно работать, и я вам напишу его на OpenCl

Документация по MQL5: Основы языка / Типы данных / Целые типы
Документация по MQL5: Основы языка / Типы данных / Целые типы
  • www.mql5.com
Целые типы представлены в языке MQL5 одиннадцатью видами. Некоторые из типов могут использоваться вместе с другими, если этого требует логика программы, но при этом необходимо иметь ввиду правила преобразования типов. В таблице приведены характеристики каждого типа. Кроме того, в...
Serhii Shevchuk
6437
Serhii Shevchuk  

Вот решение.

1) Код OpenCL (файл test_001.cl)

#pragma OPENCL EXTENSION cl_khr_fp64 : enable

#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable
#pragma OPENCL EXTENSION cl_khr_local_int32_base_atomics : enable
#pragma OPENCL EXTENSION cl_khr_global_int32_extended_atomics : enable
#pragma OPENCL EXTENSION cl_khr_local_int32_extended_atomics : enable
#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable


__kernel void Func(  int n,
                     double k,
                     __global double *x,
                     __global double *w,
                     __global int *out)
{
   size_t i = get_global_id(0);
   int a = x[i]*w[i]*k;
   atomic_add(out,a);
}

2) Скрипт для запуска

#property version   "1.00"

//--- исходные коды кернелов
#resource "test_001.cl" as string cl_test

//--- COpenCL class
#include <OpenCL/OpenCL.mqh>

#define BUF_LEN   10
double buf1[BUF_LEN]= { 0.123, 1.234, 2.345, 3.456, 4.567, 5.678, 6.789, 7.891, 8.912, 9.123};
double buf2[BUF_LEN]= {10.123,11.234,12.345,13.456,14.567,15.678,16.789,17.891,18.912,19.123};

COpenCL          *ocl;

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print(" -=< Start >=-");
//---
   double sum=0;
   for(int i=0; i<BUF_LEN; i++)
      sum+= buf1[i]*buf2[i];
   Print("sum = ",sum);
//---
   double k = 100000;  //точность до 5-го знака после точки
//--- инициализация OpenCl
   if(Init()==false)
      return;
// создаём буферы
   int result[1];
   ocl.BufferFromArray(0,buf1,  0,BUF_LEN,CL_MEM_READ_ONLY);
   ocl.BufferFromArray(1,buf2,  0,BUF_LEN,CL_MEM_READ_ONLY);
   ocl.BufferFromArray(2,result,0,BUF_LEN,CL_MEM_READ_WRITE);
// задаём аргументы
   int n = BUF_LEN;
   ocl.SetArgument(0, 0, n);
   ocl.SetArgument(0, 1, k);
   ocl.SetArgumentBuffer(0, 2, 0);  // x
   ocl.SetArgumentBuffer(0, 3, 1);  // w
   ocl.SetArgumentBuffer(0, 4, 2);  // out
//--- пространство задач кернела одномерное
//--- и количество задач в первом (и единственном) измерении равно размеру буфера
   uint global_size[1]= {BUF_LEN};
//--- начальное смещение в пространстве задач равно нулю
   uint work_offset[1]= {0};
//--- запускаем выполнение кернела
   ocl.Execute(0,1,work_offset,global_size);
//--- читаем результат
   ocl.BufferRead(2,result,0,0,1);
   Print("sum = ",double(result[0])/k);
//--- деинициализация OpenCl
   Deinit();
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool Init(void)
  {
//--- создание объекта класса COpenCL
   ocl=new COpenCL;
   while(!IsStopped())
     {
      //--- инициализация OpenCL
      ::ResetLastError();
      if(!ocl.Initialize(cl_test,true))
        {
         Print("Ошибка инициализации OpenCL");
         break;
        }
      //--- проверка поддержки работы с double
      if(!ocl.SupportDouble())
        {
         Print("Работа с double (cl_khr_fp64) не поддерживается устройством");
         break;
        }
      //--- установка количества кернелов
      if(!ocl.SetKernelsCount(1))
         break;
      //--- создание кернелов
      ocl.KernelCreate(0,"Func");
      //--- создание буферов
      if(!ocl.SetBuffersCount(3))
        {
         Print("Ошибка создания буферов");
         break;
        }
      return true;
     }
   Deinit();
   return false;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void Deinit()
  {
   if(ocl!=NULL)
     {
      //--- remove OpenCL objects
      ocl.Shutdown();
      delete ocl;
      ocl=NULL;
     }
  }
//+------------------------------------------------------------------+

Сравнение результатов:

ocl

Алексей Тарабанов
9796
Алексей Тарабанов  
Boris Egorov:
Коллеги! Упорно не понимаю как передать в кернел массивы. Подскажите пож-та примером. 

Предположим есть два массива double x[], double w[] с заранее известной длиной n.
Нужно перемножить x[]*w[] и найти сумму произведений

Как передать массивы  x[] и w[] в кернел? Желательно подправить мой код.

Вообще-то, всё наоборот. 

Rorschach
887
Rorschach  
Интересно бы сделать какой-нибудь тест и сравнить скорость на cpu, opencl и directx.
Boris Egorov
466
Boris Egorov  

to Serhii Shevchuk

Блин, это круто, спасибо большое за столь подробный ответ, мне надо немного времени чтобы переварить ваш код, я напишу вам лично, если будут вопросы. Я новичок в OpenCL, все пытаюсь его освоить, но со скрипом большим :-(

ТО что вы написали - правда круто.

Еще раз огромное спасибо. 

Serhii Shevchuk
Serhii Shevchuk
  • www.mql5.com
Опубликовал статью Работа с сетевыми функциями, или MySQL без DLL: Часть II - программа для мониторинга изменения свойств сигналов В предыдущей части статьи мы ознакомились с реализацией коннектора MySQL. В этой части мы рассмотрим его применение на примере реализации сервиса сбора свойств сигналов и программы для просмотра их изменения с...
Boris Egorov
466
Boris Egorov  
The time to calculate CPU was 0.00000 msec
sum = 844.2692940000001
OpenCL: GPU device 'Ellesmere' selected
The time to calculate GPU was 62541.00000 msec
sum = 844.26924


для маленьких задач GPU не подходит, нужно действительно что то сложное и долго считаемое 

Rorschach
887
Rorschach  
Serhii Shevchuk:

Вы могли бы сделать версию на OpenCL для этого?

Serhii Shevchuk
6437
Serhii Shevchuk  
Rorschach:

Вы могли бы сделать версию на OpenCL для этого?

Попробую

P.S. Ответил в той ветке

Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий