Этот пример показывает сравнение четырех методов, которые могут использоваться, чтобы ускорить симуляции частоты ошибок по битам (BER) с помощью Системных объектов в программном обеспечении MATLAB® Communications Toolbox™. Маленькая система, на основе сверточного кодирования, иллюстрирует эффект генерации кода, использующей продукт MATLAB® Coder™, выполнение параллельного цикла с помощью parfor
в продукте Parallel Computing Toolbox™, комбинации генерации кода и parfor
, и основанные на графическом процессоре Системные объекты.
Системные объекты этот пример функции доступны в продукте Communications Toolbox. Для того, чтобы запустить этот пример, у вас должны быть лицензия MATLAB Coder, лицензия Parallel Computing Toolbox и достаточный графический процессор.
Этот пример использует простую сверточную систему кодирования, чтобы проиллюстрировать ускоряющие стратегии симуляции. Система генерирует случайные биты сообщения с помощью randi
. Передатчик кодирует эти биты с помощью уровня 1/2 сверточный энкодер, применяет схему модуляции QPSK, и затем передает символы. Символы проходят через канал AWGN, где повреждение сигнала происходит. Демодуляция QPSK происходит в приемнике, и поврежденные биты декодируются с помощью алгоритма Viterbi. Наконец, частота ошибок по битам вычисляется. Системные объекты, используемые в этой системе:
comm.ConvolutionalEncoder - сверточное кодирование
comm.PSKModulator - Модуляция QPSK
comm.AWGNChannel - Канал AWGN
comm.PSKDemodulator - Демодуляция QPSK (приблизительно LLR)
comm.ViterbiDecoder - Декодирование Viterbi
Код для приемопередатчиков может быть найден в:
Каждая точка вдоль кривой частоты ошибок по битам представляет результат многих итераций кода приемопередатчика, описанного выше. Чтобы получить точные результаты за разумное количество времени, симуляция соберет по крайней мере 200 битовых ошибок на значение отношения сигнал-шум (SNR), и самое большее 5 000 пакетов данных. Пакет представляет 2 000 битов сообщения. ОСШ лежит в диапазоне от 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 Coder, чтобы сгенерировать MEX-функцию для симуляции
Используйте parfor, чтобы запустить параллельную симуляцию частоты ошибок по битам
Объедините сгенерированную MEX-функцию с parfor
Используйте основанные на графическом процессоре Системные объекты
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 Coder, файл MEX может быть сгенерирован с оптимизированным кодом С, который совпадает с предварительно скомпилированным кодом 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');
Используя parfor
, MATLAB выполняет код приемопередатчика против всех значений ОСШ параллельно. Это требует открытия параллельного пула и добавления 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
функциональное использование доступно для выполнения на графическом процессоре. Основанные на графическом процессоре версии:
comm.gpu.ConvolutionalEncoder - сверточное кодирование
comm.gpu.PSKModulator - Модуляция QPSK
comm.gpu.AWGNChannel - Канал AWGN
comm.gpu.PSKDemodulator - Демодуляция QPSK (приблизительно LLR)
comm.gpu.ViterbiDecoder - Декодирование Viterbi
Графический процессор является самым эффективным при обработке больших количеств данных целиком. Основанные на графическом процессоре Системные объекты могут обрабатывать несколько кадров в одном вызове метода шага. 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
Сравнивая результаты этих испытаний, ясно, что графический процессор значительно быстрее, чем какой-либо другой ускоряющий метод симуляции. Это повышение эффективности требует очень скромного изменения в коде симуляции. Однако нет никакой потери в эффективности частоты ошибок по битам, когда следующий график иллюстрирует. Очень незначительными различиями в кривых является результат различных алгоритмов генерации случайных чисел и/или эффекты усреднения различных количеств данных для той же точки на кривой.
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