- •1. Принципы и базовые алгоритмы обработки сигналов в цифровых системах. Требования к аппаратной базе для выполнения базовых операций цос.
- •3. Форматы представления данных в сигнальных процессорах. Сравнительные особенности процессоров, ориентированных на обработку фз- и пз-данных. Оценка необходимой разрядности процессора.
- •1. Данные с фиксированной запятой (Fixed-Point Numbers)
- •2. Данные с плавающей запятой (Floating-Point Numbers)
- •4. Этапы и средства разработки и отладки по для процессоров цос. Критерии выбора языка программирования и средств разработки по
- •6. Особенности программной организации ввода-обработки-вывода данных в системах цос, работающих в реальном времени. Работа по прерываниям. Использование буферов.
- •7. Методики оптимизации кода для risc-процессоров
- •Inline- функции
- •8. Dsp семейства adsp-2106x. Основные характеристики и особенности архитектуры.
- •9. Карта памяти adsp-2106x. Пространства внутренней памяти, памяти многопроцессорной системы и внешней памяти. Пространство памяти iop-регистров.
- •10. Физическая организация внутренней памяти. Блоки памяти. Возможные виды конфликтов при обращении к внутренней памяти и способы их устранения. Адресация нормальных и коротких слов.
- •12. Доступ к памяти в программе. Организация dag. Основные возможности. Особенности программирования регистров dag. Кольцевые буферы.
- •13. Системные регистры: назначение и работа с ними. Устройство обмена между шинами (px-регистры).
- •14. Общие принципы работы вычислительных блоков adsp-21ххх. Особенности организации вычислений с пз- и фз-данными. Регистровый файл. Поддерживаемые типы данных и их идентификация в программе.
- •15. Теневые регистры. Регистры флагов. Понятие "липких" флагов. Флаги аккумулирующих сравнений.
- •16. Описание и функции алу. Флаги состояния и режимы работы алу. Мас. Функционирование мас. Особенности выполнения операций округления и насыщения при работе с фз-операндами. Флаги состояния мас.
- •17. Сдвигатель. Функционирование сдвигателя. Выделение и депонирование битовых полей. Флаги состояния сдвигателя. Организация многофункциональных вычислений. Ограничения.
- •19. Организация циклов. Ограничения при организации циклов. Циклы не по счетчику. Достоинства и недостатки аппаратной поддержки циклов.
- •20. Кэш инструкций. Структура и функционирование. Возможности управления обновлением кэша. Примеры повышения/понижения эффективности выполнения кода с учетом работы кэша.
- •21. Обработка прерываний: общая последовательность действий. Таблица прерываний. Маскирование прерываний. Регистры, программного секвенсора, используемые при обработке прерываний.
- •22. Отличия работы процессора при разрешенных и запрещенных вложенных прерываниях.
- •23. Организация работы с таймером. Программные прерывания. Внешние прерывания.
- •24. Понятие и назначение dma-пересылок в adsp-2106x. Направления пересылок. Каналы dma. Принципы функционирования dma-контроллера adsp-2106x. Генерация прерываний dma-контроллером.
- •26. Цепочечные dma-пересылки в adsp-2106x. Tcb-блоки. Инициирование и завершение цепочечных dma-пересылок. Контроль состояния цепочечной dma-пересылки
- •27. Порты sharc-процессоров. Реализуемые виды интерфейсов. Организация взаимодействия между процессорным ядром и портами при вводе/выводе данных.
- •29. Архитектура и принципы функционирования линк-портов (звеньевых портов) adsp-2106x. Регистры управления работой линк-портов adsp-2106x. Связь между линк-буферами и линк-портами.
- •30. Организация взаимодействия между процессорами по выделенным каналом через линк-порты. Регистр lsrq и его назначение. Идея алгоритма "передачи маркера".
- •31. Механизм арбитража внешней шины. Схемы приоритетов. Приоритетный доступ процессорного ядра.
- •32. Операции вещательной записи. Блокировка шины. Использование "зеркальных" семафоров. Межпроцессорные сообщения и вектор прерывания virpt.
- •33. Отличительные особенности компилятора cc21k. Поддерживаемые типы данных и их реализация.
- •2.1. Общие сведения
- •2.2. Поддерживаемые типы данных
- •34. Расширения языка с. Ассемблерные вставки – использование регистров. Поддержка пространств памяти данных памяти команд. Доступ к круговым буферам.
- •2.3.1. Поддержка inline-функций
- •2.3.2. Ассемблерные вставки
- •2.3.3. Поддержка пространств памяти данных и памяти команд
- •2.3.4. Доступ к круговым буферам
- •2.4.1. Использование памяти
- •2.4.2. Регистры компилятора
- •36. Работа со стеком при вызове процедур в рамках rte. Передача и возврат параметров. Взаимодействие программ на с и ассемблере
- •2.4.4. Передача параметров в функции и возврат значений
- •2.5. Взаимодействие подпрограмм на с и ассемблере
- •2.5.1. Описания имен и области их видимости
- •2.5.2. Макросы для работы со стеком при вызове и возврате из функций на ассемблере
- •2.5.3. Вызов подпрограммы на ассемблере из программы на с
- •2.5.4. Вызов подпрограммы на с из подпрограммы на ассемблере
- •37. Разработка обработчиков прерываний. Диспетчер прерываний. Классификация обработчиков прерываний по "быстродействию" и накладным расходам.
7. Методики оптимизации кода для risc-процессоров
Анализ алиасов
Предназначен для отслеживания обращения по указателю к той или иной переменной.
int *p;
int x,y;
…
y = 1;
*p = 10;
x = y;
Без выполнения анализа алиасов, отслеживающего адрес, на который указывает указатель p, компилятор не может знать, модифицирует ли команда *p=10 переменную y? Соответственно, он не может заменить последнюю инструкцию на более простую команду x=1 (для ее выполнения не нужно читать из памяти переменную y). Если выполненный анализ позволяет сделать вывод о том, что p не указывает на y, то компилятор заменяет последнюю инструкцию.
Передача параметров подпрограмм в регистрах.
В CISC-процессорах параметры в процедуры обычно передаются через программный. Наличие большого регистрового файла в большинстве RISC-процессоров позволяет передавать часть параметров через регистры. Такой подход обычно повышает производительность программы как при передаче параметров (не нужно обращаться к памяти, чтобы затолкнуть операнды в стек), так и при обращении к ним в вызванной подпрограмме (не нужно обращаться к памяти, чтобы прочитать операнды из стека; к тому же операции могут выполняться непосредственно над полученными регистрами).
Разворачивание циклов
Это способ оптимизации, при котором эффективность выполнения программы повышается за счет увеличения объема программного кода. Такой прием дает два преимущества:
- приводит к уменьшению числа проверок условия окончания цикла и выполнения условных переходов по сравнению с числом "полезных" команд тела цикла;
- увеличивает размер блока инструкций, доступных для локальной оптимизации путем переупорядочения потока команд.
for (j=0; j<n; j++)
x += x;
После разворачивания цикл будет выглядеть так:
for (j=0; j<n; j+=4)
{
x += x;
x += x;
x += x;
x += x;
}
Межпроцедурный анализ (Interprocedural Analysis, IPA)
Используется технология, похожая на анализ алиасов и позволяющая компилятору максимально "быть в курсе" того, какие переменные и регистры модифицируются в конкретном участке кода. Если анализ алиасов предоставляет сведения, касающиеся возможной адресации указателя на ту или иную область памяти, IPA анализирует возможность модификации регистра или переменной в результате вызова функции.
int a[10], b[10], c;
void bar()
{
c++;
}
void foo()
{
int i;
for (i=1; i<10; i++)
{
b[0] =b[0] + a[i];
bar();
} }
Результат компиляции цикла без IPA-оптимизации (на промежуточном языке):
…
r5 = 1
loop_start:
r6 = r5 * 2
load r1, b
load r2, a[r6]
r1 = r1 + r2
save b, r1
call bar
r5 = r5 + 1
cmp r5, 10
if less jump loop_start
…
Результат компиляции цикла с IPA-оптимизацией (на промежуточном языке):
…
load r1, b
r5 = 1
loop_start:
r6 = r5 * 2
load r2, a[r6]
r1 = r1 + r2
call bar
r5 = r5 + 1
cmp r5, 10
if less jump loop_start
save b, r1
…
Использование IPA-анализа позволило отказаться от постоянного сохранения значения b[0] в память, поскольку компилятор имеет информацию о том, что функция bar() использует только глобальную переменную c и не работает с b[0]. Это привело к повышению производительности программы и сокращению программного кода.
Оптимизации на уровне архитектуры процессора или машинных команд
Вариантов такой оптимизации может быть очень много, они определяются особенностями архитектуры процессоров и системы команд, но их общими основными направлениями являются:
- сокращение накладных расходов на условные переходы: отказ от специальных команд проверки условий (типа cmp в ассемблере Intel), анализ флагов и выполнение условных переходов непосредственно по результатам арифметических и логических команд; выполнение операций по условию вместо выполнения условных переходов;
- замена доступа к элементам массива по индексам на доступ по указателям;
- замена условий в циклах по счетчику. Если переменная цикла не используется в теле цикла в качестве операнда, то для проверки условия завершения цикла вместо кода с проверкой переменной на равенство заданной константе выполнять сравнение ее с нулем:
До машинной оптимизации на уровне команд |
После машинной оптимизации на уровне команд |
for (i=0; i<100; i++) a += random(); |
for (i=100; i>0; i--) a += random(); |
Реализация второго варианта цикла всегда будет более эффективна, поскольку не требуется отдельная команда для сравнения с нулем. Каким является значение счетчика цикла после декрементирования можно определить сразу же по флагам. Это типичный пример Code Selection-оптимизации, когда две инструкции проверки и перехода заменяются на одну;
Управление порядком выполнения инструкций.
Поскольку для реализации необходима информация о зависимостях между инструкциями, такая планировка выполняется после всех других видов оптимизации. Главная задача планирования потока команд заключается в обеспечении постоянной и непрерывной работы конвейера инструкций. Конвейеризация эффективна только тогда, когда загрузка конвейера близка к полной, а скорость подачи новых команд и операндов соответствует максимальной производительности конвейера. Если произойдет задержка, то параллельно будет выполняться меньшее количество операций и суммарная производительность снизится.
Оптимизация ветвлений
Одним из приемов оптимизации кода является организация сложной системы переходов для выполнения команд в теле цикла и/или выхода из цикла. Например, приведенный ниже программный код на языке С может быть преобразован компилятором следующим образом
Исходный код |
Код после оптимизации |
flag = 0; while (!flag) { ...aaa... stml (); ...bbb... if (e1) break; ...ccc... if (e2) { ...ddd... flag = 1 ; ...eee... } } if (flag) { ...ttt... stm2 (); } |
L1: ...ааа... stml (); ...bbb... if (e1) goto L2; /* через stm2,т.к. flag1=0 */ ...ccc... if (!e2) /* if !e2 в начало */ goto L1; ...ddd... ...eee... ...ttt... stm2 (); L2:
|
Компилятор определяет, что вход в цикл выполняется в любом случае, и поэтому функция stml() будет вызываться всегда. Компилятор также определяет, что stm2() никогда не будет выполнена, если flag=0 и если e1=1 – тогда flag "навсегда останется" равным "0". Эти сведения позволяют компилятору сгенерировать более эффективный код.
