239 lines
7.5 KiB
MQL5
239 lines
7.5 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| BitonicSort.mq5 |
| |||
//| Copyright 2000-2025, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "Copyright 2000-2025, MetaQuotes Ltd."
| |||
#property link "https://www.mql5.com"
| |||
#property version "1.00"
| |||
//--- COpenCL class
| |||
#include <OpenCL/OpenCL.mqh>
| |||
#resource "Kernels/bitonicsort.cl" as string cl_program
| |||
//+------------------------------------------------------------------+
| |||
//| QuickSortAscending |
| |||
//+------------------------------------------------------------------+
| |||
//| The function sorts array[] QuickSort algorithm. |
| |||
//| |
| |||
//| Arguments: |
| |||
//| array : Array with values to sort |
| |||
//| first : First element index |
| |||
//| last : Last element index |
| |||
//| |
| |||
//| Return value: None |
| |||
//+------------------------------------------------------------------+
| |||
void QuickSortAscending(double &array[],int first,int last)
| |||
{
| |||
int i,j;
| |||
double p_double,t_double;
| |||
//---
| |||
if(first<0 || last<0)
| |||
return;
| |||
//---
| |||
i=first;
| |||
j=last;
| |||
while(i<last)
| |||
{
| |||
p_double=array[(first+last)>>1];
| |||
while(i<j)
| |||
{
| |||
while(array[i]<p_double)
| |||
{
| |||
if(i==ArraySize(array)-1)
| |||
break;
| |||
i++;
| |||
}
| |||
while(array[j]>p_double)
| |||
{
| |||
if(j==0)
| |||
break;
| |||
j--;
| |||
}
| |||
if(i<=j)
| |||
{
| |||
//-- swap elements i and j
| |||
t_double=array[i];
| |||
array[i]=array[j];
| |||
array[j]=t_double;
| |||
i++;
| |||
if(j==0)
| |||
break;
| |||
j--;
| |||
}
| |||
}
| |||
if(first<j)
| |||
QuickSortAscending(array,first,j);
| |||
first=i;
| |||
j=last;
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| QuickSort_CPU |
| |||
//+------------------------------------------------------------------+
| |||
bool QuickSort_CPU(double &data_array[],ulong &time_cpu)
| |||
{
| |||
int data_count=ArraySize(data_array);
| |||
//---
| |||
if(data_count<=1)
| |||
return(false);
| |||
//--- sort values on CPU
| |||
time_cpu=GetMicrosecondCount();
| |||
QuickSortAscending(data_array,0,data_count-1);
| |||
time_cpu=ulong((GetMicrosecondCount()-time_cpu)/1000);
| |||
//---
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| BitonicSort_GPU |
| |||
//+------------------------------------------------------------------+
| |||
bool BitonicSort_GPU(COpenCL &OpenCL,double &data_array[],ulong &time_gpu)
| |||
{
| |||
int data_count=ArraySize(data_array);
| |||
//---
| |||
if(data_count<=1)
| |||
return(false);
| |||
//--- check support working with double
| |||
if(!OpenCL.SupportDouble())
| |||
{
| |||
PrintFormat("Working with double (cl_khr_fp64) is not supported on the device.");
| |||
return(false);
| |||
}
| |||
| |||
OpenCL.SetKernelsCount(1);
| |||
OpenCL.KernelCreate(0,"BitonicSort_GPU");
| |||
//--- create buffers
| |||
OpenCL.SetBuffersCount(1);
| |||
if(!OpenCL.BufferFromArray(0,data_array,0,data_count,CL_MEM_READ_WRITE))
| |||
{
| |||
PrintFormat("Error in BufferFromArray for data array. Error code=%d",GetLastError());
| |||
return(false);
| |||
}
| |||
OpenCL.SetArgumentBuffer(0,0,0);
| |||
//---
| |||
uint work_offset[1]= {0};
| |||
uint global_size[1];
| |||
uint passes_total=0;
| |||
uint stages_total=0;
| |||
| |||
global_size[0]=data_count>>1;
| |||
for(uint temp=data_count; temp>1; temp>>=1)
| |||
stages_total++;
| |||
//--- GPU calculation start
| |||
time_gpu=GetMicrosecondCount();
| |||
for(uint stage=0; stage<stages_total; stage++)
| |||
{
| |||
//--- set stage of the algorithm
| |||
OpenCL.SetArgument(0,1,stage);
| |||
for(uint pass=0; pass<stage+1; pass++)
| |||
{
| |||
//--- set pass of the current stage
| |||
OpenCL.SetArgument(0,2,pass);
| |||
//--- execute kernel
| |||
if(!OpenCL.Execute(0,1,work_offset,global_size))
| |||
{
| |||
PrintFormat("Error in Execute. Error code=%d",GetLastError());
| |||
return(false);
| |||
}
| |||
else
| |||
passes_total++;
| |||
}
| |||
}
| |||
//---
| |||
if(!OpenCL.BufferRead(0,data_array,0,0,data_count))
| |||
{
| |||
PrintFormat("Error in BufferRead for data array with %d size. Error code=%d",data_count,GetLastError());
| |||
return(false);
| |||
}
| |||
//--- GPU calculation finish
| |||
time_gpu=ulong((GetMicrosecondCount()-time_gpu)/1000);
| |||
PrintFormat("Bitonic sort finished. Total stages=%d, total passes=%d",stages_total,passes_total);
| |||
//---
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| PrepareDataArray |
| |||
//+------------------------------------------------------------------+
| |||
bool PrepareDataArray(long global_memory_size,double &data[],int &data_count)
| |||
{
| |||
int pwr_max=(int)(MathLog(global_memory_size/2/sizeof(double))/MathLog(2));
| |||
int pwr=(int)MathMax(15,pwr_max-4);
| |||
//--- prepare array and generate random data
| |||
data_count=(int)MathPow(2,pwr);
| |||
| |||
if(data_count<4096)
| |||
data_count=4096;
| |||
| |||
if(data_count>4*1024*1024)
| |||
data_count=4*1024*1024;
| |||
| |||
Print(data_count," elements in double array");
| |||
//---
| |||
if(ArrayResize(data,data_count)<data_count)
| |||
{
| |||
Print("Error in ArrayResize. Error code=",GetLastError());
| |||
return(false);
| |||
}
| |||
| |||
for(int i=0; i<data_count; i++)
| |||
data[i]=(double)(100000000*MathRand()/32767.0);
| |||
//---
| |||
return(true);
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
//| Script program start function |
| |||
//+------------------------------------------------------------------+
| |||
void OnStart()
| |||
{
| |||
COpenCL OpenCL;
| |||
//--- OpenCL
| |||
if(!OpenCL.Initialize(cl_program,true))
| |||
{
| |||
PrintFormat("Error in OpenCL initialization. Error code=%d",GetLastError());
| |||
return;
| |||
}
| |||
//---
| |||
long global_memory_size=0;
| |||
| |||
if(!OpenCL.GetGlobalMemorySize(global_memory_size))
| |||
{
| |||
Print("Error in request of global memory size. Error code=",GetLastError());
| |||
return;
| |||
}
| |||
//--- prepare array with random values
| |||
double data_cpu[];
| |||
int data_count=0;
| |||
| |||
if(PrepareDataArray(global_memory_size,data_cpu,data_count)==false)
| |||
return;
| |||
//--- copy array values for sorting on GPU
| |||
double data_gpu[];
| |||
| |||
if(ArrayCopy(data_gpu,data_cpu,0,0,data_count)!=data_count)
| |||
return;
| |||
//--- Quick sort values using CPU
| |||
ulong time_cpu=0;
| |||
| |||
if(!QuickSort_CPU(data_cpu,time_cpu))
| |||
return;
| |||
//--- Bitonic sort values using GPU
| |||
ulong time_gpu=0;
| |||
| |||
if(!BitonicSort_GPU(OpenCL,data_gpu,time_gpu))
| |||
return;
| |||
//--- remove OpenCL objects
| |||
OpenCL.Shutdown();
| |||
//--- calculate CPU/GPU ratio
| |||
double CPU_GPU_ratio=0;
| |||
| |||
if(time_gpu!=0)
| |||
CPU_GPU_ratio=1.0*time_cpu/time_gpu;
| |||
| |||
PrintFormat("time CPU=%d ms, time GPU =%d ms, CPU/GPU ratio: %f",time_cpu,time_gpu,CPU_GPU_ratio);
| |||
//--- check calculations
| |||
double total_error=0;
| |||
| |||
for(int i=0; i<data_count; i++)
| |||
total_error+=MathAbs(data_gpu[i]-data_cpu[i]);
| |||
| |||
PrintFormat("Total error = %f",total_error);
| |||
}
| |||
//+------------------------------------------------------------------+
|