|
Лишние NOP в ассемблерном коде |
|
|
|
Oct 23 2007, 12:04
|

Эксперт
    
Группа: Свой
Сообщений: 1 467
Регистрация: 25-06-04
Пользователь №: 183

|
Цитата(Degun @ Oct 23 2007, 15:54)  Code Composer Studio v 3.3; Процессор DM642 на evalution board. Имеется программка, написанная на C++. После анализа её ассемблерного кода выяснилось, что после многих команд (LDW, ADD, CMPLT, AND, LDHU и др.) компилятор вставляет команду NOP, что естественно замедляет функционирование программы. Для чего это? Можно ли отключить вставку операторов NOP в ассемблерный код? Это не ассемблерный код, а выход С-компилятора. У TI такой подход, что программист (или компилятор) должен следить за длительностью выполнения инструкций (latency). Например, если умножение требует 5 тактов, то нужно пять тактов и ждать, в противном случае Вы выхватываете из конвейера неверный результат (что, однако, никто Вам запретить не может). Вот компилятор и борется с "открытым конвейером" Есть единственный гарантированый способ избежать вставки NOP в ассемблерный код - написать самому на ассемблере, так чтобы без NOPов ;-) TMS серии 6х - это ведь кубик Рубика
|
|
|
|
|
Oct 23 2007, 12:15
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Цитата(fontp @ Oct 23 2007, 16:04)  Это не ассемблерный код, а выход С-компилятора. А разве это не одно и тоже? Цитата(fontp @ Oct 23 2007, 16:04)  У TI такой подход, что программист (или компилятор) должен следить за длительностью выполнения инструкций (latency). Например, если умножение требует 5 тактов, то нужно пять тактов и ждать, в противном случае Вы выхватываете из конвейера неверный результат (что, однако, никто Вам запретить не может). Вот компилятор и борется с "открытым конвейером" Есть единственный гарантированый способ избежать вставки NOP в ассемблерный код - написать самому на ассемблере, так чтобы без NOPов ;-) TMS серии 6х - это ведь кубик Рубика Не совсем понятно. Если умножению, например, требуется 5 тактов, то соответствующая команда и будет выполняться необходимое кол-во тактов и только после после этого перейдёт к выполнению следующей. Или переход к выполнению следующей команды осуществляется сразу, а результат умножения будет готов только через 5 тактов?
|
|
|
|
|
Oct 23 2007, 12:28
|

Эксперт
    
Группа: Свой
Сообщений: 1 467
Регистрация: 25-06-04
Пользователь №: 183

