В этом примере показано сравнение четырех методов, которые можно использовать для ускорения моделирования частоты битовых ошибок (BER) с использованием системных объектов в программном обеспечении MATLAB ® Communications Toolbox™. Небольшая система, основанная на сверточном кодировании, иллюстрирует эффект генерации кода с использованием продукта MATLAB ® Coder™, параллельное выполнение цикла с использованиемparfor в продукте Parallel Computing Toolbox™ сочетание генерации кода и parforи системные объекты на основе графического процессора.
Системные объекты этого примера доступны в продукте Communications Toolbox. Для выполнения этого примера необходимо иметь лицензию MATLAB Coder, лицензию Parallel Computing Toolbox и достаточный графический процессор.
В этом примере используется простая система сверточного кодирования для иллюстрации стратегий ускорения моделирования. Система генерирует биты случайных сообщений с помощью randi. Передатчик кодирует эти биты с использованием сверточного кодера скорости 1/2, применяет схему модуляции QPSK и затем передает символы. Символы проходят через канал AWGN, где происходит повреждение сигнала. Демодуляция QPSK происходит в приемнике, и поврежденные биты декодируются с использованием алгоритма Витерби. Наконец, вычисляется частота битовых ошибок. В этой системе используются следующие системные объекты:
коммуникация. ConvolutionalEncoder - сверточное кодирование
comm.PSKModulator - модуляция QPSK
comm.AWGNChannel - канал AWGN
comm.PSKDemodulator - демодуляция QPSK (приблизительно LLR)
comm.ViterbiDecoder - декодирование Витерби
Код приемопередатчиков можно найти в:
Каждая точка вдоль кривой частоты битовых ошибок представляет результат многих итераций кода приемопередатчика, описанного выше. Для получения точных результатов за разумное время моделирование соберет по меньшей мере 200 битовых ошибок на значение отношения сигнал/шум (SNR) и не более 5000 пакетов данных. Пакет представляет 2000 битов сообщения. SNR находится в диапазоне от 1 дБ до 5 дБ.
iterCntThreshold = 5000; minErrThreshold = 200; msgL = 2000; snrdb = 1:5;
Вызовите функции приемопередатчика один раз, чтобы уменьшить время установки и накладные расходы на построение объекта. Объекты хранятся в постоянных переменных в каждой функции.
errs = zeros(length(snrdb),1); iters = zeros(length(snrdb),1); berplot = cell(1,5); numframes = 500; %GPU version runs 500 frames in parallel. viterbiTransceiverCPU(-10,1,1); viterbiTransceiverGPU(-10,1,1,numframes); N=1; %N tracks which simulation variant is run
Поток операций для этого примера:
Выполнение моделирования базовой линии системных объектов
Использование кодера MATLAB для создания функции MEX для моделирования
Используйте parfor для параллельного выполнения моделирования частоты битовых ошибок
Объединение созданной функции MEX с parfor
Использование системных объектов на основе GPU
fprintf(1,'Bit Error Rate Acceleration Analysis Example\n\n');
Чтобы установить опорную точку для различных стратегий ускорения, кривая частоты битовых ошибок генерируется только с использованием системных объектов. Код приемопередатчика находится в viterbiTransceiverCPU.m.
fprintf(1,'***Baseline - Standard System object simulation***\n'); % create random stream for each snrdb simulation s = RandStream.create('mrg32k3a','NumStreams',1,... 'CellOutput',true,'NormalTransform', 'Inversion'); RandStream.setGlobalStream(s{1}); ts = tic; for ii=1:numel(snrdb) fprintf(1,'Iteration number %d, SNR (dB) = %d\n',ii, snrdb(ii)); [errs(ii),iters(ii)] =viterbiTransceiverCPU(snrdb(ii), minErrThreshold, iterCntThreshold); end ber = errs./ (msgL* iters); baseTime=toc(ts); berplot{N} = ber; desc{N} = 'baseline'; reportResultsCommSysGPU(N, baseTime,baseTime, 'Baseline');
С помощью кодера MATLAB можно создать файл MEX с оптимизированным кодом C, который соответствует предварительно скомпилированному коду MATLAB. Потому что viterbiTransceiverCPU функция соответствует подмножеству генерации кода MATLAB, она может быть скомпилирована в функцию MEX без модификации.
Для выполнения этой части примера необходимо иметь лицензию MATLAB Coder.
fprintf(1,'\n***Baseline + codegen***\n'); N=N+1; %Increase simulation counter % Create the coder object and turn off checks which will cause low % performance. fprintf(1,'Generating Code ...'); config_obj = coder.config('MEX'); config_obj.EnableDebugging = false; config_obj.IntegrityChecks = false; config_obj.ResponsivenessChecks = false; config_obj.EchoExpressions = false; % Generate a MEX file codegen('viterbiTransceiverCPU.m', '-config', 'config_obj', '-args', {snrdb(1), minErrThreshold, iterCntThreshold} ) fprintf(1,' Done.\n'); %Run once to eliminate startup overhead. viterbiTransceiverCPU_mex(-10,1,1); s = RandStream.getGlobalStream; reset(s); % Use the generated MEX function viterbiTransceiverCPU_mex in the % simulation loop. ts = tic; for ii=1:numel(snrdb) fprintf(1,'Iteration number %d, SNR (dB) = %d\n',ii, snrdb(ii)); [errs(ii),iters(ii)] = viterbiTransceiverCPU_mex(snrdb(ii), minErrThreshold, iterCntThreshold); end ber = errs./ (msgL* iters); trialtime=toc(ts); berplot{N} = ber; desc{N} = 'codegen'; reportResultsCommSysGPU(N, trialtime,baseTime, 'Baseline + codegen');
Используя parforMATLAB выполняет код приемопередатчика для всех значений SNR параллельно. Для этого необходимо открыть параллельный пул и добавить parfor цикл.
Для выполнения этой части примера необходимо иметь лицензию Parallel Computing Toolbox.
fprintf(1,'\n***Baseline + parfor***\n'); fprintf(1,'Accessing multiple CPU cores ...\n'); if isempty(gcp('nocreate')) pool = parpool; poolWasOpen = false; else pool = gcp; poolWasOpen = true; end nW=pool.NumWorkers; N=N+1; %Increase simulation counter snrN = numel(snrdb); mT = minErrThreshold / nW; iT = iterCntThreshold / nW; errN = zeros(nW, snrN); itrN = zeros(nW, snrN); % replicate snrdb snrdb_rep=repmat(snrdb,nW,1); % create an independent stream for each worker s = RandStream.create('mrg32k3a','NumStreams',nW,... 'CellOutput',true,'NormalTransform', 'Inversion'); % pre-run parfor jj=1:nW RandStream.setGlobalStream(s{jj}); viterbiTransceiverCPU(-10, 1, 1); end fprintf(1,'Start parfor job ... '); ts = tic; parfor jj=1:nW for ii=1:snrN [err, itr] = viterbiTransceiverCPU(snrdb_rep(jj,ii), mT, iT); errN(jj,ii) = err; itrN(jj,ii) = itr; end end ber = sum(errN)./ (msgL*sum(itrN)); trialtime=toc(ts); fprintf(1,'Done.\n'); berplot{N} = ber; desc{N} = 'parfor'; reportResultsCommSysGPU(N, trialtime,baseTime, 'Baseline + parfor');
Можно объединить последние два метода дополнительного ускорения. Скомпилированная функция MEX может выполняться внутри parfor цикл.
Для выполнения этой части примера необходимо иметь лицензию MATLAB Coder и лицензию Parallel Computing Toolbox.
fprintf(1,'\n***Baseline + codegen + parfor***\n'); N=N+1; %Increase simulation counter % pre-run parfor jj=1:nW RandStream.setGlobalStream(s{jj}); viterbiTransceiverCPU_mex(1, 1, 1); % use the same mex file end fprintf(1,'Start parfor job ... '); ts = tic; parfor jj=1:nW for ii=1:snrN [err, itr] = viterbiTransceiverCPU_mex(snrdb_rep(jj,ii), mT, iT); errN(jj,ii) = err; itrN(jj,ii) = itr; end end ber = sum(errN)./ (msgL*sum(itrN)); trialtime=toc(ts); fprintf(1,'Done.\n'); berplot{N} = ber; desc{N} = 'codegen + parfor'; reportResultsCommSysGPU(N, trialtime,baseTime, 'Baseline + codegen + parfor');
Системные объекты, которые viterbiTransceiverCPU использование функций доступно для выполнения на GPU. Версии на основе GPU:
comm.gpu. ConvolutionalEncoder - сверточное кодирование
comm.gpu.PSKModulator - модуляция QPSK
comm.gpu.AWGNChannel - канал AWGN
comm.gpu.PSKDemodulator - демодуляция QPSK (приблизительно LLR)
comm.gpu.ViterbiDecoder - декодирование Витерби
Графический процессор наиболее эффективен при одновременной обработке больших объемов данных. Системные объекты на основе GPU могут обрабатывать несколько кадров за один вызов пошагового метода. numframes переменная представляет количество кадров, обработанных за один вызов. Это аналогично parfor за исключением того, что параллелизм основан на каждом объекте, а не на viterbiTransceiverCPU основа вызова.
Для выполнения этой части примера необходимо иметь лицензию Parallel Computing Toolbox и графический процессор с поддержкой CUDA ® 1.3.
fprintf(1,'\n***GPU***\n'); N=N+1; %Increase simulation counter try dev = parallel.gpu.GPUDevice.current; fprintf(... 'GPU detected (%s, %d multiprocessors, Compute Capability %s)\n',... dev.Name, dev.MultiprocessorCount, dev.ComputeCapability); sg = parallel.gpu.RandStream.create('mrg32k3a','NumStreams',1,'NormalTransform','Inversion'); parallel.gpu.RandStream.setGlobalStream(sg); ts = tic; for ii=1:numel(snrdb) fprintf(1,'Iteration number %d, SNR (dB) = %d\n',ii, snrdb(ii)); [errs(ii),iters(ii)] =viterbiTransceiverGPU(snrdb(ii), minErrThreshold, iterCntThreshold, numframes); end ber = errs./ (msgL* iters); trialtime=toc(ts); berplot{N} = ber; desc{N} = 'GPU'; reportResultsCommSysGPU(N, trialtime,baseTime, 'Baseline + GPU'); fprintf(1,' Done.\n'); catch %#ok<CTCH> % Report that the appropriate GPU was not found. fprintf(1, ['Could not find an appropriate GPU or could not ', ... 'execute GPU code.\n']); end
Сравнивая результаты этих испытаний, ясно, что GPU значительно быстрее, чем любой другой метод ускорения моделирования. Это повышение производительности требует очень скромного изменения кода моделирования. Однако нет потерь в производительности частоты битовых ошибок, как показано на следующем графике. Очень незначительные различия в кривых являются результатом различных алгоритмов генерации случайных чисел и/или эффектов усреднения различных количеств данных для одной и той же точки кривой.
lines = {'kx-.', 'ro-', 'cs--', 'm^:', 'g*-'};
for ii=1:numel(desc)
semilogy(snrdb, berplot{ii}, lines{ii});
hold on;
end
hold off;
title('Bit Error Rate for Various Acceleration Strategies');
xlabel('Signal to Noise Ratio (dB)');
ylabel('BER');
legend(desc{:});

Оставьте параллельный пул в исходном состоянии.
if ~poolWasOpen delete(gcp); end