Вплотную ускорение системной симуляции Используя графические процессоры

Этот пример показывает сравнение четырех методов, которые могут использоваться, чтобы ускорить симуляции частоты ошибок по битам (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

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

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

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

Используя 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');

Parfor и Code Generation

Можно объединить последние два метода для дополнительного ускорения. Скомпилированная 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