|
Цитата(Degun @ Oct 23 2007, 16:15)  А разве это не одно и тоже?
Не совсем понятно. Если умножению, например, требуется 5 тактов, то соответствующая команда и будет выполняться необходимое кол-во тактов и только после после этого перейдёт к выполнению следующей. Или переход к выполнению следующей команды осуществляется сразу, а результат умножения будет готов только через 5 тактов? Навроде того. Как захотите так и будет. Вставите 4 NOP дождётесь правильного результата. Не вставите - никто Вам мешать не станет, процессор не станет ждать. Но результат будет неверный. Ваше личное дело. Програмировать открытый конвейер - это как оперировать на открытом мозге Выход кодогенератора - формально тоже ассемблерный код, но не сильно осмысленый Осмысленный ассемблерный код пишется программистом
|
|
|
|
|
Oct 26 2007, 12:06
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Что можно сделать или оптимизировать в функции ниже в плане генерации оптимального кода для DSP-процессора? Код int SplitImage2Areas(unsigned char **Image, int **AreasNumb, int *Areas2Mode, int Width, int Height, int Thres1, int Thres2) { if ((Image==0)||(Width<=0)||(Height<=0)||(AreasNumb==0)) return -1;
int iPrevAreaNumbL=-1; //Номер области предыдущей левой точки int iPrevModeNumbL=-1; //Номер моды предыдущей левой точки int iPrevAreaNumbU=-1; //Номер области предыдущей верхней точки int iPrevModeNumbU=-1; //Номер моды предыдущей верхней точки int iPrevAreaNumbUL=-1; //Номер области предыдущей верхней левой точки int iPrevModeNumbUL=-1; //Номер моды предыдущей верхней левой точки int iPrevAreaNumbUR=-1; //Номер области предыдущей верхней правой точки int iPrevModeNumbUR=-1; //Номер моды предыдущей верхней правой точки int iAreasCount=0; //Счётчик кол-ва областей int i, j, y, x, iNumb[4], iNumbCount, iDelta, iTmp; int iCurrPixel, iCurrAreaNumb, iCurrModeNumb; //Изображение последовательно просматривается по строкам //для отнесения каждого пиксела к определённой области for (y=0; y<Height; y++) for (x=0; x<Width; x++) { //Значение яркости текущего пикселя iCurrPixel=Image[y][x]; //Номер области текущей точки iCurrAreaNumb=-1; //Номер моды текущей точки iCurrModeNumb=(iCurrPixel<Thres1?0:(iCurrPixel>Thres2?1:-1)); //Определение параметров верхней правой точки if ((y>0)&&(x<(Width-1))) { iPrevAreaNumbUR=AreasNumb[y-1][x+1]; iPrevModeNumbUR=Areas2Mode[iPrevAreaNumbUR]; } //В зависимости от того является ли точка переходной if (iCurrModeNumb>=0) { //Эта точка не является переходной ни к одной из мод //Просмотр всех соседних уже размеченных ранее точек //для возможного определения номера области текущей точки if ((x>0)&&(iCurrModeNumb==iPrevModeNumbL)) { iCurrAreaNumb=iPrevAreaNumbL; //Левая точка } else if (y>0) { //Верхняя строка if (iCurrModeNumb==iPrevModeNumbU) { iCurrAreaNumb=iPrevAreaNumbU; //Верхняя точка } else if ((x>0)&&(iCurrModeNumb==iPrevModeNumbUL)) { iCurrAreaNumb=iPrevAreaNumbUL; //Верхняя левая точка } else if ((x<(Width-1))&&(iCurrModeNumb==iPrevModeNumbUR)) { iCurrAreaNumb=iPrevAreaNumbUR; //Верхняя правая точка } } } else { //Текущая точка является переходной к любой области //Выбор наиболее близкой области iDelta=iTmp=-1; if (x>0) { //Левая точка iCurrAreaNumb=iPrevAreaNumbL; iDelta=abs(Image[y][x-1]-iCurrPixel); } if (y>0) { //Верхняя строка //Верхняя точка iTmp=abs(Image[y-1][x]-iCurrPixel); if ((iDelta<0)||(iDelta>iTmp)) { iCurrAreaNumb=iPrevAreaNumbU; iDelta=iTmp; } if (x>0) { //Верхняя левая точка iTmp=abs(Image[y-1][x-1]-iCurrPixel); if (iDelta>iTmp) { iCurrAreaNumb=iPrevAreaNumbUL; iDelta=iTmp; } } if (x<(Width-1)) { //Верхняя правая точка iTmp=abs(Image[y-1][x+1]-iCurrPixel); if (iDelta>iTmp) { iCurrAreaNumb=iPrevAreaNumbUR; iDelta=iTmp; } } } //Проверка на верхнюю левую точку всего изображения if (iCurrAreaNumb>=0) iCurrModeNumb=Areas2Mode[iCurrAreaNumb]; else //Нужно добавить новую область с нулевой модой iCurrModeNumb=0; } //Если это новая область if (iCurrAreaNumb<0) { //Добавление нового объекта в коллекцию iCurrAreaNumb=iAreasCount++; Areas2Mode[iCurrAreaNumb]=iCurrModeNumb; } //Установка номера области текущей точки AreasNumb[y][x] = iCurrAreaNumb; //Сканирование окрестных точек для выяснения граничащих областей iNumbCount=0; if ((x==0)||(iPrevAreaNumbL!=iCurrAreaNumb)) { //Просмотр левой точки if ((x>0)&&(iCurrModeNumb==iPrevModeNumbL)&&(iCurrAreaNumb!=iPrevAreaNumbL)) iNumb[iNumbCount++]=iPrevAreaNumbL; //Просмотр верхней левой точки if ((y>0)&&(x>0)&&(iCurrModeNumb==iPrevModeNumbUL)&&(iCurrAreaNumb!=iPrevAreaNumbUL)) iNumb[iNumbCount++]=iPrevAreaNumbUL; //Просмотр верхней точки if ((y>0)&&(iCurrModeNumb==iPrevModeNumbU)&&(iCurrAreaNumb!=iPrevAreaNumbU)) iNumb[iNumbCount++]=iPrevAreaNumbU; } //Сканирование верхней правой точки (в любом случае независимо от iPrevAreaNumb!!!) if ((y>0)&&(x<(Width-1))&&(iCurrModeNumb==iPrevModeNumbUR)&&(iCurrAreaNumb!=iPrevAreaNumbUR)) iNumb[iNumbCount++]=iPrevAreaNumbUR; //Добавление найденных граничных областей for (i=0; i<iNumbCount; i++) { iTmp=iNumb[i]; bool bFind=false; for (j=0; j<i; j++) if (iNumb[j]==iTmp) { bFind=true; break; } if (!bFind) { //Здесь области добавляются к списку соседних областей } } //Запоминание номера области и моды для левой точки iPrevAreaNumbL=iCurrAreaNumb; iPrevModeNumbL=iCurrModeNumb; //Запоминание номера области и моды для верхней левой точки iPrevAreaNumbUL=iPrevAreaNumbU; iPrevModeNumbUL=iPrevModeNumbU; //Запоминание номера области и моды для верхней точки iPrevAreaNumbU=iPrevAreaNumbUR; iPrevModeNumbU=iPrevModeNumbUR; }
return iAreasCount; }
Сообщение отредактировал Degun - Oct 26 2007, 12:11
|
|
|
|
|
Oct 31 2007, 06:29
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Цитата(Edmundo @ Oct 30 2007, 23:32)  Куча ветвлений в виде if-ов -- не есть истинная обработка сигналов. А посему, не будет работа DSP воистину эффективна. Для этого можно и 8051 или ARM поставить. Не для этого рождены Обработчики Сигналов... Если цифровую обработку сводить только к линейной фильтрации и линейным алгоритмам вообще, то такую обработку вообще с лёгкостью можно и в ПЛИС запихнуть. Но настоящая интеллектуальность алгоритмов начинает проявляться при их нелинейности, что предполагает широкое использование if-ов. Поэтому на DSP-процессоры хотелось бы возложить именно такие более сложные алгоритмы. Да и та функция, которую я привёл не настолько сложна, чтобы сильно загружать процессор: она целочисленна; в ней нет никаких математических вычислений. Я бы даже сказал так: если уж он эту функцию не может выполнять быстро, то вообще что же он тогда может.
|
|
|
|
|
Oct 31 2007, 08:42
|

