Этот пример показывает, как преобразовать алгоритм с плавающей точкой в фиксированную точку и затем сгенерировать код С для алгоритма. Пример использует применяющие лучшые методы:
Разделите свой алгоритм от тестового файла.
Подготовьте свой алгоритм к инструментированию и генерации кода.
Справьтесь с ростом бита управления и типами данных.
Отдельные определения типов от алгоритмического кода путем составления таблицы определений данных.
Для полного списка лучшых практик смотрите Ручные Лучшые практики Преобразования Фиксированной точки.
Запишите функцию MATLAB®, mysum
, который суммирует элементы вектора.
function y = mysum(x) y = 0; for n = 1:length(x) y = y + x(n); end end
Поскольку только необходимо преобразовать алгоритмический фрагмент в фиксированную точку, более эффективно структурировать код так, чтобы алгоритм, в котором вы делаете базовую обработку, был отдельным от тестового файла.
В тестовом файле создайте свои входные параметры, вызовите алгоритм и постройте результаты.
Запишите скрипт MATLAB, mysum_test
, который проверяет поведение вашего алгоритма с помощью двойных типов данных.
n = 10; rng default x = 2*rand(n,1)-1; % Algorithm y = mysum(x); % Verify results y_expected = sum(double(x)); err = double(y) - y_expected
rng default
помещает настройки генератора случайных чисел, используемого функцией rand к ее значению по умолчанию так, чтобы это произвело те же случайные числа, как будто вы перезапустили MATLAB.
Запустите тестовый скрипт.
mysum_test
err = 0
Результаты, полученные с помощью mysum
, совпадают с теми полученное использование функции sum
MATLAB.
Для получения дополнительной информации смотрите, Создают Тестовый файл.
В вашем алгоритме, после функциональной подписи, добавляет директива компиляции %#codegen
, чтобы указать, что вы намереваетесь оснастить алгоритм и сгенерировать код С для него. Добавление этой директивы дает анализатору кода MATLAB команду помогать вам диагностировать и зафиксировать нарушения, которые привели бы к ошибкам во время инструментирования и генерации кода.
function y = mysum(x) %#codegen y = 0; for n = 1:length(x) y = y + x(n); end end
Для этого алгоритма индикатор анализатора кода в правом верхнем углу окна редактора остается зеленое сообщение вам, что это не обнаружило проблем.
Для получения дополнительной информации смотрите, Готовят Ваш Алгоритм к Ускорению Кода или Генерации кода.
Сгенерируйте код С для исходного алгоритма, чтобы проверить, что алгоритм подходит для генерации кода и видеть код С с плавающей точкой. Используйте функцию codegen
(требует MATLAB Coder™) сгенерировать библиотеку C.
Добавьте следующую строку в конец своего тестового скрипта, чтобы сгенерировать код С для mysum
.
codegen mysum -args {x} -config:lib -report
Запустите тестовый скрипт снова.
MATLAB Coder генерирует код С для функции mysum
и обеспечивает ссылку на отчет генерации кода.
Щелкните по ссылке, чтобы открыть отчет генерации кода и просмотреть сгенерированный код C для mysum
.
/* Function Definitions */ double mysum(const double x[10]) { double y; int n; y = 0.0; for (n = 0; n < 10; n++) { y += x[n]; } return y; }
Поскольку C не позволяет индексы с плавающей точкой, счетчик цикла, n
, автоматически объявляется как целочисленный тип. Вы не должны преобразовывать n
в фиксированную точку.
Введите x
, и вывод y
объявляются как дважды.
Протестируйте свой алгоритм с одиночными играми, чтобы проверять на несоответствия типов
Измените свой тестовый файл так, чтобы тип данных x
был одним.
n = 10; rng default x = single(2*rand(n,1)-1); % Algorithm y = mysum(x); % Verify results y_expected = sum(double(x)); err = double(y) - y_expected codegen mysum -args {x} -config:lib -report
Запустите тестовый скрипт снова.
mysum_test
err = -4.4703e-08 ??? This assignment writes a 'single' value into a 'double' type. Code generation does not support changing types through assignment. Check preceding assignments or input type specifications for type mismatches.
Сбои генерации кода, сообщая о неверном типе данных относительно строки y = y + x(n);
.
Чтобы просмотреть ошибку, откройте отчет.
В отчете, на строке y = y + x(n)
, отчет подсвечивает y
на левой стороне присвоения красного цвета, чтобы указать, что существует ошибка. Проблема - то, что y
объявляется как двойное, но присваивается синглу. y + x(n)
является суммой двойного и сингла, который является синглом. Если вы устанавливаете свой курсор на переменные и выражения в отчете, вы видите информацию об их типах. Здесь, вы видите, что выражение, y + x(n)
является синглом.
Чтобы зафиксировать несоответствие типов, обновите свой алгоритм, чтобы использовать преобразованное в нижний индекс присвоение за сумму элементов. Измените y = y + x(n)
на y(:) = y + x(n)
.
function y = mysum(x) %#codegen y = 0; for n = 1:length(x) y(:) = y + x(n); end end
Используя преобразованное в нижний индекс присвоение, вы также предотвращаете рост разрядности, который является поведением по умолчанию, когда вы добавляете числа фиксированной точки. Для получения дополнительной информации смотрите Рост разрядности. Предотвращение роста разрядности важно, потому что вы хотите поддержать свои фиксированные точки в вашем коде. Для получения дополнительной информации смотрите Рост разрядности Управления.
Регенерируйте код С и откройте отчет генерации кода. В коде С результат теперь брошен, чтобы удвоиться, чтобы разрешить несоответствие типов.
Используйте функцию buildInstrumentedMex
, чтобы оснастить ваш алгоритм для журналирования минимальных и максимальных значений всех именованных и промежуточных переменных. Используйте функцию showInstrumentationResults
, чтобы предложить типы данных с фиксированной точкой на основе этих регистрируемых значений. Позже, вы используете эти предложенные фиксированные точки, чтобы протестировать ваш алгоритм.
Обновите тестовый скрипт:
После того, как вы объявите n
, добавьте buildInstrumentedMex mySum —args {zeros(n,1)} -histogram
.
Возвратите x
, чтобы удвоиться. Замените x = single(2*rand(n,1)-1);
на x = 2*rand(n,1)-1;
Вместо того, чтобы вызвать исходный алгоритм, вызовите сгенерированную MEX-функцию. Измените y = mysum(x)
на y=mysum_mex(x)
.
После вызова MEX-функции добавьте showInstrumentationResults mysum_mex -defaultDT numerictype(1,16) -proposeFL
. Флаги -defaultDT numerictype(1,16) -proposeFL
указывают, что вы хотите предложить дробные длины для 16-битного размера слова.
Вот обновленный тестовый скрипт.
%% Build instrumented mex n = 10; buildInstrumentedMex mysum -args {zeros(n,1)} -histogram %% Test inputs rng default x = 2*rand(n,1)-1; % Algorithm y = mysum_mex(x); % Verify results showInstrumentationResults mysum_mex ... -defaultDT numerictype(1,16) -proposeFL y_expected = sum(double(x)); err = double(y) - y_expected %% Generate C code codegen mysum -args {x} -config:lib -report
Запустите тестовый скрипт снова.
Функция showInstrumentationResults
предлагает типы данных и открывает отчет отобразить результаты.
В отчете кликните по вкладке Variables. showInstrumentationResults
предлагает дробную длину 13 для y
и 15 для x
.
В отчете вы можете:
Просмотрите симуляцию минимальные и максимальные значения для входа x
и вывода y
.
Просмотрите предложенные типы данных для x
и y
.
Просмотрите информацию для всех переменных, промежуточных результатов и выражений в вашем коде.
Чтобы просмотреть эту информацию, установите свой курсор на переменную или выражение в отчете.
Просмотрите данные о гистограмме для x
и y
, чтобы помочь вам идентифицировать любые значения, которые являются внешней областью значений или ниже точности на основе текущего типа данных.
Чтобы просмотреть гистограмму для конкретной переменной, кликните по ее иконке гистограммы.
Вместо того, чтобы вручную изменять алгоритм, чтобы исследовать поведение на каждый тип данных, разделите определения типов от алгоритма.
Измените mysum
так, чтобы он взял входной параметр, T
, который является структурой, которая задает типы данных входных и выходных данных. Когда y
будет сначала задан, используйте функцию cast
как синтаксис — cast(x,'like',y)
— чтобы бросить x
к желаемому типу данных.
function y = mysum(x,T) %#codegen y = cast(0,'like',T.y); for n = 1:length(x) y(:) = y + x(n); end end
Запишите функцию, mytypes
, который задает различные типы данных, которые вы хотите использовать, чтобы протестировать ваш алгоритм. В вашей таблице типов данных включайте двойные, один, и масштабируемые двойные типы данных, а также типы данных с фиксированной точкой, предложенные ранее. Прежде, чем преобразовать ваш алгоритм в фиксированную точку, это - хорошая практика к:
Протестируйте связь между таблицей определения типов, и ваше использование алгоритма удваивается.
Протестируйте алгоритм с одиночными играми, чтобы найти неверные типы данных и другие проблемы.
Запуститесь масштабируемое использование алгоритма удваивается, чтобы проверять на переполнение.
function T = mytypes(dt) switch dt case 'double' T.x = double([]); T.y = double([]); case 'single' T.x = single([]); T.y = single([]); case 'fixed' T.x = fi([],true,16,15); T.y = fi([],true,16,13); case 'scaled' T.x = fi([],true,16,15,... 'DataType','ScaledDouble'); T.y = fi([],true,16,13,... 'DataType','ScaledDouble'); end end
Для получения дополнительной информации смотрите Отдельные Определения типов из Алгоритма.
Обновите тестовый скрипт, mysum_test
, чтобы использовать таблицу типов.
Для первого показа проверяйте, что связь между использованием таблицы и алгоритма удваивается. Прежде чем вы объявите n
, добавьте T = mytypes('double');
Обновите вызов buildInstrumentedMex
, чтобы использовать тип T.x
, заданного в таблице типов данных: buildInstrumentedMex mysum -args {zeros(n,1,'like',T.x),T} -histogram
Бросьте x
, чтобы использовать тип T.x
, заданного в таблице: x = cast(2*rand(n,1)-1,'like',T.x);
Вызовите передачу MEX-функции в T
: y = mysum_mex(x,T);
Вызовите передачу codegen
в T
: codegen mysum -args {x,T} -config:lib -report
Вот обновленный тестовый скрипт.
%% Build instrumented mex T = mytypes('double'); n = 10; buildInstrumentedMex mysum ... -args {zeros(n,1,'like',T.x),T} -histogram %% Test inputs rng default x = cast(2*rand(n,1)-1,'like',T.x); % Algorithm y = mysum_mex(x,T); % Verify results showInstrumentationResults mysum_mex ... -defaultDT numerictype(1,16) -proposeFL y_expected = sum(double(x)); err = double(y) - y_expected %% Generate C code codegen mysum -args {x,T} -config:lib -report
Запустите тестовый скрипт и щелкните по ссылке, чтобы открыть отчет генерации кода.
Сгенерированный код C совпадает с кодом, сгенерированным для исходного алгоритма. Поскольку переменная T
используется, чтобы задать типы, и эти типы являются постоянными во время генерации кода; T
не используется во время выполнения и не появляется в сгенерированном коде.
Обновите тестовый скрипт, чтобы использовать фиксированные точки, предложенные ранее и просмотреть сгенерированный код C.
Обновите тестовый скрипт, чтобы использовать фиксированные точки. Замените T = mytypes('double');
на T = mytypes('fixed');
и затем сохраните скрипт.
Запустите тестовый скрипт и просмотрите сгенерированный код C.
Эта версия кода С не очень эффективна; это содержит большую обработку переполнения. Следующий шаг должен оптимизировать типы данных, чтобы избежать переполнения.
Масштабируемый удваивается, гибрид между числами и фиксированной точки с плавающей точкой. Fixed-Point Designer™ хранит их, как удваивается с масштабированием, знаком и сохраненной информацией о размере слова. Поскольку вся арифметика выполняется в с двойной точностью, вы видите любое переполнение, которое происходит.
Обновите тестовый скрипт, чтобы использовать масштабируемый, удваивается. Замените T = mytypes('fixed');
на T = mytypes('scaled');
Запустите тестовый скрипт снова.
Масштабируемое использование тестовых прогонов удваивает и отображает отчет. Никакое переполнение не обнаруживается.
До сих пор вы запустили тестовый скрипт с помощью случайных входных параметров, что означает, что маловероятно, что тест осуществил полный рабочий диапазон алгоритма.
Найдите полный спектр входа.
range(T.x)
-1.000000000000000 0.999969482421875 DataTypeMode: Fixed-point: binary point scaling Signedness: Signed WordLength: 16 FractionLength: 15
Обновите скрипт, чтобы протестировать отрицательный случай ребра. Запустите mysum_mex
с исходным случайным входом и с входом, который тестирует полный спектр, и агрегируйте результаты.
%% Build instrumented mex T = mytypes('scaled'); n = 10; buildInstrumentedMex mysum ... -args {zeros(n,1,'like',T.x),T} -histogram %% Test inputs rng default x = cast(2*rand(n,1)-1,'like',T.x); y = mysum_mex(x,T); % Run once with this set of inputs y_expected = sum(double(x)); err = double(y) - y_expected % Run again with this set of inputs. The logs will aggregate. x = -ones(n,1,'like',T.x); y = mysum_mex(x,T); y_expected = sum(double(x)); err = double(y) - y_expected % Verify results showInstrumentationResults mysum_mex ... -defaultDT numerictype(1,16) -proposeFL y_expected = sum(double(x)); err = double(y) - y_expected %% Generate C code codegen mysum -args {x,T} -config:lib -report
Запустите тестовый скрипт снова.
Тестовые прогоны и y
переполняют области значений типа данных с фиксированной точкой. showInstrumentationResults
предлагает новую дробную длину 11 для y
.
Обновите тестовый скрипт, чтобы использовать масштабируемый, удваивается с новым предложенным типом для y
. В myTypes.m
, для случая 'scaled'
, T.y = fi([],true,16,11,'DataType','ScaledDouble')
Повторно выполните тестовый скрипт.
Нет теперь никакого переполнения.
Обновите таблицу типов данных, чтобы использовать предложенную фиксированную точку и сгенерировать код.
В myTypes.m
, для случая 'fixed'
, T.y = fi([],true,16,11)
Обновите тестовый скрипт, mysum_test
, чтобы использовать T = mytypes('fixed');
Запустите тестовый скрипт и затем щелкните по ссылке Отчета Представления, чтобы просмотреть сгенерированный код C.
short mysum(const short x[10]) { short y; int n; int i0; int i1; int i2; int i3; y = 0; for (n = 0; n < 10; n++) { i0 = y << 4; i1 = x[n]; if ((i0 & 1048576) != 0) { i2 = i0 | -1048576; } else { i2 = i0 & 1048575; } if ((i1 & 1048576) != 0) { i3 = i1 | -1048576; } else { i3 = i1 & 1048575; } i0 = i2 + i3; if ((i0 & 1048576) != 0) { i0 |= -1048576; } else { i0 &= 1048575; } i0 = (i0 + 8) >> 4; if (i0 > 32767) { i0 = 32767; } else { if (i0 < -32768) { i0 = -32768; } } y = (short)i0; } return y; }
По умолчанию арифметика fi
использует насыщение на переполнении и самом близком округлении, которое приводит к неэффективному коду.
Чтобы сделать сгенерированный код более эффективным, используйте математические настройки (fimath
) фиксированной точки, которые более подходят для генерации кода C: перенеситесь на округлении пола и переполнении.
В myTypes.m
добавьте случай 'fixed2'
:
case 'fixed2' F = fimath('RoundingMethod', 'Floor', ... 'OverflowAction', 'Wrap', ... 'ProductMode', 'FullPrecision', ... 'SumMode', 'KeepLSB', ... 'SumWordLength', 32, ... 'CastBeforeSum', true); T.x = fi([],true,16,15,F); T.y = fi([],true,16,11,F);
Вместо того, чтобы вручную ввести свойства fimath
, можно использовать редактора MATLAB опция Insert fimath. Для получения дополнительной информации смотрите Создание fimath Конструкторы Object в графический интерфейсе пользователя.
Обновите тестовый скрипт, чтобы использовать 'fixed2'
, запустить скрипт, и затем просмотреть сгенерированный код C.
short mysum(const short x[10]) { short y; int n; y = 0; for (n = 0; n < 10; n++) { y = (short)(((y << 4) + x[n]) >> 4); } return y; }
Сгенерированный код более эффективен, но y
переключен, чтобы выровняться с x
и теряет 4 бита точности.
Чтобы зафиксировать эту потерю точности, обновите размер слова y
к 32 битам и сохраните 15 битов точности, чтобы выровняться с x
.
В myTypes.m
добавьте случай 'fixed32'
:
case 'fixed32' F = fimath('RoundingMethod', 'Floor', ... 'OverflowAction', 'Wrap', ... 'ProductMode', 'FullPrecision', ... 'SumMode', 'KeepLSB', ... 'SumWordLength', 32, ... 'CastBeforeSum', true); T.x = fi([],true,16,15,F); T.y = fi([],true,32,15,F);
Обновите тестовый скрипт, чтобы использовать 'fixed32'
и запустить скрипт, чтобы сгенерировать код снова.
Теперь, сгенерированный код очень эффективен.
int mysum(const short x[10]) { int y; int n; y = 0; for (n = 0; n < 10; n++) { y += x[n]; } return y; }
Для получения дополнительной информации смотрите, Оптимизируют Ваш Алгоритм.