Код OpenCL на MQL5 не доставляет распределенные задания каждому ядру графического процессора. - PullRequest
0 голосов
/ 16 мая 2018

Я создал индикатор на основе графического процессора для платформы терминала MetaTrader, используя OpenCL и MQL5.

Я очень старался, чтобы мое задание по оптимизации [MetaTrader Terminal: Strategy Tester] было перенесено на графический процессор максимально.Большая часть расчетов выполняется индикатором.Следовательно, я внес изменения в индикатор и полностью перешел на GPU.

Но реальная проблема возникает, когда я пытаюсь перейти к процессу оптимизации в разделе тестера стратегий.
В процессе, который я вижу, используются как мой графический процессор, так и процессор, но это никак не влияет на весь процесс.

Я подозреваю, что процесс не распределяется по каждому ядру графического процессора для обработки, вместо этого все ядра графического процессора работают над тем же процессом или функцией для выполнения.

Пожалуйста, дайте мне знать, что мне нужно сделать, чтобы заставить один GPU работать над выполнением одной функции, чтобы обеспечить более быстрый вывод.

Вот моя кодовая ссылка: Полный код с экспертом

Ядро моего кода:

__kernel void calSMA(
                     int limit, 
                     int rates_total, 
                     __global double *price, 
                     __global double *ExtLineBuffer,
                     int InpMAPeriod

                   )
         { 

                int count = 0;
                int len = get_global_id(2);
                for(int i=limit;i<rates_total;i++) 

                     ExtLineBuffer[len+i] = ExtLineBuffer[len+ i-1]+(price[len+i]-price[len+i-InpMAPeriod])/InpMAPeriod;

         }


 __kernel void calcSMALoop(int begin, int limit, __global double *price, __global double *firstValue, int InpMAPeriod)
          { 
                int i, len = get_global_id(2);
                for(i=begin;i<limit;i++) 
                  firstValue[len]+=price[i]; 
                firstValue[len]/=InpMAPeriod;

          }

__kernel void calcEMA(int begin, int limit, __global double *price, __global double *ExtLineBuffer, double SmoothFactor)
          {
            int len = get_global_id(2);
            for(int i=begin;i<limit;i++)
               ExtLineBuffer[len + i]=price[len + i]*SmoothFactor+ExtLineBuffer[len + i-1]*(1.0-SmoothFactor);
          }

__kernel void calcSSMA(int limit, int rates_total, __global double *price, __global double *ExtLineBuffer, int InpMAPeriod)
          {
            int len = get_global_id(2);
            for(int i=limit;i<rates_total;i++)
               ExtLineBuffer[len + i]=(ExtLineBuffer[len + i-1]*(InpMAPeriod-1)+price[len + i])/InpMAPeriod;         
          }

__kernel void calcLWMALoop(int begin, int limit, __global double *price, __global double *firstValue, int weightsum, __global int *weightreturn)
          {

            weightsum = 0;
            int len = get_global_id(2);
            for(int i=begin;i<limit;i++)
            {                 
               weightsum+=(i-begin+1);
               firstValue[len]+=(i-begin+1)*price[i];
            }
      firstValue[len]/=(double)weightsum;
          weightreturn[0] = weightsum;
          }
//__global int counter = 0;
double returnCalculation(int InpMAPeriod, double price, int j)
{

   return ((InpMAPeriod-j)*price);
}
__kernel void calcLWMA(int limit, int rates_total, __global double *price, __global double *ExtLineBuffer, int InpMAPeriod, int weightsum)
          {
          int len = get_global_id(2);
            for(int i=limit;i<rates_total;i++)
            {
               double     sum = 0;
               for(int j=0;j<InpMAPeriod;j++) sum+=returnCalculation(InpMAPeriod,price[len + i-j],j);
                  ExtLineBuffer[len + i]=sum/weightsum;
            }
          }

Пожалуйста, предложите мне путьдля распределения функции с разными значениями или фреймами в MQL5 с использованием графического процессора в OpenCL.

EDITED

Это большая проблема для тех, кто ищет вызов ... Даже мне не терпится узнать, можно ли что-нибудь сделать с OpenCL и MQL5 для задачи оптимизации.Я надеюсь, что получу ответы на то, что я ищу.

ИЗМЕНЕНО СНОВА файл MAGPU.mqh

#include "CHECKMA.mq5"
#define CUDA_CORE 2

