端到端系统仿真加速使用的GPU

该示例示出,其可以被用于加速用在MATLAB®通信工具箱™软件系统对象的误码率(BER)的模拟四种技术的比较。一个小的系统的基础上,卷积编码,并行循环的执行使用示出的代码生成的使用MATLAB®编码器™产品的影响PARFOR在并行计算工具箱™产品中,代码生成和PARFOR和基于GPU的系统对象。

系统中的对象这个例子的特点是在通信工具箱产品进行访问。为了运行这个例子,你必须有一个MATLAB编码器许可证,并行计算工具箱许可证,和足够的GPU。

系统设计及仿真参数

此示例使用一个简单的卷积编码系统来说明仿真加速策略。该系统使用产生随机消息比特兰迪。发射机使用速率1/2卷积编码器对这些比特进行编码,应用QPSK调制方案,然后传输这些符号。这些符号通过一个AWGN通道,在那里发生信号损坏。QPSK解调发生在接收端,并使用维特比算法对损坏的比特进行解码。最后,计算误码率。本系统中使用的系统对象为:

  • comm.ConvolutionalEncoder - 卷积编码

  • comm.PSKModulator - QPSK调制

  • comm.AWGNChannel - AWGN信道

  • comm.PSKDemodulator - QPSK解调(约LLR)

  • comm.ViterbiDecoder - 维特比解码

对于收发器的代码可以发现:

沿着误码率曲线的每个点代表上述收发信机代码的多次迭代的结果。为了获得合理的时间量准确的结果,该模拟将聚集每信噪比(SNR)值至少为200个比特错误,并且数据的至多5000级的数据包。分组2000表示消息比特。的SNR范围从1 dB至5分贝。

iterCntThreshold = 5000;minErrThreshold = 200;MSGL = 2000;snrdb = 1:5;

初始化

再次呼吁收发器功能的因素了安装时间和对象的构造开销。对象存储在每个功能的持久性变量。

ERRS =零(长度(snrdb),1);iters =零(长度(snrdb),1);berplot =细胞(1,5);numframes = 500;%GPU版本并行运行500帧。viterbiTransceiverCPU(-10年,1,1);viterbiTransceiverGPU(-10、1、1、numframes);N = 1;%N跟踪正在运行的仿真变量

工作流程

这个例子的工作流程是:

  1. 运行系统对象的基线模拟

  2. 使用MATLAB编码器生成一个MEX函数进行仿真

  3. 使用PARFOR并行运行误码率模拟

  4. 结合生成MEX函数PARFOR

  5. 使用基于GPU的系统对象

fprintf中(1,“误码率加速分析示例\n\n”);

基线模拟

为建立各种加速策略的参考点,仅使用系统对象生成误码率曲线。无线电收发机的密码在viterbiTransceiverCPU.m

fprintf中(1,“***基线 - 标准体系物体模拟*** \ n”);%对于每个snrdb模拟创建随机流S = RandStream.create('mrg32k3a'“NumStreams”, 1...'CellOutput',真正,“NormalTransform”“反转”);RandStream.setGlobalStream (s {1});TS =抽动;对于II = 1:numel(snrdb)fprintf中(1,'迭代次数%d, SNR (dB) = %d\n'第二,snrdb (ii));[errs(ii),iter (ii)] =viterbiTransceiverCPU(snrdb(ii), minErrThreshold, iterCntThreshold);结束BER = errs./(MSGL * iters);baseTime = toc (ts);berplot {N} = 1;降序{N} =“基线”;reportResultsCommSysGPU(N,baseTime,baseTime,“基线”);

代码生成

利用MATLAB编码器,可以使用优化的C代码相匹配的预编译MATLAB代码来生成MEX文件。由于viterbiTransceiverCPU函数符合MATLAB生成子集的代码,可以将其编译成MEX函数而无需修改。

你必须有一个MATLAB编码器的许可证来运行示例的这一部分。