Эксперт
    
Группа: Свой
Сообщений: 1 467
Регистрация: 25-06-04
Пользователь №: 183

|
Цитата(Degun @ Oct 31 2007, 09:29)  Если цифровую обработку сводить только к линейной фильтрации и линейным алгоритмам вообще, то такую обработку вообще с лёгкостью можно и в ПЛИС запихнуть. Но настоящая интеллектуальность алгоритмов начинает проявляться при их нелинейности, что предполагает широкое использование if-ов. Поэтому на DSP-процессоры хотелось бы возложить именно такие более сложные алгоритмы. Да и та функция, которую я привёл не настолько сложна, чтобы сильно загружать процессор: она целочисленна; в ней нет никаких математических вычислений. Я бы даже сказал так: если уж он эту функцию не может выполнять быстро, то вообще что же он тогда может. Ну, он там может всякую фильтрацию, всякие преобразоввания, интерполяцию, экстраполяцию. Если алгоритм состоит из одних if-ов то вам нужен процессор без конвейера. Кроме того сигнальный или мультимедийный алгоритм часто можно перепроектировать (не переписать) так, что if-ов не останется, или почти не останется. Например, у DSP для нахождения экстремумов есть специальные ассемблерные инструкции и на них надо выходить или программируя на ассемблере, или с помощью интринсиков. Есть и другие хитроумные специализированые инструкции о которых стандартный С ничего не знает. Но есть интринсики ассемблерные библиотечные функции? котрые их пользуют. Все современные DSP имеют хороший длинный конвейер и алгоритмы для них должны состоять из коротких циклов с не более одного if внутри, чтобы выполняться эффективно. А в более старых DSP не было этой напасти, зато в них было так мало регистров, что всё приходилось программировать на ассемблере, на С ничего не писалось Понятно, что это трудно объяснить своему менеджеру, который купил Вам самый дорогой процессор (10 млрд. МИПС :-)) на все случаи жизни. Менеджер - это ведь тот человек, который понятия не имеет, где он находится, но совершенно точно знает, кто виноват :-) По поводу Вашего предыдущего вопроса - сам он ничего не будет загружать. Компилятор будет делать попытки, но ... оптимизирующий компилятор должен узнать один из известных ему шаблонов, чтобы сгенерировать эффективный код. C FIR фильтром он разберётся лучше программиста, а так не очень... Что ему программист скажет загрузить (с помощью ассемблера или интринсиков) - то он примерно и загрузит
|
|
|
|
|
Oct 31 2007, 19:50
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Цитата(fontp @ Oct 31 2007, 11:42)  Ну, он там может всякую фильтрацию, всякие преобразоввания, интерполяцию, экстраполяцию. Если алгоритм состоит из одних if-ов то вам нужен процессор без конвейера. Конечно не из одних, но их достаточное кол-во. Типовые задачи цифровой обработки сигналов также присутствуют. Вообще с трудом представляю себе алгоритмы без if-ов. Например, сравнение одной переменной с другой и в зависимости от результата ветвление - это if. А уж без этого трудно представить большинство алгоритмов. Цитата(fontp @ Oct 31 2007, 11:42)  Понятно, что это трудно объяснить своему менеджеру, который купил Вам самый дорогой процессор (10 млрд. МИПС :-)) на все случаи жизни. На самом деле производительность всего 4800 МИПС. Теоретически. Но чтобы их вытянуть у процессора на языке C\C++ нужно приложить массу усилий или перейти на ассемблер. Причём тип процессора был выбран вполне обосновано и претензий к этому у меня нет. Как показывает практика такой производительности процессора не хватает для обработки кадров с разрешением 768*576 с частотой 50 Гц по приведённому выше алгоритму, т. к. эта функция для указанного разрешения выполняется порядка 1 сек! Цитата(fontp @ Oct 31 2007, 11:42)  По поводу Вашего предыдущего вопроса - сам он ничего не будет загружать. Компилятор будет делать попытки, но ... оптимизирующий компилятор должен узнать один из известных ему шаблонов, чтобы сгенерировать эффективный код. C FIR фильтром он разберётся лучше программиста, а так не очень... Что ему программист скажет загрузить (с помощью ассемблера или интринсиков) - то он примерно и загрузит А есть ли документ где можно посмотреть типовые шаблоны алгоритмов?
Сообщение отредактировал Degun - Oct 31 2007, 19:52
|
|
|
|
|
Oct 31 2007, 20:20
|