int Execute_SMA(
                 const double &price[],                 
                 int rates_total,
                 int limit
                 )
 {

   int cl_mem = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE), 
       cl_price = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE);

          Check_Memory_Initialization(cl_mem, cl_price, cl_CommonKernel1, "Execute_SMA function error");
           if(!CLSetKernelArgMem(cl_CommonKernel1,2,cl_price))
            Print("Input Bufer Not Set");
          //else Print("Input Buffer Set");
           if(!CLSetKernelArgMem(cl_CommonKernel1,3,cl_mem))
            Print("Output Bufer Not Set");
           //else Print("Output Buffer Set");

           if(!CLBufferWrite(cl_price, price))
            Print("Could not copy Input buffer"); 
           //else Print("Copied: ",cl_price);    
           if(!CLBufferWrite(cl_mem, ExtLineBuffer))
            Print("Could not copy Input buffer"); 
           //else Print("Copied: ",cl_mem);  

           //else Print("Input Buffer Copied");
           if(!CLSetKernelArg(cl_CommonKernel1,0,limit))
           Print("Could Not Set Arg 0");
           //else Print("Set Arg 0");
           if(!CLSetKernelArg(cl_CommonKernel1,1,rates_total))
           Print("Could Not Set Arg 1");
           //else Print("Set Arg 1");
           //if(!CLSetKernelArg(cl_CommonKernel1,4,previous_value))
           //Print("Could Not Set Arg2");
           //else Print("Set Arg 2");
           if(!CLSetKernelArg(cl_CommonKernel1,4,InpMAPeriod))
           Print("Could Not Set Arg3: ",GetLastError());

           //Print(CLGetInfoInteger(cl_ctx,CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS));


           if(!CLExecute(cl_CommonKernel1,CUDA_CORE,offset,work))
               Print("Kernel not executed",GetLastError());
           //else Print("Executing Now!");
           //if(CLExecutionStatus(cl_krn) == 0) Print("Completed");
           //if(CLExecutionStatus(cl_krn) == 1) Print("CL_RUNNING");
           //if(CLExecutionStatus(cl_krn) == 2) Print("CL_SUBMITTED");
           //if(CLExecutionStatus(cl_krn) == 3) Print("CL_QUEUED");
           //if(CLExecutionStatus(cl_krn) == -1)Print("Error Occurred:", GetLastError());
           //if(!CLExecutionStatus(cl_krn))
            //Print(CLExecutionStatus(cl_krn));

           if(!CLBufferRead(cl_mem,ExtLineBuffer))
           Print("Buffer Copy Nothing: ", GetLastError());

      CLBufferFree(cl_price);
      CLBufferFree(cl_mem);
  return(1);
 } 

 double ExecuteLoop(
                 int begin,
                 int limit,
                 const double &price[]
                 )
 {

   int cl_mem = CLBufferCreate(cl_ctx,sizeof(double),CL_MEM_READ_WRITE), 
       cl_price = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE);
   double temp[];
   ArrayResize(temp,1);
   temp[0] = 0;

           Check_Memory_Initialization(cl_mem, cl_price, cl_CommonKernel2, "ExecuteLoop function error");

           if(!CLSetKernelArgMem(cl_CommonKernel2,2,cl_price))
            Print("Input Bufer Not Set 2");
           if(!CLSetKernelArgMem(cl_CommonKernel2,3,cl_mem))
            Print("Output Bufer Not Set 2");

           if(!CLBufferWrite(cl_price, price))
            Print("Could not copy Input buffer 2"); 
           if(!CLSetKernelArg(cl_CommonKernel2,0,begin))
            Print("Could Not Set Arg 0");
           if(!CLSetKernelArg(cl_CommonKernel2,1,limit))
            Print("Could Not Set Arg 1");
           if(!CLSetKernelArg(cl_CommonKernel2,4,InpMAPeriod))
            Print("Could Not Set Arg3: ",GetLastError());

           if(!CLExecute(cl_CommonKernel2,CUDA_CORE,offset,work))
               Print("Kernel not executed",GetLastError());

           if(!CLBufferRead(cl_mem,temp))
           Print("Buffer Copy Nothing: ", GetLastError());
      CLBufferFree(cl_price);
      CLBufferFree(cl_mem);
  return(temp[0]);
 } 
int ExecuteEMA(int begin, int limit, const double &price[], double SmoothFactor)
{

   int cl_mem = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE), 
       cl_price = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE);

         Check_Memory_Initialization(cl_mem, cl_price, cl_CommonKernel1, "ExecuteEMA function error");

           if(!CLSetKernelArgMem(cl_CommonKernel1,2,cl_price))
            Print("Input Bufer Not Set");
           if(!CLSetKernelArgMem(cl_CommonKernel1,3,cl_mem))
            Print("Output Bufer Not Set");

           if(!CLBufferWrite(cl_price, price))
            Print("Could not copy Input buffer"); 
           if(!CLBufferWrite(cl_mem, ExtLineBuffer))
            Print("Could not copy Input buffer"); 

           if(!CLSetKernelArg(cl_CommonKernel1,0,begin))
            Print("Could Not Set Arg 0");
           if(!CLSetKernelArg(cl_CommonKernel1,1,limit))
            Print("Could Not Set Arg 1");
           if(!CLSetKernelArg(cl_CommonKernel1,4,SmoothFactor))
            Print("Could Not Set Arg3: ",GetLastError());

           if(!CLExecute(cl_CommonKernel1,CUDA_CORE,offset,work))
               Print("Kernel not executed",GetLastError());

           if(!CLBufferRead(cl_mem,ExtLineBuffer))
            Print("Buffer Copy Nothing: ", GetLastError());

      CLBufferFree(cl_price);
      CLBufferFree(cl_mem);

  return(1);
 }   
int Execute_SSMA(
                 const double &price[],                 
                 int rates_total,
                 int limit
                 )
 {

   int cl_mem = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE), 
       cl_price = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE);

       Check_Memory_Initialization(cl_mem, cl_price, cl_CommonKernel1, "Execute_SSMA function error");

           if(!CLSetKernelArgMem(cl_CommonKernel1,2,cl_price))
            Print("Input Bufer Not Set");
           if(!CLSetKernelArgMem(cl_CommonKernel1,3,cl_mem))
            Print("Output Bufer Not Set");

           if(!CLBufferWrite(cl_price, price))
            Print("Could not copy Input buffer"); 
           if(!CLBufferWrite(cl_mem, ExtLineBuffer))
            Print("Could not copy Input buffer"); 
//             
           //else Print("Input Buffer Copied");
           if(!CLSetKernelArg(cl_CommonKernel1,0,limit))
            Print("Could Not Set Arg 0");
           if(!CLSetKernelArg(cl_CommonKernel1,1,rates_total))
            Print("Could Not Set Arg 1");
           if(!CLSetKernelArg(cl_CommonKernel1,4,InpMAPeriod))
            Print("Could Not Set Arg3: ",GetLastError());

           if(!CLExecute(cl_CommonKernel1,CUDA_CORE,offset,work))
               Print("Kernel not executed",GetLastError());
           if(!CLBufferRead(cl_mem,ExtLineBuffer))
            Print("Buffer Copy Nothing: ", GetLastError());

      CLBufferFree(cl_price);
      CLBufferFree(cl_mem);
  return(1);
 } 

 double ExecuteLWMALoop(
                 int begin,
                 int limit,
                 const double &price[],
                 int weightsumlocal
                 )
 {

   int cl_mem = CLBufferCreate(cl_ctx,sizeof(double),CL_MEM_READ_WRITE), 
       cl_price = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE),
       cl_weightsumlocal = CLBufferCreate(cl_ctx,sizeof(int),CL_MEM_READ_WRITE);
   double temp[];
   int weight[];
   ArrayResize(temp,1);
   ArrayResize(weight,1);
   weight[0] = 0;
   temp[0] = 0;

           Check_Memory_Initialization(cl_mem, cl_price, cl_CommonKernel2, "ExecuteLWMALoop function error");

           if(!CLSetKernelArgMem(cl_CommonKernel2,2,cl_price))
            Print("Input Bufer Not Set 2");
           if(!CLSetKernelArgMem(cl_CommonKernel2,3,cl_mem))
            Print("Output Bufer Not Set 2");
           if(!CLSetKernelArgMem(cl_CommonKernel2,5,cl_weightsumlocal))
            Print("Output Bufer Not Set 2");

           if(!CLBufferWrite(cl_price, price))
            Print("Could not copy Input buffer 2"); 
           if(!CLSetKernelArg(cl_CommonKernel2,0,begin))
            Print("Could Not Set Arg 0");
           if(!CLSetKernelArg(cl_CommonKernel2,1,limit))
            Print("Could Not Set Arg 1");
           if(!CLSetKernelArg(cl_CommonKernel2,4,weightsumlocal))
            Print("Could Not Set Arg3: ",GetLastError());

           if(!CLExecute(cl_CommonKernel2,CUDA_CORE,offset,work))
               Print("Kernel not executed",GetLastError());

           if(!CLBufferRead(cl_mem,temp))
            Print("Buffer Copy Nothing: ", GetLastError());
           if(!CLBufferRead(cl_weightsumlocal,weight))
            Print("Buffer Copy Nothing: ", GetLastError());
       weightsum = weight[0];

       CLBufferFree(cl_weightsumlocal);
      CLBufferFree(cl_price);
      CLBufferFree(cl_mem);
  return(temp[0]);
 } 
