Ускорение

Системной симуляции конца и конца с использованием графических процессоров

Этот пример показывает сравнение четырех методов, которые могут использоваться для ускорения симуляций вероятности битовой ошибки (BER) с использованием системных объектов в программном обеспечении MATLAB ® Communications Toolbox™. Небольшая система, основанная на сверточном кодировании, иллюстрирует эффект генерации кода с помощью Coder™ продукта MATLAB ®, выполнения параллельного цикла с использованием 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 битовых ошибок на значение отношения сигнал/шум (ОСШ) и самое большее 5000 пакетов данных. Пакет представляет 2000 биты сообщений. ОСШ находится в областях значений от 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

Рабочий процесс

Рабочий процесс для этого примера:

  1. Запустите симуляцию базовой линии системных объектов

  2. Используйте MATLAB Coder, чтобы сгенерировать MEX-функцию для симуляции

  3. Используйте parfor, чтобы запустить параллельную симуляцию частоты битовой ошибки

  4. Объедините сгенерированную MEX-функцию с parfor

  5. Использование системных объектов на базе графического процессора

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 - Выполнение параллельного цикла

Использование parforMATLAB выполняет код приемопередатчика против всех значений ОСШ параллельно. Это требует открытия параллельного пула и добавления 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');

Генерация 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:

  • comm.gpu.ConvolutionalEncoder - сверточное кодирование

  • comm.pu.PSKModulator - QPSK модуляция

  • comm.gpu.AWGNChannel - канал AWGN

  • comm.pu.PSKDemodulator - демодуляция QPSK (приблизительно LLR)

  • comm.pu.ViterbiDecoder - декодирование Viterbi

Графический процессор наиболее эффективен при обработке больших количеств данных сразу. Системные объекты на базе GPU могут обрабатывать несколько системы координат за один вызов метода шага. The 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