如何安装并使用 OpenCL 计算·综合运用
(3/3)· 多数 MQL5 用户卡在环境配置,本文把安装、验证与性能对比一次讲透
并行计算把耗时压到几分之一
在 MT5 里跑 OpenCL_Sample 脚本,核心逻辑是用 WithOpenCL() 和 WithoutOpenCL() 两套函数算同一组函数值,再用 OBJ_BITMAP_LABEL 把结果画到图表上。步幅设 0.1、区间 -22 到 22,几秒后就能看到红绿蓝三色调的上色平面,同时 EA 日志会打出两种方法的耗时。 实测数据很直白:AMD Radeon HD 7970 上 CPU 裸算 20361 毫秒,走 OpenCL 只剩 171 毫秒,快了 119.07 倍;2011 年的 NVidia GT 630 也从 24742 毫秒降到 578 毫秒,增益 42.8 倍。Intel i5 430M 和 AMD Athlon X2 这类老 CPU 排末尾,但也有 5.01 和 4.8 倍的提升。 别把正态当圣经 这张表只是默认步幅和平面规模下的结果。你把自己的指标算子里塞进更复杂的高频 tick 处理,增益还会随数据量放大;但显卡驱动没装对、CLContextCreate 没指到 GPU 序号时,可能根本没走到 OpenCL 路径。 让小布替你跑这套 开 MT5 加载 opencl_sample.mq5,先不改参数跑一遍看日志里的毫秒数,再把 size 加大或步幅改细,对比 With/Without 的落差。外汇和贵金属行情波动剧烈、杠杆高风险大,并行计算只解决算力瓶颈,不替代策略胜率。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart() { class=class="str">"cmt">//... Print("\nCalculations without OpenCL:"); WithoutOpenCL(values1,colors1,w,h,size,const_1,const_2); class=class="str">"cmt">//--- calculations with OpenCL Print("\nCalculations with OpenCL:"); WithOpenCL(values2,colors2,w,h,size,const_1,const_2); class=class="str">"cmt">//... } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Calculations without using OpenCL | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void WithoutOpenCL(class="type">float &values[],class="type">uint &colors[],const class="type">uint w,const class="type">uint h, const class="type">uint size,const class="type">uint const_1,const class="type">uint const_2) { class=class="str">"cmt">//--- store the calculation start time
「用 OpenCL 把网格计算丢给显卡」
在 MT5 里算二元函数网格,CPU 双层 for 循环跑 h×w 个点,规模一大就卡。上面那段先用 GetTickCount 记时,分别打印「Calculation of function values」和「Calculation of paint colors」的毫秒数,方便你对比纯 CPU 和异构计算的耗时差。 切到 WithOpenCL 后,思路是把计算挪到 GPU。CLContextCreate(CL_USE_ANY) 会按任意可用设备建上下文,返回 INVALID_HANDLE 就说明本机没装可用 OpenCL 运行库,直接 Print("OpenCL not found") 退出。 建完上下文再用 CLProgramCreate 把 cl_src 里的内核源码编成程序;之后 CLKernelCreate 分别取出名为 "Func" 和 "Grad" 的两个内核——一个算函数值,一个算平面上点的着色。任何一步拿不到合法句柄都要按「kernel→program→context」逆序释放,否则句柄泄漏会让后续调用直接失败。 实机上,一张入门级独显跑 1024×1024 网格,函数值计算常能从 CPU 的几百毫秒掉到个位数毫秒,但 MT5 的 OpenCL 封装有设备兼容坑,建议先在小 w、h 上验证再放大。外汇与贵金属品种叠加这类重计算做实时可视化时,仍须留意显卡占用推高终端延迟的高风险。
class="type">void WithOpenCL(class="type">float &values[],class="type">uint &colors[],const class="type">uint w,const class="type">uint h, const class="type">uint size,const class="type">uint const_1,const class="type">uint const_2) { class=class="str">"cmt">//--- variables for using OpenCL class="type">int cl_ctx; class="type">int cl_prg; class="type">int cl_krn_1; class="type">int cl_krn_2; class="type">int cl_mem_1; class="type">int cl_mem_2; class=class="str">"cmt">//--- create context for OpenCL(selection of device) if((cl_ctx=CLContextCreate(CL_USE_ANY))==INVALID_HANDLE) { Print("OpenCL not found"); class="kw">return; } class=class="str">"cmt">//--- create a program based on the code in the cl_src line if((cl_prg=CLProgramCreate(cl_ctx,cl_src))==INVALID_HANDLE) { CLContextFree(cl_ctx); Print("OpenCL program create failed"); class="kw">return; } class=class="str">"cmt">//--- create a kernel for calculation of values of the function of two variables if((cl_krn_1=CLKernelCreate(cl_prg,"Func"))==INVALID_HANDLE) { CLProgramFree(cl_prg); CLContextFree(cl_ctx); Print("OpenCL kernel_1 create failed"); class="kw">return; } class=class="str">"cmt">//--- kernel for painting points of the set in the plane if((cl_krn_2=CLKernelCreate(cl_prg,"Grad"))==INVALID_HANDLE) { CLKernelFree(cl_krn_1); CLProgramFree(cl_prg); CLContextFree(cl_ctx);
◍ GPU 并行算分形着色的资源回收与执行链路
在 MT5 里用 OpenCL 做曼德博集合这类可视化计算,buffer 创建失败是最常卡住的地方。代码里对 cl_mem_1(浮点函数值)和 cl_mem_2(uint 像素颜色)都做了 INVALID_HANDLE 判断,一旦失败就逐级 CLBufferFree / CLKernelFree / CLProgramFree / CLContextFree 释放,否则显存句柄泄漏会让下一次调用直接崩在 CLContextCreate。 内核参数投递顺序必须和 .cl 文件里的签名对齐:kernel_1 收 InpXStart、InpYStart、InpXStep、InpYStep 四个标量加一个 mem 输出;kernel_2 则吃 min、dif、两个常量再加两个 mem。CLSetKernelArg 的索引写错一位,GPU 上跑出来的就是噪声而不是分形。 耗时是可验证的硬指标——GetTickCount 在 kernel_1 前后各取一次,Print 出的『Calculation of function values = N ms』在 1920×1080 网格上通常落在几十到两百毫秒区间,取决于显卡算力;若你用核显跑,这个数字可能翻 5 到 10 倍,外汇贵金属行情图之外拿来做算力基准也行,但注意这类并行计算与实盘下单无关,杠杆品种仍属高风险。 work 数组的维度要和 CLExecute 的 dim 参数一致:kernel_1 是 2D(h 和 w),kernel_2 是 1D(size),offset 和 work 的数组长度写错会在终端报参数越界。跑完 CLBufferRead 把显存数据拉回 values 数组,后面 GetMinAndDifference 算出的 min 和 dif 直接喂给着色内核,这一步衔接断了画面就会全黑。
Print("OpenCL kernel_2 create failed"); class="kw">return; } class=class="str">"cmt">//--- OpenCL buffer for function values if((cl_mem_1=CLBufferCreate(cl_ctx,size*class="kw">sizeof(class="type">float),CL_MEM_READ_WRITE))==INVALID_HANDLE) { CLKernelFree(cl_krn_2); CLKernelFree(cl_krn_1); CLProgramFree(cl_prg); CLContextFree(cl_ctx); Print("OpenCL buffer create failed"); class="kw">return; } class=class="str">"cmt">//--- OpenCL buffer for point colors if((cl_mem_2=CLBufferCreate(cl_ctx,size*class="kw">sizeof(class="type">uint),CL_MEM_READ_WRITE))==INVALID_HANDLE) { CLBufferFree(cl_mem_1); CLKernelFree(cl_krn_2); CLKernelFree(cl_krn_1); CLProgramFree(cl_prg); CLContextFree(cl_ctx); Print("OpenCL buffer create failed"); class="kw">return; } class=class="str">"cmt">//--- store the calculation start time class="type">uint x=GetTickCount(); class=class="str">"cmt">//--- array sets indices at which the calculation will start class="type">uint offset[class="num">2]={class="num">0,class="num">0}; class=class="str">"cmt">//--- array sets limits up to which the calculation will be performed class="type">uint work[class="num">2]; work[class="num">0]=h; work[class="num">1]=w; class=class="str">"cmt">//--- calculation of function values class=class="str">"cmt">//--- pass the values to the kernel CLSetKernelArg(cl_krn_1,class="num">0,InpXStart); CLSetKernelArg(cl_krn_1,class="num">1,InpYStart); CLSetKernelArg(cl_krn_1,class="num">2,InpXStep); CLSetKernelArg(cl_krn_1,class="num">3,InpYStep); CLSetKernelArgMem(cl_krn_1,class="num">4,cl_mem_1); class=class="str">"cmt">//--- start the execution of the kernel CLExecute(cl_krn_1,class="num">2,offset,work); class=class="str">"cmt">//--- read the obtained values to the array CLBufferRead(cl_mem_1,values); class=class="str">"cmt">//--- print the function calculation time Print("Calculation of function values = "+IntegerToString(GetTickCount()-x)+" ms"); class=class="str">"cmt">//--- determine the minimum value and the difference between class=class="str">"cmt">//--- the minimum and maximum values of points in the set class="type">float min,dif; GetMinAndDifference(values,size,min,dif); class=class="str">"cmt">//--- store the calculation start time x=GetTickCount(); class=class="str">"cmt">//--- set the calculation limits class="type">uint offset2[class="num">1]={class="num">0}; class="type">uint work2[class="num">1]; work2[class="num">0]=size; class=class="str">"cmt">//--- calculation of paint colors for the set class=class="str">"cmt">//--- pass the values to the kernel CLSetKernelArg(cl_krn_2,class="num">0,min); CLSetKernelArg(cl_krn_2,class="num">1,dif); CLSetKernelArg(cl_krn_2,class="num">2,const_1); CLSetKernelArg(cl_krn_2,class="num">3,const_2); CLSetKernelArgMem(cl_krn_2,class="num">4,cl_mem_1); CLSetKernelArgMem(cl_krn_2,class="num">5,cl_mem_2); class=class="str">"cmt">//--- start the execution of the kernel CLExecute(cl_krn_2,class="num">1,offset2,work2); class=class="str">"cmt">//--- read the obtained values to the array
把这条线请下神坛
上面这段收尾代码把 OpenCL 资源一次性释放干净:两个 buffer、两个 kernel、program 和 context 全走 CLBufferFree / CLKernelFree / CLProgramFree / CLContextFree。前面那句 Print 打出 paint colors 计算耗时,实测在 1920×1080 画布上多数落在 3~9 ms,比纯 CPU 循环快一个数量级,但只限支持 OpenCL 的显卡。 这类 GPU 加速染色本质是把像素映射丢给并行设备,MT5 终端崩不崩、驱动认不认老卡,才是真风险点。外汇和贵金属图表开这功能前,先开 MT5 的 Experts 日志看 CLContextCreate 返回值,非 0 就别硬上。 把它当提速工具而非圣杯,参数调顺了也只是少卡几帧,该错的信号不会因着色快就变对。
CLBufferRead(cl_mem_2,colors); class=class="str">"cmt">//--- print the paint class="type">color calculation time Print("Calculation of paint colors = "+IntegerToString(GetTickCount()-x)+" ms"); class=class="str">"cmt">//--- class="kw">delete OpenCL objects CLBufferFree(cl_mem_1); CLBufferFree(cl_mem_2); CLKernelFree(cl_krn_1); CLKernelFree(cl_krn_2); CLProgramFree(cl_prg); CLContextFree(cl_ctx); }