int Execute_LWMA(const double &price[], int rates_total, int limit, int weightsum1)
       {

         int cl_mem = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE), 
       cl_price = CLBufferCreate(cl_ctx,ArraySize(price)*sizeof(double),CL_MEM_READ_WRITE);

       Check_Memory_Initialization(cl_mem, cl_price, cl_CommonKernel1, "Execute_SSMA function error");

           if(!CLSetKernelArgMem(cl_CommonKernel1,2,cl_price))
            Print("Input Bufer Not Set");
           if(!CLSetKernelArgMem(cl_CommonKernel1,3,cl_mem))
            Print("Output Bufer Not Set");

           if(!CLBufferWrite(cl_price, price))
            Print("Could not copy Input buffer"); 
           if(!CLBufferWrite(cl_mem, ExtLineBuffer))
            Print("Could not copy Input buffer"); 

           //else Print("Input Buffer Copied");
           if(!CLSetKernelArg(cl_CommonKernel1,0,limit))
            Print("Could Not Set Arg 0");
           if(!CLSetKernelArg(cl_CommonKernel1,1,rates_total))
            Print("Could Not Set Arg 1");
           if(!CLSetKernelArg(cl_CommonKernel1,4,InpMAPeriod))
            Print("Could Not Set Arg4: ",GetLastError());
           if(!CLSetKernelArg(cl_CommonKernel1,5,weightsum1))
            Print("Could Not Set Arg5: ",GetLastError());

           if(!CLExecute(cl_CommonKernel1,CUDA_CORE,offset,work))
               Print("Kernel not executed",GetLastError());
           if(!CLBufferRead(cl_mem,ExtLineBuffer))
            Print("Buffer Copy Nothing: ", GetLastError());

      CLBufferFree(cl_price);
      CLBufferFree(cl_mem);
  return(1);

       }
void checkKernel(int cl_kernel, string var_name)
{

           if(cl_kernel==INVALID_HANDLE )
           {
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               Print("OpenCL kernel create failed: ERR_OPENCL_INVALID_HANDLE ", var_name);
               return;
           }
           if(cl_kernel==ERR_INVALID_PARAMETER )
           {
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               Print("OpenCL kernel create failed: ERR_INVALID_PARAMETER ", var_name);
               return;
           }
           if(cl_kernel==ERR_OPENCL_TOO_LONG_KERNEL_NAME  )
           {
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               Print("OpenCL kernel create failed: ERR_OPENCL_TOO_LONG_KERNEL_NAME ", var_name);
               return;
           }
           if(cl_kernel==ERR_OPENCL_KERNEL_CREATE )
           {
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               Print("OpenCL kernel create failed 1: ERR_OPENCL_KERNEL_CREATE ", var_name);
               return;
           }

}

 int Check_Memory_Initialization(int cl_mem, int cl_price, int cl_ker, string name_process_call)
      {

         if(cl_mem==INVALID_HANDLE)
           {
               CLKernelFree(cl_ker);
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               Print("OpenCL buffer create failed: cl_mem INVALID_HANDLE: ", name_process_call);
               return(0);
           }
           if(cl_mem==ERR_NOT_ENOUGH_MEMORY )
           {
               CLKernelFree(cl_ker);
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               Print("OpenCL buffer create failed: cl_mem ERR_NOT_ENOUGH_MEMORY: ", name_process_call);
               return(0);
           }
           if(cl_mem==ERR_OPENCL_BUFFER_CREATE )
           {
               CLKernelFree(cl_ker);
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               Print("OpenCL buffer create failed: cl_mem ERR_OPENCL_BUFFER_CREATE: ", name_process_call);
               return(0);
           }

           if(cl_price==INVALID_HANDLE)
           {
               CLKernelFree(cl_ker);
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               CLBufferFree(cl_mem);
               Print("OpenCL buffer create failed: cl_price: ", name_process_call);
               return(0);
           }
           if(cl_price==ERR_NOT_ENOUGH_MEMORY)
           {
               CLKernelFree(cl_ker);
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               CLBufferFree(cl_mem);
               Print("OpenCL buffer create failed: cl_price ERR_NOT_ENOUGH_MEMORY: ", name_process_call);
               return(0);
           }
           if(cl_price==ERR_OPENCL_BUFFER_CREATE)
           {
               CLKernelFree(cl_ker);
               CLProgramFree(cl_prg);
               CLContextFree(cl_ctx);
               CLBufferFree(cl_mem);
               Print("OpenCL buffer create failed: cl_price ERR_OPENCL_BUFFER_CREATE: ", name_process_call);
               return(0);
           }
        return(1);       
      }