fprintf中(1,' \ n * * *基线+ codegen * * * \ n”);N = N + 1;%增加模拟计数器%创建编码器对象并关闭检查,这将导致低%的性能。fprintf中(1,“生成代码...”);config_obj = coder.config (墨西哥人的);config_obj.EnableDebugging = FALSE;config_obj.IntegrityChecks = FALSE;config_obj.ResponsivenessChecks = FALSE;config_obj.EchoExpressions = FALSE;%生成一个MEX文件代码生成(“viterbiTransceiverCPU.m”'-config'“config_obj”'-args', {snrdb(1), minErrThreshold, iterCntThreshold})“。\ n”);%运行一次,以消除启动开销。viterbiTransceiverCPU_mex(-10年,1,1);s = RandStream.getGlobalStream;重置(年代);方法中使用生成的MEX函数viterbiTransceiverCPU_mex%模拟循环。TS =抽动;对于II = 1:numel(snrdb)fprintf中(1,'迭代次数%d, SNR (dB) = %d\n'第二,snrdb (ii));[ERRS(ii)中,iters(II)] = viterbiTransceiverCPU_mex(snrdb(ii)中,minErrThreshold,iterCntThreshold);结束BER = errs./(MSGL * iters);trialtime = TOC(TS);berplot {N} = 1;降序{N} =“代码生成”;reportResultsCommSysGPU (N trialtime baseTime“基线+代码生成”);

并行循环执行

运用PARFOR,MATLAB执行针对所有并联SNR值所述收发器的代码。这需要打开并行游泳池和添加PARFOR循环。

你必须有一个并行计算工具箱许可证来运行示例的这一部分。

fprintf中(1,' \ n * * *基线+ parfor * * * \ n”);fprintf中(1,'访问多个CPU内核... \ N');如果isempty (gcp (“nocreate”pool = parpool;poolWasOpen = false;其他的池= GCP;poolWasOpen = TRUE;结束纳瓦= pool.NumWorkers;N = N + 1;%增加模拟计数器snrN = numel(snrdb);MT = minErrThreshold /净重;IT = iterCntThreshold /净重;errN =零(NW,snrN);itrN =零(NW,snrN);%重复snrdbsnrdb_rep = repmat (snrdb, nW, 1);%为每个工人的独立流S = RandStream.create('mrg32k3a'“NumStreams”,NW,...'CellOutput',真正,“NormalTransform”“反转”);%跑前PARFORjj = 1:西北RandStream.setGlobalStream (s {jj});viterbiTransceiverCPU(-10年,1,1);结束fprintf中(1,“开始PARFOR工作......”);TS =抽动;PARFORjj = 1:西北对于II = 1:snrN [ERR,ITR] = viterbiTransceiverCPU(snrdb_rep(JJ,ⅱ),MT,IT);errN (jj, ii) =犯错;itrN(JJ,ⅱ)= ITR;结束结束BER =总和(errN)./(MSGL *总和(itrN));trialtime = TOC(TS);fprintf中(1,'完成。\ n');berplot {N} = 1;降序{N} ='PARFOR';reportResultsCommSysGPU (N trialtime baseTime'基线+ PARFOR');

PARFOR和代码生成

您可以结合最后两种技术来获得额外的加速。编译后的MEX函数可以在a的内部执行PARFOR循环。

您必须拥有MATLAB Coder许可证和并行计算工具箱许可证才能运行示例的这一部分。

fprintf中(1,'\ n ***基线+代码生成+ PARFOR *** \ n');N = N + 1;%增加模拟计数器%跑前PARFORjj = 1:西北RandStream.setGlobalStream (s {jj});viterbiTransceiverCPU_mex (1 1 1);%使用相同的mex文件结束fprintf中(1,“开始PARFOR工作......”);TS =抽动;PARFORjj = 1:西北对于2:snrN [err, itr] = viterbiTransceiverCPU_mex(snrdb_rep(jj,ii), mT, iT);errN (jj, ii) =犯错;itrN(JJ,ⅱ)= ITR;结束结束BER =总和(errN)./(MSGL *总和(itrN));trialtime = TOC(TS);fprintf中(1,'完成。\ n');berplot {N} = 1;降序{N} =“codegen + parfor”;reportResultsCommSysGPU (N trialtime baseTime'基线+代码生成+ PARFOR');

GPU

系统中的对象的viterbiTransceiverCPU函数使用可用于在GPU上执行。基于gpu的版本有:

  • comm.gpu。卷积编码

  • comm.gpu.PSKModulator - QPSK调制

  • comm.gpu。AWGNChannel - AWGN频道

  • comm.gpu.PSKDemodulator - QPSK解调(约LLR)

  • comm.gpu.ViterbiDecoder - 维特比解码

一个GPU同时处理大量数据的时候是最有效的。基于GPU的系统对象可以在步骤方法的单次调用处理多个帧。该numframes变量表示每个呼叫处理的帧的数量。这类似于PARFOR除了并行性是基于每个对象的,而不是每个对象的viterbiTransceiverCPU呼叫的基础。

你必须有一个并行计算工具箱许可证和1.3CUDA®GPU能够运行示例的这一部分。

fprintf中(1,' \ n * * * * * * GPU \ n”);N = N + 1;%增加模拟计数器试一试dev的= parallel.gpu.GPUDevice.current;fprintf中(...'GPU检测(%S,%d多处理器,计算能力%S)\ N'...dev.Name、dev.MultiprocessorCount dev.ComputeCapability);sg = parallel.gpu.RandStream.create ('mrg32k3a'“NumStreams”, 1“NormalTransform”“反转”);parallel.gpu.RandStream.setGlobalStream (sg);TS =抽动;对于II = 1:numel(snrdb)fprintf中(1,'迭代次数%d, SNR (dB) = %d\n'第二,snrdb (ii));[errs(ii),iter (ii)] =viterbiTransceiverGPU(snrdb(ii), minErrThreshold, iterCntThreshold, numframes)结束BER = errs./(MSGL * iters);trialtime = TOC(TS);berplot {N} = 1;降序{N} =“图形”;reportResultsCommSysGPU (N trialtime baseTime“基数+ GPU”);fprintf中(1,“。\ n”);%#确定%报告​​认为,适当的GPU没有被发现。fprintf中(1,[“找不到合适的GPU或无法”...'执行GPU代码。\ N']);结束

分析

比较这些试验的结果,很明显,GPU明显快于任何其他模拟加速技术。这种性能提升需要对模拟代码进行非常温和的更改。然而,没有损失在比特误码率性能如下图所示。曲线中非常微小的差异是由于不同的随机数生成算法和/或对曲线上的同一点平均不同数量的数据的影响造成的。

行= {“kx -。”“ro - - - - - -”'cs--'“m ^:”'G*-'};对于II = 1:numel(降序)semilogy(snrdb,berplot {二},线{二});保持;结束保持;标题(“不同加速策略的误码率”);xlabel(“信噪比(dB)”);ylabel('BER');图例(降序{:});

清理

留在原来的状态并行池。

如果〜poolWasOpen删除(GCP);结束