Эксперт
    
Группа: Свой
Сообщений: 1 467
Регистрация: 25-06-04
Пользователь №: 183

|
Цитата(Degun @ Oct 31 2007, 22:50)  А есть ли документ где можно посмотреть типовые шаблоны алгоритмов? SPRU187 - TMS320C6000 Optimizing Compiler User's Guide SPRU198- TMS320C6000 Programmer's Guide SPRU565-TMS320C64x DSP Library Programmer's Reference SPRU023-TMS320C64x Image/Video Processing Library Programmer’s Reference и т.д. они все есть у Вас в 3-м композере C:\CCStudio_v3.1\docs\pdf\manuals_ccs_full_c6000.html В первой из книг рассказывается как поменять мозги и программировать в стиле шестьдесятого TMS конвейеры. Это действительно в каком-то обобщённом смысле больше похоже на проектирование конвейеров ПЛИС , а не обычное последовательное программирование Другие - описания библиотек. Есть же и исходники если интересно Приёмчики, которыми пользуется оптимизатор, вряд ли где-то описаны. С опытом почувствуете Между прочим pentium-мы на чисто логических задачах - тоже страшный тормоз, например на шахматных программах. И причины те же. Это всё процессоры заточеные под потоковые задачи Короче, нужно или перепроектировать алгоритм (для вычислительных алгоритмов это возможно почти всегда), либо если задача типа перебора на дереве решений с нелинейными метриками - не рассчитывать на быстродействие 500<мгц>х8<число конвейеров> мипс, а рассчитывать скорее на быстродействие 500мгц/ <глубина конвейера>. В вашем случае 50-100 мипс. Последний факт, понятно, аккуратно умалчивается не только в рекламных листовках, но и в документации на процессор. Харизма не та, чтобы прямо сказать: "Вот такие мы, Тексас (Аналог, Интел), мудАки" :-)
|
|
|
|
|
Nov 7 2007, 19:59
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Цитата(fontp @ Oct 31 2007, 23:20)  Короче, нужно или перепроектировать алгоритм (для вычислительных алгоритмов это возможно почти всегда), либо если задача типа перебора на дереве решений с нелинейными метриками - не рассчитывать на быстродействие 500<мгц>х8<число конвейеров> мипс, а рассчитывать скорее на быстродействие
500мгц/ <глубина конвейера>. В вашем случае 50-100 мипс. Последний факт, понятно, аккуратно умалчивается не только в рекламных листовках, но и в документации на процессор. Харизма не та, чтобы прямо сказать: "Вот такие мы, Тексас (Аналог, Интел), мудАки" :-) Что-то я запутался в конвейерах и в глубине конвейера для DM642. В документации по нему сказано, что у него 8 параллельно работающих устройств, выполняющих команды. Т. е. если повезёт, то за один такт может выполниться параллельно до 8-ми команд. Значит 8 - это количество конвейеров или это глубина всего одного конвейера?
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|