ФАЙЛ ОСНОВНОГО ИНДИКАТОРА CHECKMA.mq5 файл

    #resource "program_MA_GPU.cl" as string cl_program
    #include "MAGPU.mqh"
    #property indicator_chart_window
    #property indicator_buffers 1
    #property indicator_plots   1
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  Red
    input int            InpMAPeriod=13;         // Period
    input int            InpMAShift=0;           // Shift
    input ENUM_MA_METHOD InpMAMethod=MODE_SMA;  // Method
    //--- indicator buffers
    double               ExtLineBuffer[];
    int  offset[CUDA_CORE], work[CUDA_CORE];//={0,19,38,57,76,95,114,123};
    string str;   
       int cl_ctx, cl_prg, cl_CommonKernel1, cl_CommonKernel2;
    static int weightsum;    
    void CalculateSimpleMA(int rates_total,int prev_calculated,int begin,const double &price[])
      {
       int limit;
if(prev_calculated==0)
         {
          limit=InpMAPeriod+begin;
          ArrayFill(ExtLineBuffer,0,limit-1,0.0);
          ExtLineBuffer[limit-1]=ExecuteLoop(begin,limit,price);
         }
       else limit=prev_calculated-ArraySize(price)+InpMAPeriod+17;
         Execute_SMA(price,rates_total,limit);
      }
    void CalculateEMA(int rates_total,int prev_calculated,int begin,const double &price[])
      {
       int    limit;
       double SmoothFactor=2.0/(1.0+InpMAPeriod);

       if(prev_calculated==0)
         {

          limit=InpMAPeriod+begin;
          ExtLineBuffer[begin]=price[begin];
          ExecuteEMA(begin+1,limit,price,SmoothFactor);      
         }
       else limit=prev_calculated;
       ExecuteEMA(begin+99900,limit,price,SmoothFactor);
      }
    void CalculateLWMA(int rates_total,int prev_calculated,int begin,const double &price[])
      {
       int  limit; 
       if(prev_calculated==0)
         {
          weightsum=0;
          limit=InpMAPeriod+begin;
          //--- set empty value for first limit bars
          ArrayFill(ExtLineBuffer,0,limit,0.0);
          //--- calculate first visible value
          ExtLineBuffer[limit-1]=ExecuteLWMALoop(begin,limit,price,weightsum);
         }
       else limit=prev_calculated-ArraySize(price)+InpMAPeriod+17;
    //--- main loop
       Execute_LWMA(price,rates_total,limit,weightsum);
    }

    void CalculateSmoothedMA(int rates_total,int prev_calculated,int begin,const double &price[])
      {
       int limit;
    //--- first calculation or number of bars was changed
       if(prev_calculated==0)
         {
          limit=InpMAPeriod+begin;
          //--- set empty value for first limit bars
          ArrayFill(ExtLineBuffer,0,limit-1,0.0);
          ExtLineBuffer[limit-1]=ExecuteLoop(begin,limit,price);
         }
       else limit=prev_calculated-ArraySize(price)+InpMAPeriod+17;

          Execute_SSMA(price,rates_total,limit);
    //---
      }

    void OnInit()
      {
    //--- indicator buffers mapping
       SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA);
    //--- set accuracy
       IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
    //--- sets first bar from what index will be drawn
       PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,InpMAPeriod);
    //---- line shifts when drawing
       PlotIndexSetInteger(0,PLOT_SHIFT,InpMAShift);
    //--- name for DataWindow

    //---- sets drawing line empty value--
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
    //---- initialization done
       cl_ctx = CLContextCreate(CL_USE_GPU_ONLY);
       cl_prg=CLProgramCreate(cl_ctx,cl_program,str);

    if(cl_ctx==INVALID_HANDLE)
         {
          Print("OpenCL not found: ", GetLastError() );
          return;

         }  
       if(cl_prg==INVALID_HANDLE)
         {
          CLContextFree(cl_ctx);

          Print("OpenCL program create failed: ", str);
          return;
         }
         if(cl_prg==ERR_INVALID_PARAMETER )
         {
          CLContextFree(cl_ctx);

          Print("OpenCL program create failed: ", str);
          return;
         }
         if(cl_prg==ERR_NOT_ENOUGH_MEMORY )
         {
          CLContextFree(cl_ctx);

          Print("OpenCL program create failed: ", str);
          return;
         }
         if(cl_prg==ERR_OPENCL_PROGRAM_CREATE )
         {
          CLContextFree(cl_ctx);

          Print("OpenCL program create failed: ", str);
          return;
         }
         int c = 1;
         ArrayFill(work,0,CUDA_CORE,c);
         //ArrayInitialize(offset,0);
         int enter = -c;
         for (int i =0; i <  CUDA_CORE; i++)
         {
          offset[i] = enter +  c;
          enter = offset[i];
         }
       switch(InpMAMethod)
         {
          case MODE_SMA : cl_CommonKernel1 = CLKernelCreate(cl_prg,"calSMA");
                          checkKernel(cl_CommonKernel1,"cl_CommonKernel1 SMA");
                          cl_CommonKernel2 = CLKernelCreate(cl_prg,"calcSMALoop");  
                          checkKernel(cl_CommonKernel2,"cl_CommonKernel2 SMA");     
                          break;
          case MODE_EMA : cl_CommonKernel1 = CLKernelCreate(cl_prg,"calcEMA");
                          checkKernel(cl_CommonKernel1,"cl_CommonKernel1 EMA");                          
                          break;       
          case MODE_LWMA : cl_CommonKernel1 = CLKernelCreate(cl_prg,"calcLWMA");
                           checkKernel(cl_CommonKernel1,"cl_CommonKernel1 LWMA");
                           cl_CommonKernel2 = CLKernelCreate(cl_prg,"calcLWMALoop"); 
                           checkKernel(cl_CommonKernel2,"cl_CommonKernel2 LWMA");      
                          break;       
          case MODE_SMMA : cl_CommonKernel1 = CLKernelCreate(cl_prg,"calcSSMA");
                           checkKernel(cl_CommonKernel1,"cl_CommonKernel1 SSMA");
                           cl_CommonKernel2 = CLKernelCreate(cl_prg,"calcSMALoop"); 
                           checkKernel(cl_CommonKernel2,"cl_CommonKernel2 SSMA");    
                           break;  

         }


      }
int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const int begin,
                    const double &price[])
      {
       if(rates_total<InpMAPeriod-1+begin)
          return(0);
       if(prev_calculated==0)
          ArrayInitialize(ExtLineBuffer,0);
       PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,InpMAPeriod-1+begin);
       switch(InpMAMethod)
         {
          case MODE_EMA:  CalculateEMA(rates_total,prev_calculated,begin,price);        break;
          case MODE_LWMA: CalculateLWMA(rates_total,prev_calculated,begin,price);       break;
          case MODE_SMMA: CalculateSmoothedMA(rates_total,prev_calculated,begin,price); break;
          case MODE_SMA:  CalculateSimpleMA(rates_total,prev_calculated,begin,price);   break;
         }
    //--- return value of prev_calculated for next call
       return(rates_total);
      }
 void OnDeinit(const int reason)
      {
       CLKernelFree(cl_CommonKernel1);
       CLKernelFree(cl_CommonKernel2);
       CLProgramFree(cl_prg);
       CLContextFree(cl_ctx);
      }

1 Ответ

0 голосов
/ 18 мая 2018

Помогите мне написать этот код надлежащим образом , чтобы мой процесс стал быстрее и дал правильный результат с моим графическим процессором. Animated UI-interactive Law of Diminishing Returns, re-formulated so as to also contain processing add-on setup / termination overheads

Факты в первую очередь,
со 2 апреля и
вы уже знаете,
MQL5 CustomIndicator не будут работать таким образом ...

Факты имеют значение - если в архитектуре выполнения кода MQL4 / 5 явно задокументировано , что нет места для каких-либо расширенных операций задержки / асинхронизации / блокировки внутри любого модуля MQL4 / 5 CustomIndicator для выполнения кода может быть разумное время, чтобы остановить все эти атаки членов сообщества S / O и начать просто уважать документированный и опубликованный факт .

Документация

MQL5 является довольно явной и предупреждает о неблагоприятных последствиях ее архитектуры с одним общим потоком для производительности и / или предупреждает о риске полной непреднамеренной блокировки вся система:

Все индикаторы, рассчитанные по одному символу, даже если они прикреплены к разным графикам, работают в одном потоке. Таким образом, все индикаторы на одном символе совместно используют ресурсы одного потока .


Бесконечный цикл (блокировка / увеличение задержки / неожиданная задержка) в одном индикаторе остановит все другие индикаторы на этом символе.

Конечно, можно попросить многих добрых специалистов OpenCL, присутствующих в сообществе S / O, помочь, выразить крайнюю заинтересованность в получении своих спонсируемых знаний и так далее, и так далее, , если они решат продолжить тратить свои драгоценное время для спонсирования и расширения таких усилий.

Все это все еще должно соответствовать реальности и наилучшим образом уважать известные факты, прежде чем любое разумное усилие (при любых выраженных силах) может по крайней мере начать работать в правильном направлении.


Как внутренне работает топология CPU: GPU?

Использование асинхронной доставки с доставкой вне порядка, асинхронная по определению, Очередь в памяти, последовательность из ноль-ко-многим GPU- целевое устройство вычислительное (ые) задание (задания) , все из которых имеют начальную и конечную длительность, преимущественно недетерминированную RTT-длительность.

Конец стороны доступа к GPU-устройству на стороне процессора - Очередь может быть задан запрос на отправку задания в GPU (задача ~ программа для выполнения):

F inite S tate A Автоматизированный рабочий процесс управления задачами GPU имеет следующую топологическую диаграмму:

< START____________> s = GetMicrosecondCount();
( MQL5 RQSTs )
          |
          |
          |
       T0:+---+--> CL_QUEUED            : 3 == "queued", i.e. waiting for its turn ( submission )
          ?   |    |  |
          ?   +----+  |
          ?           v
       T0+?      +--> CL_SUBMITTED      : 2 == "submitted" for an OpenCL-device execution
          ?      |    |  |        
          ?      +----+  |        
          ?              v        
       T0+?         +--> CL_RUNNING     : 1 == "running" the kernel-code on a mapped OpenCL resource pool
          ?         |    |  |     
          ?         +----+  |     
          ?                 v     
       T0+?                 CL_COMPLETE : 0 == "program complete", processing has finished its remote outputs
          ?                 |  
       T0+?-----------------+
          |
( MQL5 FREEs )
< END_____________>  e = GetMicrosecondCount();
                     PrintFormat( "RTT-COST WAS ~ %9d [us] ( CLES==0 ? %d )",
                                   ( e - s ),
                                   CLExecutionStatus( _gpuKernelHANDLE )
                                   );

Далее, давайте с уважением относимся к архитектуре графических процессоров:

Вычислительные устройства на GPU отличаются от других, имеющих кремниевую архитектуру, чем любые универсальные вычислительные устройства CISC / RISC с ЦП.

Причина ПОЧЕМУ очень важна здесь.

В устройствах

GPU используются S трейнинг M ultiprocessor e X исполнительных блоков ( SMX блоков), которые упоминаются в некоторых аппаратных инструменты осмотра.

В то время как буква M в аббревиатуре S M X подчеркивает, на SMX-модуль можно загрузить несколько выполнений, но все такие случаи действительно выполняются (конечно, толькоесли дано указание таким образом, что выходит за рамки этой темы, чтобы охватить / охватить все SM-ядра SMX-присутствия) одни и те же вычислительные инструкции - это единственный способ, которым они могут работать - этоназывается SIMT/SIMD -типом ограниченной области параллелизма, достижимой (совместно) по периметру SMX, где s ingle- i nstruction- м ультипул- { т ступни | d ata} может выполняться в рамках существующих возможностей планировщика SIMT / SIMD- (WARP-wide-half-WARP-wide-WARP-ignoring-GreedyMode).Важно отметить, что чем более узкой становится ширина запланированного выполнения SIMT/SIMD, тем меньше SMX / SM-ядер фактически выполняют какую-либо полезную часть глобального выполнения задания и тем больше потраченного времени приводит к снижению производительности из-за падения числаиз N-(CPUs), как описано ниже.

Перечисленные выше эти 384 ядра означают аппаратное ограничение, сверх которого этот совместно организованный тип SIMT / SIMDпараллелизм ограниченной области не может расти, и все попытки в этом направлении приведут к чисто [SERIAL] внутреннему планированию заданий GPU (да, то есть один за другим).

Понимание этих основ является кардинальным, поскольку без этих особенностей архитектуры можно ожидать поведения, которое на самом деле принципиально невозможно организовать в какой-либо системе GPGPU, имеющей формальную форму [ 1-CPU-host : N-GPU-device(s) ]составы автономных, асинхронных звезды узлов.

Любое ядро ​​GPU loaded с CPU-host на GPU будет отображаться на непустом наборе SMX-блоков, где применяется указанное количество ядер (другое, более мелкое зерно, геометрия вычислительных ресурсов, снова выходя далеко за пределыобъем этого поста) загружается потоком SIMT / SIMD-инструкций, не нарушая ограничения GPU-устройства:

 ...
+----------------------------------------------------------------------------------------
 Max work items dimensions:          3       // 3D-geometry grids possible
    Max work items[0]:               1024    // 1st dimension max.
    Max work items[1]:               1024
    Max work items[2]:               64      // theoretical max. 1024 x 1024 x 64 BUT...
+----------------------------------------------------------------------------------------
 Max work group size:                1024    // actual      max. "geometry"-size
+----------------------------------------------------------------------------------------
 ...

Итак,

  • если 1-SM-core было внутренне предписано выполнить какой-либо модуль задачи GPU (задание GPU), то только это одно ядро ​​SM будет извлекать одну инструкцию GPU-RISC за другой (игнорируя любые возможныеILP для простоты здесь) и выполняйте его по одному, шагая по потоку SIMD-инструкций упомянутого GPU-задания.Все остальные ядра SM, присутствующие на одном и том же модуле SMX, обычно ничего не делают в течение этого времени, пока эта задача GPU не будет завершена, и внутренняя система управления процессами GPU не решит отобразить какую-либо другую работу для этого SMX.

  • если 2-SM-ядрам было поручено выполнить какое-либо задание GPU, то только эта пара SM-ядер получит один (и тот же тот же ) графический процессор-RISC-инструкция за другой (игнорируя любую возможную ILP для простоты здесь) и оба выполняют ее по одной, шагая по потоку SIMT / SIMD-инструкций упомянутой работы GPU.В этом случае, если одно SM-ядро попадает в состояние, при котором поток if или аналогично разветвленный поток выполнения заставляет одно SM-ядро переходить в другой путь потока выполнения кодачем другой, SIMT / SIMD -параллелизм попадает в расходящийся сценарий, где одно SM-ядро получает следующую SIMT / SIMD-инструкцию, принадлежащую его пути выполнения кода, тогда как другое ничего не делает (получает GPU_NOP (s)) до тех пор, пока первый не завершит всю работу (или не будет вынужден остановиться на каком-то барьере синхронизации, попавшем в состояние ожидания немаскируемой задержки, при ожидании получения части данных из "далеко "(медленное) расположение нелокальной памяти, опять же, детали выходят далеко за рамки этого поста) - только после того, как что-то из этого случится, расходящийся путь, так что только GPU_NOP -еданное SM-ядро может получитьлюбая следующая SIMT / SIMD-инструкция, принадлежащая ее (расходящемуся) пути выполнения кода, для продвижения вперед.Все остальные ядра SM, присутствующие на одном и том же модуле SMX, обычно ничего не делают в течение этого времени, пока эта задача GPU не будет завершена, и внутренняя система управления процессами GPU не решит отобразить какую-либо другую работу для этого SMX.

  • если 16-SM-ядрам было дано указание выполнить какое-либо задание на GPU с помощью "геометрии" конкретной задачи, то только это "стадо" SM-ядер получит одно(и очень то же самое ) SIMT / SIMD-инструкция GPU-RISC за другой (игнорируя любую возможную ILP для простоты здесь) и все выполняют ее по одному, проходя черезпоток SIMT / SIMD-инструкций указанного GPU-задания.Любое расхождение внутри «стада» уменьшает SIMT / SIMD-эффект, и ядра, блокированные GPU_NOP, продолжают ждать, пока основная часть «стада» завершит работу (так же, как было нарисовано прямо над этой точкой).

  • если доступно больше-SIMT / SIMD-threads-чем-SM-cores было дано указание выполнить какое-либо задание GPU с помощью специфической для задачи "геометрии", графического процессора-устройство кремния будет работать так, чтобы оно передавалось как [SERIAL] -последовательность из множества { WARP-wide | half-WARP-wide } -SIMT / SIMD-потоков, пока такая последовательность не завершит все указанное число SIMT / SIMD-потоков, отображаемых наSMX.Следовательно, временная согласованность такой финальной финализации пакета принципиально невозможна, так как они достигают своих соответствующих концов определенным образом WARP-планировщика, но никогда синхронно (да, ваш код на стороне ЦП здесь должен будет ждать, покасамый последний (самый ленивый (по какой-либо причине, будь то причина планирования с ограниченной пропускной способностью, либо причина планирования расхождения кода, либо причина плохой взаимной (повторной) синхронизации) поток выполнения кода) будетв конечном итоге, в какое-то неизвестное время в будущем, завершите обработку кода __kernel, и устройство, работающее с OpenCL, разрешит «дистанционное» обнаружение состояния CL_COMPLETE, прежде чем сможет получить какие-либо значимые результаты (как вы спросите с удивлением в одном из ваших других вопросов).

в любом случае, все остальные SM-ядра, не отображенные конкретной геометрией задачи на соответствующем GPU-SMX-модуль устройства, как правило, вообще не будет делать ничего полезного - поэтому важно знать подробности аппаратного обеспечения.• правильная «геометрия» для конкретной задачи действительно важна, и профилирование может помочь определить пиковую производительность для любого такого созвездия задач GPU (различия могут варьироваться в пределах нескольких порядков - от лучшего к общему и худшему - среди всех возможных специфических для задачи«геометрия»).


Во-вторых, когда у меня много ядер, как openCL распределяет задачу , это на каждом ядре один и тот же процесс и одни и те же данные или это разные ядра с разнымиdata?

Как объяснено вкратце выше - кремниевая архитектура устройства типа SIMT/SIMD не позволяет ни одному из SMX-ядер выполнять что-либо другоечем та же самая SIMT / SIMD-инструкция для всего «стада» SM-ядер, которая была отображена «геометрией» задачи на SMX-блок (не считая GPU_NOP (s) как выполняющие ») что-то еще", поскольку он просто тратит впустую CPU: системное время GPU).

Итак, да, " .. на каждом ядре тот же process .. " (лучше, если он никогда не расходится во внутренних путях выполнения кода после if или while или любого другого вида кода-ветвление пути выполнения), поэтому, если алгоритм, основанный на управляемых данными значениях, приводит к разному внутреннему состоянию, каждое ядро ​​может иметь разные локальные потоки.состояние, в соответствии с которым обработка может отличаться (как проиллюстрировано с помощью приведенных выше путей выполнения кода с использованием if).Дополнительные сведения о локальных SM-регистрах, локальном SM-кэшировании, ограниченном использовании общей памяти (и затратах на задержку), использовании глобальной памяти на устройствах с графическим процессором (а также затратах на задержку, длине строк кэширования и ассоциативности для лучшего объединения шаблонов доступа для задержкиопции маскировки - многие сведения об аппаратной части + программирование экосистемы входят в небольшие тысячи страниц документации по аппаратному обеспечению + программному обеспечению и выходят далеко за рамки этого упрощенного для ясности сообщения)

те же данные или это другое ядро ​​с разными данными?

Это последнее, но не менее важное,Дилемма - любая хорошо параметризованная активация ядра GPU может также передавать некоторое количество данных внешнего мира в ядро ​​GPU, что может отличать локальные данные потока SMX от ядра SM до ядра SM.Методы сопоставления и лучшая производительность для этого в основном зависят от устройства ({SMX | SM-регистры | GPU_GDDR gloMEM: shaMEM: constMEM | GPU SMX-локальная иерархия кэша} -детали и возможности

  ...
 +---------------------------------------------------------
  ...                                               901 MHz
  Cache type:                            Read/Write
  Cache line size:                     128
  Cache size:                        32768
  Global memory size:           4294967296
  Constant buffer size:              65536
  Max number of constant args:           9
  Local memory size:                 49152
 +---------------------------------------------------------
  ...                                              4000 MHz
  Cache type:                            Read/Write
  Cache line size:                      64
  Cache size:                       262144
  Global memory size:            536838144
  Constant buffer size:             131072
  Max number of constant args:         480
  Local memory size:                 32768
 +---------------------------------------------------------
  ...                                              1300 MHz
  Cache type:                            Read/Write
  Cache line size:                      64
  Cache size:                       262144
  Global memory size:           1561123226
  Constant buffer size:              65536
  Max number of constant args:           8
  Local memory size:                 65536
 +---------------------------------------------------------
  ...                                              4000 MHz
  Cache type:                            Read/Write
  Cache line size:                      64
  Cache size:                       262144
  Global memory size:           2147352576
  Constant buffer size:             131072
  Max number of constant args:         480
  Local memory size:                 32768

принципиально настолько отличаются от устройства к устройству, что каждый высокопроизводительный кодовый проект принципиально может, но профилировать свою соответствующую задачу GPU-устройства - «составление карт геометрии и использования ресурсов для фактического устройства развертывания. Что может работать быстрее на одном GPU-устройстве /Стек GPU-накопителей, не обязательно должен работать как умный на другом (или после обновления GPU-драйвера + экзо-программирования экосистемы), просто скажет только реальный тест (как теория может быть легко напечатана, но вряд ликак легко выполнимо, так как многие ограничения для конкретных устройств и рабочей нагрузки будут применяться в реальном развертывании).


предложить мне выход дляраспределение функции с разными значениями или фреймами в MQL5 с использованием графического процессора в OpenCL.

Честные и лучшие предложения те же, что были представлены вам уже 2 апреля.

Не пытайтесь блокировать / задерживать поток выполнения любого MQL5 CustomIndicator -тип модуля выполнения кода с любым кодом с большой задержкой / асинхронностью / блокировкой. Никогда, до тех пор, пока документация платформы MetaTrader Terminal явно не удалит такие предупреждения (присутствующие там еще в 2018 / Q2) и не даст явных советов по методам, использующим неблокируемые средства связи для неблокирующих распределенных агентов для скоординированных (почти) -синхронный обмен данными / результатами обработки между MQL5-стороной и GPU-устройством (которая скоро будет недоступна из-за SIMT/SIMD характера планирования GPU Out-of-Order- доступны работы в современных классах GPU-устройств.

Это было задокументировано для естественного потока времени , стробированного потоком внешних событий FX Market (распространяемых через брокерскую трансляцию), имеющих около нескольких сотен [us] Каденция от события к событию.

При входе в синтетический поток времени 1298 *, как это организовано в симуляторе [Тестера стратегий] Терминалаeco-system, проблема , задокументированная выше , идет на много порядков хуже, , поскольку симулятор фактически ускоряет поток времени / частоту, и все, что не способно идти в ногу, будет (снова) блокирует любое ускорение (которое уже было плохим в естественном темпе вышеописанного потока времени).Так что нет, это очень плохое направление, чтобы вкладывать средства в очередной шаг усилий (опять же, , по крайней мере, пока обе платформы не изменят свои архитектурные ограничения ).


... чтобы мой процесс стал быстрее ...

Эта часть определения проблемы имеетбыло решено уже ~ 60 лет назад доктором Джином AMDAHL .

The Law of Diminishing Returns and its contemporary Criticism and re-formulation

Его (тогда упрощенно) Закон объясняет ПОЧЕМУ основной потолок любого ускорения процесса связан с неподвижной [SERIAL] частью, учитывая различие между чисто [SERIAL] часть и потенциально N- (ЦП) -части true- [PARALLEL] части четко определены.

Это помогает предварительно оценить эффект затрат / выгод повторного процессаengineering.

Итак, ваш код ядра GPU является своего рода (почти) - [PARALLEL] обрабатывающей частью.Все остальное по-прежнему является чисто [SERIAL] обрабатывающей частью.

Этого достаточно, чтобы угадать пределы эффекта от попыток перейти к перепроектированию процессов в оболочке OpenCL.


Но дьявол скрыт в деталях ...

Реальные затраты намного выше.

Guess the percentage + add the add-on overhead costs ...

  • [SERIAL] -часть никогда не станет быстрее per se.
  • [SERIAL] -part на самом деле станет "медленнее" и "расширеннее" , так как будет много других шагов для выполнения до того, как первая SIMT / SIMD-инструкция полезной нагрузки (ий) ... "удаленно" доставлена ​​в OpenCL-очередь + OpenCL-передача данных (ей) + ожидание управления задачами OpenCL-ожидание ... + OpenCL-Передача очереди TaskManagement на устройство ... даже начнет выполняться + задача == запланированное WARP-запланированное OpenCL-устройство / SIMT / SIMD-выполнение + все время назад, из удаленного цирка --- задача OpenCL-Deviceнакладные расходы на завершение + задержки асинхронного завершения на стороне MQL5-обнаружения + задержки OpenCL-Data-Transfer (s)
  • [PARALLEL] -часть будет выполняться только " после"или" в"все дополнительные расходы были начислены (не показано на рисунке выше, из-за необходимости избегать усложнения и усложненияпонять предел теоретического, накладного игнорирования, ускорения ( not- ) масштабирование), но все же еще хуже, как выполняемое со скоростью примерно в 4 раза ниже GPU_CLOCK (не говоря уже о ~ 10x ~ 1000x медленнее время задержки доступа к памяти икэша), и, поскольку алгоритм телепортации все еще остается [SERIAL] -линейной, линейно-извилистой обработкой данных TimeSeries, таким образом, это не может иметь отрицательного чистого влияния << 1.0 фактора улучшения на теоретическое ускорение обработки (достигнутая результирующая производительность становится на хуже , чем без такой попытки "улучшить").

Для полного ознакомления с этими сетевыми эффектами, пожалуйста, прочитайте раздел на Критика , где оба Пересмотр со строгой надписью ускорения закона Амдаля и Пересмотр со строгой надзором и переосмыслением были более подробными:


               1
S =  __________________________; where s, ( 1 - s ), N were defined above
                ( 1 - s )            pSO:= [PAR]-Setup-Overhead     add-on
     s  + pSO + _________ + pTO      pTO:= [PAR]-Terminate-Overhead add-on
                    N

                           1                         where s, ( 1 - s ), N
S =  ______________________________________________ ;      pSO, pTO
                    / ( 1 - s )           \                were defined above
     s  + pSO + max|  _________ , atomicP  |  + pTO        atomicP:= further indivisible duration of atomic-process-block
                    \     N               /

График заголовков, приведенный в верхней части этого поста, предоставляет ссылку на живой графический интерфейс с интерактивными входами и анимированными выходами, где один может проверять воздействия значений для p == ( 1 - s ) где-то ниже 1,00 (что является чисто теоретическим, абсолютно 100% [PARALLEL] графиком (что технически невозможно в любом реальном сценарии)), а также настройка воздействиявсе дополнительные издержки в o (выраженные как просто скалярная дробь по соображениям простоты) в редактируемом диапазоне значений ~ < 0.0 ~ 0.0001 >, чтобы лучше чувствовал основные ограничения действительногоПоведение многих ядерных устройств в мире и возможность принимать лучшие инженерные решения, даже не задумываясь о каких-либо шагах кодирования.


И учитывая известное (легко измеримое до одного * 1445)* -разрешение на MQL5-стороне выполнения кода с использованием вызова GetMicrosecondCount()) значений для дополнительных издержек и атомарности обработки - pSO, pTO, atomicP- чистый эффект от попытки продвинуться к OpenCL-оболочке Simple Moving Average , как было показано в коде ядра GPU:

kernel void SMA_executeSMA(          float  ExtLineBufferi_1,
                                     float  price1,
                                     float  price2,
                                     int    InpMAPeriod,
                            __global float *output
                            )
{                                                  // 1: .STO 0x0001, REG
   int len = get_global_id( 1 );                   // 2: .JMP intrinsic_OpenCL_fun(), ... may get masked by reading a hardwired-const-ID#
                                                   // 3: .GET len, REG
   output[len] =                                   // 4: .STO MEM[*],
                 ExtLineBufferi_1                  // 5:     .ADD const,
               + ( price1 - price2 )               //             ( .SUB const, const
                 / InpMAPeriod;                    //               .FDIV REG, const )
}                                                  // 6: .RET

, который не имеет ничего, кроменесколько команд с тактовой частотой 900 МГц - то есть фактор p = ( 1 - s ) в анимированном графике-визуализации пойдет где-то близко к концу p == 0, в результате чего в игре будет доминировать чисто [SERIAL] -часть процессора: GPU-состав системы - (~ несколько, макс. Десятки [ns] + голые (не-маскируемый, с нулевым повторным использованием здесь) задержка доступа к памяти устройства на графическом процессоре ~ 350 - 700+ [ns]).

При таком низком p является плохим знаком настройки производительности (если не АНТИ-ШАБЛОН ) для любых попыток сделать это.

Потому что даже если идти в N-(CPUs) ~ +INF, он все равно никогда не сделает желаемое ускорение (ссылка: может попытаться изменить такие факторы в предлагаемом интерактивном графикевыше и наглядно увидим эффект - насколько низко получатся числа) - , в то время как то же самое можно было бы вычислить почти менее чем за ~ 0.5 [ns], еще более векторизованное,Инструкции ЦП, здесь также вообще отсутствуют затраты на добавление).

Это причины "экономии затрат"
(помимо основной MQL5 one) ПОЧЕМУ лучше не делать это

, которое никогда не окупит сумму всех [SERIAL] дополнительных расходов , введенных в течение всей OpenCL-re-wrapping-there-send-Цирк "Отправляйся и вычисляйся после обратной отправки" на стороне кода CPU / MQL5 (все ради того, чтобы сделать не больше, чем просто эти действительно очень немногие GPU_INSTR-ы), которые были простократко упомянуто выше, , даже если использовалось бесконечное количество ядер GPU .

Вы просто все еще пытаетесь заплатить больше, чем один когда-либо получит обратно.

...