|
|
  |
Лишние NOP в ассемблерном коде |
|
|
|
Nov 8 2007, 07:33
|

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

|
Цитата(Degun @ Nov 7 2007, 22:59)  Что-то я запутался в конвейерах и в глубине конвейера для DM642. В документации по нему сказано, что у него 8 параллельно работающих устройств, выполняющих команды. Т. е. если повезёт, то за один такт может выполниться параллельно до 8-ми команд. Значит 8 - это количество конвейеров или это глубина всего одного конвейера? Это количество конвейеров. Если постараться и повезёт будут выполняться 8 инструкций. Если не стараться или не повезёт, то конвейер (ы) не будет загружен и будет простаивать. У конвейера есть две временных характеристики - bandwidth и latency. Если удаётся подпихивать конвейеру данные на тактовой частоте - то быстродействие равно bandwith. Если не удаётся то можно заработать 1/latency Загрузить конвейер - это искусство, если задача не стандартная. И уж совершенно точно компилятор этим искусством не владеет
|
|
|
|
|
Nov 16 2007, 10:07
|

Частый гость
 
Группа: Свой
Сообщений: 86
Регистрация: 22-03-07
Из: Санкт-Петербург
Пользователь №: 26 406

|
Цитата(Degun @ Oct 26 2007, 16:06)  Что можно сделать или оптимизировать в функции ниже в плане генерации оптимального кода для DSP-процессора? Код int SplitImage2Areas(unsigned char **Image, int **AreasNumb, int *Areas2Mode, int Width, int Height, int Thres1, int Thres2) [поудалял // qxov] Я хотел бы попытался подсказать, как можно улучшить этот код, но для этого желательно на словах объяснить, что он должен делать - код не прозрачен и практически не читается, к сожалению. Подскажите, если задача не потеряла актуальность. Цитата(Edmundo @ Oct 24 2007, 16:36)  Вы читали про ядро, про 8 "вычислителей", про кросс-пути и про параллельное выполнение? И еще -- если поиграться оптимизацией -- можно получить неплохой код, не обязательно бросаться писать на АСМе. Ну да C vs. ASM -- это из другой оперы  На начальном этапе оптимизации многое из этого излишне. Скажем, кросс-пути - это вообще в последнюю очередь должно волновать голову, полно гораздо более значимых моментов. Цитата(Degun @ Oct 31 2007, 00:12)  Как известно у TI C6000-го семейства 8 конвейеров. А вообще приведённый код функции будет выполняться на одном конвейере или на 8-ми. Т. е. будет ли компилятор загружать автоматически все конвейеры или только один и необходимо предпринимать специальные меры, чтобы загрузить остальные 7? У этого семейства 8 юнитов, по 2 каждого типа: .L, .S, .D, .M. В частности, в SPRU732C Appendix B есть таблица "Mapping Between Instruction and Functional Unit". Например, в .M юнитах выполняются, в основном, умножения, в .L - логические операции и т.д. То есть, нельзя загрузить все юниты, если не используются соответствующие инструкции. Компилятор очень хорошо оптимизирует код, но только в том случае, если ему предоставить такую возможность - дать знать, сколько раз (обычно) выполняются циклы, вытащить из циклов вызовы функций и т.д. Большое количество разных if - смерть для оптимизатора. Стоит заметить, что у этих процессоров все инструкции являются условными, что можно грамотно применить при написании кода, получив ускорение даже в несколько раз, по сравнению с написанием "в лоб", при этом не приходится даже серьезно модифицировать алгоритм.
|
|
|
|
|
Nov 16 2007, 13:08
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Цитата(qxov @ Nov 16 2007, 13:07)  Я хотел бы попытался подсказать, как можно улучшить этот код, но для этого желательно на словах объяснить, что он должен делать - код не прозрачен и практически не читается, к сожалению. Подскажите, если задача не потеряла актуальность. Задача состоит в следующем: на вход функции поступает полутоновое (серое) 8-ми битовое изображение Image размерами Height и Width. Типичный пример изображения - звёзды на фоне космического пространства. Необходимо разбить изображение на непересекающиеся пронумерованные области в соответствии с двуми порогами: Thr1 - порог фона (нижний порог); Thr2 - порог объектов (верхний порог). Те пикселы, которые находятся между порогами Thr1 и Thr2 (т. е. Thr1 < PixelValue < Thr2) являются переходными и могут быть отнесены к любой ближайшей наиболее близкой (с точки зрения близости значений пикселов) области. На выходе функции в массиве AreasNumbBuf для каждого пиксела сохраняется номер области, к которой принадлежит этот пиксель (это может быть или космическое пространство или звезда или НЛО  ). Алгоритм с последней публикации был достаточно сильно изменён, поэтому привожу его заново. Но несмотря на значительную оптимизацию его быстродействие на процессоре DM642 оставляет желать лучшего. Например для изображения размерами 640*480 он выполняется порядка 0.6 сек, что неприемлемо. Планировалось, что он должен выполняться порядка 20 мсек (50 Гц). Код #include <stdlib.h>
//Максимальное кол-во областей в изображении #define MAX_AREAS 1000 #define min(x,y) (x<y?x:y)
//Структура хранения информации об областях typedef struct TAreaInfo { //Номер кластера short iNumber; //Номер моды short iMode; //Счётчик мощности моды int iCount; } TAreaInfo;
//Счётчики количества областей в изображении и в контейнере static int iAreasCount=0, iAreasInfoCount=0;
//Ограниченный массив структур описателей областей!!! static TAreaInfo AreasInfo[MAX_AREAS+1];
//Таблица соответствия значений пикселов модам гистограммы static int ModeNumb4Pix[256];
//Функция создания новой области static inline int AddNewAreaInfo(const int iModeNumb) { // if (iAreasInfoCount>=MAX_AREAS) return -1; iAreasCount++; AreasInfo[iAreasInfoCount].iMode=iModeNumb; AreasInfo[iAreasInfoCount].iNumber=-1; AreasInfo[iAreasInfoCount].iCount=0; return iAreasInfoCount++; }
/*!\brief Разбиение изображения на непересекающиеся области * \param Image - исходное изображение * \param Height - высота изображения * \param Width - ширина изображение * \param AreasNumbBuf - буфер результата, отражающий номера областей каждого из пикселов * \param Thr1 - нижний порог * \param Thr2 - верхний порог * \result Количество непересекающихся областей в изображении */ int SplitImage2Areas( register unsigned char ** __restrict Image, register int Height, register int Width, register int ** __restrict AreasNumbBuf, register int Thr1, register int Thr2) { //Изображение последовательно просматривается по строкам //для отнесения каждого пиксела к определённой области //Вычисление площади, занимаемой каждой областью и модой register int iPrevAreaNumbL=-1; //Номер области предыдущей левой точки register int iPrevModeNumbL=-1; //Номер моды предыдущей левой точки register int iPrevAreaNumbU=-1; //Номер области предыдущей верхней точки register int iPrevModeNumbU=-1; //Номер моды предыдущей верхней точки register int iPrevAreaNumbUL=-1; //Номер области предыдущей верхней левой точки register int iPrevModeNumbUL=-1; //Номер моды предыдущей верхней левой точки register int iPrevAreaNumbUR=-1; //Номер области предыдущей верхней правой точки register int iPrevModeNumbUR=-1; //Номер моды предыдущей верхней правой точки //Временные переменные register int i, y, x, iSrcIndex, iDstIndex, iDelta, iTmp; register int iCurrPixel, iCurrAreaNumb, iCurrModeNumb; register TAreaInfo * __restrict pSrcObj, * __restrict pDstObj, * __restrict pTmpObj;
//Проверка корректности входных параметров if ((Image==0)||(Width<=0)||(Height<=0)||(ModeNumb4Pix==0)||(AreasNumbBuf==0)) return -1; //Формирование таблицы мод пикселей for (i=0; i<256; i++) { if (i<Thr1) ModeNumb4Pix[i]=0; else if (i>=Thr2) ModeNumb4Pix[i]=1; else ModeNumb4Pix[i]=-1; } //Обнуление счётчиков кол-ва областей iAreasCount=iAreasInfoCount=0; //Обработка верхней левой точки изображения iCurrPixel=Image[0][0]; iCurrModeNumb=ModeNumb4Pix[iCurrPixel]; if (iCurrModeNumb<0) { //Сканирование зигзагом окрестных точек для //выявления ближайшей с установленной модой iDelta=min(Width,Height); for (i=1; i<iDelta; i++) { iSrcIndex=-1; for (y=i, x=0; y>=0; y--, x++) { iDstIndex=Image[y][x]; iTmp=ModeNumb4Pix[iDstIndex]; if (iTmp<0) continue; iDstIndex=abs(iDstIndex-iCurrPixel); if ((iSrcIndex<0)||(iSrcIndex>iDstIndex)) { iSrcIndex=iDstIndex; iCurrModeNumb=iTmp; } } //Нашли подходящую точку - выход из поиска if (iCurrModeNumb>=0) break; } //Если точек нет, то нулевая мода if (iCurrModeNumb<0) iCurrModeNumb=0; } iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb); AreasNumbBuf[0][0]=iCurrAreaNumb; //Обработка первой верхней строки изображения, //начиная со второго пиксела строки for (x=1; x<Width; x++) { //Запоминание номера области и моды для левой точки iPrevAreaNumbL=iCurrAreaNumb; iPrevModeNumbL=iCurrModeNumb; //Номер моды текущей точки iCurrModeNumb=ModeNumb4Pix[Image[0][x]]; //В зависимости от наличия моды if (iCurrModeNumb<0) { iCurrModeNumb=iPrevModeNumbL; iCurrAreaNumb=iPrevAreaNumbL; } else if (iCurrModeNumb==iPrevModeNumbL) { iCurrAreaNumb=iPrevAreaNumbL; } else { //Добавление новой области iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb); } //Установка номера области текущей точки AreasNumbBuf[0][x] = iCurrAreaNumb; //Инкремент счётчика площади, занимаемой областью AreasInfo[iCurrAreaNumb].iCount++; } //Декремент ширины изображения для увеличения скорости обработки Width--; //Цикл по всем точкам изображения for (y=1; y<Height; y++) { //Установка начального значения для верхней левой точки iPrevAreaNumbUL=AreasNumbBuf[y-1][0]; iPrevModeNumbUL=AreasInfo[iPrevAreaNumbUL].iMode; //Установка начального значения для верхней точки iPrevAreaNumbU=AreasNumbBuf[y-1][1]; iPrevModeNumbU=AreasInfo[iPrevAreaNumbU].iMode; //Значение яркости первого пикселя строки iCurrPixel=Image[y][0]; //Значение моды первого пикселя строки iPrevModeNumbL=ModeNumb4Pix[iCurrPixel]; //В зависимости от признака переходности пиксела if (iPrevModeNumbL<0) { if (abs(iCurrPixel-Image[y-1][0])<abs(iCurrPixel-Image[y-1][1])) { iPrevModeNumbL=iPrevModeNumbUL; iPrevAreaNumbL=iPrevAreaNumbUL; } else { iPrevModeNumbL=iPrevModeNumbU; iPrevAreaNumbL=iPrevAreaNumbU; } } else if (iPrevModeNumbL==iPrevModeNumbUL) iPrevAreaNumbL=iPrevAreaNumbUL; //Верхняя левая точка else if (iPrevModeNumbL==iPrevModeNumbU) iPrevAreaNumbL=iPrevAreaNumbU; //Верхняя точка else { //Добавление новой области iPrevAreaNumbL=AddNewAreaInfo(iPrevModeNumbL); } //Установка номера области первого пиксела строки AreasNumbBuf[y][0] = iPrevAreaNumbL; //Инкремент счётчика площади, занимаемой областью AreasInfo[iPrevAreaNumbL].iCount++; //Обработка очередной строки изображения for (x=1; x<Width; x++) { //Определение параметров верхней правой точки iPrevAreaNumbUR=AreasNumbBuf[y-1][x+1]; iPrevModeNumbUR=AreasInfo[iPrevAreaNumbUR].iMode; //Значение яркости текущего пикселя iCurrPixel=Image[y][x]; //Номер моды текущей точки iCurrModeNumb=ModeNumb4Pix[iCurrPixel]; //В зависимости от того является ли точка переходной if (iCurrModeNumb<0) { //Текущая точка является переходной к любой области //Перебор всех точек окрестности и выбор наиболее близкой области //Левая точка iDelta=abs(Image[y][x-1]-iCurrPixel); iCurrAreaNumb=iPrevAreaNumbL; iCurrModeNumb=iPrevModeNumbL; //Верхняя точка iTmp=abs(Image[y-1][x]-iCurrPixel); if (iDelta>iTmp) { iDelta=iTmp; iCurrAreaNumb=iPrevAreaNumbU; iCurrModeNumb=iPrevModeNumbU; } //Верхняя левая точка iTmp=abs(Image[y-1][x-1]-iCurrPixel); if (iDelta>iTmp) { iDelta=iTmp; iCurrAreaNumb=iPrevAreaNumbUL; iCurrModeNumb=iPrevModeNumbUL; } //Верхняя правая точка iTmp=abs(Image[y-1][x+1]-iCurrPixel); if (iDelta>iTmp) { iDelta=iTmp; iCurrAreaNumb=iPrevAreaNumbUR; iCurrModeNumb=iPrevModeNumbUR; } } //Эта точка не является переходной ни к одной из мод //Просмотр всех соседних уже размеченных ранее точек //для возможного определения номера области текущей точки else if (iCurrModeNumb==iPrevModeNumbL) iCurrAreaNumb=iPrevAreaNumbL; //Левая точка else if (iCurrModeNumb==iPrevModeNumbUL) iCurrAreaNumb=iPrevAreaNumbUL; //Верхняя левая точка else if (iCurrModeNumb==iPrevModeNumbU) iCurrAreaNumb=iPrevAreaNumbU; //Верхняя точка else if (iCurrModeNumb==iPrevModeNumbUR) iCurrAreaNumb=iPrevAreaNumbUR; //Верхняя правая точка else { //Добавление новой области iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb); } //Установка номера области текущей точки AreasNumbBuf[y][x] = iCurrAreaNumb; //Объект области в дереве pSrcObj=&AreasInfo[iCurrAreaNumb]; //Инкремент счётчика площади, занимаемой областью pSrcObj->iCount++; //Сканирование верхней правой точки для добавления её в список соседних областей if ((iCurrModeNumb==iPrevModeNumbUR)&&(iCurrAreaNumb!=iPrevAreaNumbUR)) { //Объект области предыдущей ссылки pDstObj=&AreasInfo[iPrevAreaNumbUR]; //Всего четыре возможных варианта if (pSrcObj->iNumber<0) { if (pDstObj->iNumber>=0) { //Только добавляется ссылка на ссылочную область pSrcObj->iNumber=pDstObj->iNumber; //Декремент кол-ва областей iAreasCount--; } else { //Обе области являются доменными iTmp=min(iCurrAreaNumb,iPrevAreaNumbUR); pSrcObj->iNumber=iTmp; pDstObj->iNumber=iTmp; //Декремент кол-ва областей iAreasCount--; } } else //if (pSrcObj->iNumber>=0) { if (pDstObj->iNumber<0) { //Только добавляется ссылка на ссылочную область pDstObj->iNumber=pSrcObj->iNumber; //Декремент кол-ва областей iAreasCount--; } else { iSrcIndex=pSrcObj->iNumber; iDstIndex=pDstObj->iNumber; if (iSrcIndex!=iDstIndex) { //Это самый трудоёмкий случай. //Но, к счастью, самый редкий! //Необходимо объединить две области for (i=0; i<iAreasInfoCount; i++) { pTmpObj=&AreasInfo[i]; if (pTmpObj->iNumber==iSrcIndex) pTmpObj->iNumber=iDstIndex; } //Декремент кол-ва областей iAreasCount--; } } } } //Запоминание номера области и моды для левой точки iPrevAreaNumbL=iCurrAreaNumb; iPrevModeNumbL=iCurrModeNumb; //Запоминание номера области и моды для верхней левой точки iPrevAreaNumbUL=iPrevAreaNumbU; iPrevModeNumbUL=iPrevModeNumbU; //Запоминание номера области и моды для верхней точки iPrevAreaNumbU=iPrevAreaNumbUR; iPrevModeNumbU=iPrevModeNumbUR; } //Значение яркости последнего пикселя строки iCurrPixel=Image[y][Width]; //Значение моды последнего пикселя строки iCurrModeNumb=ModeNumb4Pix[iCurrPixel]; //В зависимости от наличия моды if (iCurrModeNumb<0) { //Левая точка iDelta=abs(Image[y][x-1]-iCurrPixel); iCurrAreaNumb=iPrevAreaNumbL; iCurrModeNumb=iPrevModeNumbL; //Верхняя левая точка iTmp=abs(Image[y-1][x-1]-iCurrPixel); if (iDelta>iTmp) { iDelta=iTmp; iCurrAreaNumb=iPrevAreaNumbUL; iCurrModeNumb=iPrevModeNumbUL; } //Верхняя точка iTmp=abs(Image[y-1][x]-iCurrPixel); if (iDelta>iTmp) { iDelta=iTmp; iCurrAreaNumb=iPrevAreaNumbU; iCurrModeNumb=iPrevModeNumbU; } } else if (iCurrModeNumb==iPrevModeNumbL) iCurrAreaNumb=iPrevAreaNumbL; //Левая точка else if (iCurrModeNumb==iPrevModeNumbUL) iCurrAreaNumb=iPrevAreaNumbUL; //Верхняя левая точка else if (iCurrModeNumb==iPrevModeNumbU) iCurrAreaNumb=iPrevAreaNumbU; //Верхняя правая точка else { //Добавление новой области iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb); } //Установка номера области последнего пикселя строки AreasNumbBuf[y][Width] = iCurrAreaNumb; //Инкремент счётчика площади, занимаемой областью AreasInfo[iCurrAreaNumb].iCount++; } //Восстановление исходной ширины изображения Width++;
//Упорядочивание переадресации областей (именно здесь)!!!!! for (i=0; i<iAreasInfoCount; i++) { pTmpObj=&AreasInfo[i]; iTmp=pTmpObj->iNumber; if (iTmp<0) { //Это независимый кластер pTmpObj->iNumber=i; } else if (iTmp!=i) { //Это не кластерная область AreasInfo[iTmp].iCount+=pTmpObj->iCount; //Обнуление счётчика - выключение области pTmpObj->iCount=0; } }
//Могут оставаться независимые области с индексами больше iAreasCount for (iDstIndex=0, iSrcIndex=iAreasCount; iSrcIndex<iAreasInfoCount; iSrcIndex++) { //Если эта область уже переадресована - все нормально pSrcObj=&AreasInfo[iSrcIndex]; if ((pSrcObj==NULL)||(pSrcObj->iCount==0)) continue; //Поиск индекса для переадресации for (; iDstIndex<iAreasCount; iDstIndex++) { pDstObj=&AreasInfo[iDstIndex]; if ((pDstObj!=NULL)&&(pDstObj->iCount==0)) break; } //Сохранение указателя на моду pDstObj->iMode=pSrcObj->iMode; //Коррекция счётчиков мощности областей pDstObj->iCount=pSrcObj->iCount; pSrcObj->iCount=0; //Установка ссылки данной области на переадресованную pSrcObj->iNumber=iDstIndex; //Замена всех ссылок на эту область!!!!!!!! for (i=0; i<iAreasInfoCount; i++) { pTmpObj=&AreasInfo[i]; if (pTmpObj->iNumber==iSrcIndex) pTmpObj->iNumber=iDstIndex; } }
//Переписывание индексов областей на новые переназначенные for (y=0; y<Height; y++) for (x=0; x<Width; x++) { iTmp=AreasNumbBuf[y][x]; iTmp=AreasInfo[iTmp].iNumber; AreasNumbBuf[y][x]=iTmp; }
return iAreasCount; }
|
|
|
|
|
Nov 16 2007, 21:53
|

Частый гость
 
Группа: Свой
Сообщений: 86
Регистрация: 22-03-07
Из: Санкт-Петербург
Пользователь №: 26 406

|
Цитата(Degun @ Nov 16 2007, 16:08)  Задача состоит в следующем: на вход функции поступает полутоновое (серое) 8-ми битовое изображение Image размерами Height и Width. Типичный пример изображения - звёзды на фоне космического пространства. Необходимо разбить изображение на непересекающиеся пронумерованные области в соответствии с двуми порогами: Thr1 - порог фона (нижний порог); Thr2 - порог объектов (верхний порог). Те пикселы, которые находятся между порогами Thr1 и Thr2 (т. е. Thr1 < PixelValue < Thr2) являются переходными и могут быть отнесены к любой ближайшей наиболее близкой (с точки зрения близости значений пикселов) области. На выходе функции в массиве AreasNumbBuf для каждого пиксела сохраняется номер области, к которой принадлежит этот пиксель (это может быть или космическое пространство или звезда или НЛО  ). Алгоритм с последней публикации был достаточно сильно изменён, поэтому привожу его заново. Но несмотря на значительную оптимизацию его быстродействие на процессоре DM642 оставляет желать лучшего. Например для изображения размерами 640*480 он выполняется порядка 0.6 сек, что неприемлемо. Планировалось, что он должен выполняться порядка 20 мсек (50 Гц). Вот плохо понятно, как, всё-таки, производится разбиение на области  Вот так можно загружать процессор по полной программе (сам не тестировал, ибо дома композера нет): Код void calcModes(unsigned char * restrict image, char * restrict modes, int pixelsCount, int thresold1, int thresold2) { int thresoldLo=_packl4(_pack2(thresold1,thresold1),_pack2(thresold1,thresold1)); int thresoldHi=_packl4(_pack2(thresold2,thresold2),_pack2(thresold2,thresold2));
for(int i=0;i<pixelsCount;i+=8) { // Загрузим сразу 8 пикселов double pixels=_amemd8_const(image+i); // D
// Если пиксел >= thresold1, то в соответсвующем байте будет 0xff int compare1Lo=_xpnd4(_cmpgtu4(_lo(pixels),thresoldLo)); // L + M
// Если пиксел >= thresold2, то в соответсвующем байте будет 0xff int compare1Hi=_xpnd4(_cmpgtu4(_lo(pixels),thresoldHi)); // L + M
// Здесь очень удачно все сложилось - возможны три варианта: // если пиксел>=thresold1 и пиксел<thresold2, то получится 0xff ^ 0x00 = 0xff = -1; // если пиксел>=thresold1 и пиксел>=thresold2, то получится 0xff ^ 0xfe = 0x01 // если пиксел<thresold, то пиксел<thresold2 и получается 0x00 ^ 0x00 = 0x00 int result1=compare1Lo ^ (compare1Hi & 0xfefefefe); // LSD + LSD
// Здесь все тоже самое, для оставшихся четырех пикселов int compare2Lo=_xpnd4(_cmpgtu4(_hi(pixels),thresoldLo)); // L + M int compare2Hi=_xpnd4(_cmpgtu4(_hi(pixels),thresoldHi)); // L + M
int result2=compare2Lo ^ (compare2Hi & 0xfefefefe); // LSD + LSD
// Сохраним результат _amemd8(image+i)=_itod(result2,result1); // D
// Здесь получаем, что 4L и 4M юнита - два цикла; еще осталось 4S и 4D, 2D на загрузку // уйдет -> остается 4S и 2D юнита, для внутренностей цикла надо 4LSD => останется два S // юнита на поддержание цикла. Теоретически, производительность кода - 2 такта на 8 пикселов // или 0.25 такта на пиксел. При этом удалось добиться соответствия со значениями мод из // оригинального кода, добавив лишь одно ограничение по - количество пикселей должно быть кратно 8. // Следует также отметить, что даже при больших размерах изображения, не возникает простоя из-за // того, что данные не находятся в кэше. } }
|
|
|
|
|
Nov 17 2007, 10:54
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Цитата(qxov @ Nov 17 2007, 00:53)  Вот плохо понятно, как, всё-таки, производится разбиение на области  Для примера представьте себе изображение, где на черном фоне имеются три белых геометрических фигуры: прямоугольник, треугольник и круг. Тогда на выходе алгоритма в массиве AreasNumbBuf будет следующий результат: 00000000000000000000000000000000000000000000 00011111111111111111000000000000000000200000 00011111111111111111000000000000000002200000 00011111111111111111000000000000000022200000 00011111111111111111000000000000000222200000 00011111111111111111000000000000002222200000 00011111111111111111000000000000022222200000 00000000000000000000000000000000222222200000 00000000000333333000000000000002222222200000 00000003333333333333300000000022222222200000 00003333333333333333333300000222222222200000 00333333333333333333333333002222222222200000 03333333333333333333333333300000000000000000 00333333333333333333333333000000000000000000 00003333333333333333333300000000000000000000 00000003333333333333300000000000000000000000 00000000000333333000000000000000000000000000 00000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000 Т. е. исходный массив значений пикселов был преобразован в массив, где вместо значений пикселов находятся номера областей, к которым относятся данные пикселы. Всего здесь 4 области. При обработке алгоритм последовательно проходит по всем строкам изображения и каждый пиксел относит к одной из трёх возможных групп (мод): 1. группа (мода) фоновых пикселов: Pixel_Value<Threshold1 2. группа (мода) пикселов объектов: Pixel_Value>=Threshold2 3. группа (мода) переходных пикселов: Threshold1<=Pixel_Value<Threshold2, которые при работе алгоритма могут быть в итоге отнесены как к первой, так и ко второй группе (моде). На основе принадлежности текущего пиксела к той или иной группе (моде) делается вывод о том принадлежит ли текущий пиксел к уже размеченной ранее области или этот пиксел принадлежит к новой области. Для этого у текущего пиксела просматриваются четыре точки его окрестности (левая; верхняя левая; верхняя; верхняя правая точки) и если среди них найдена точка с такой же модой как у него, то номер области текущего пиксела будет такой же как у того пиксела, с которым у него совпала мода. Если таких нет, то добавляется новая область. Если пиксел относится к переходной моде, то из четырех точек его окрестности выбирается наиболее близкий по абсолютному значению яркости пиксел и берётся его номер области. Вкраце примерно так работает алгоритм. За основным циклом происходит ещё перенос областей с номерами большими, чем общее кол-во областей. Также производится окончательная перенумерация областей.
Сообщение отредактировал Degun - Nov 17 2007, 10:55
|
|
|
|
|
Nov 18 2007, 13:15
|

Частый гость
 
Группа: Свой
Сообщений: 86
Регистрация: 22-03-07
Из: Санкт-Петербург
Пользователь №: 26 406

|
Цитата(Degun @ Nov 16 2007, 16:08)  Алгоритм с последней публикации был достаточно сильно изменён, поэтому привожу его заново. Но несмотря на значительную оптимизацию его быстродействие на процессоре DM642 оставляет желать лучшего. Например для изображения размерами 640*480 он выполняется порядка 0.6 сек, что неприемлемо. Планировалось, что он должен выполняться порядка 20 мсек (50 Гц). А вот возникает еще вопрос: 640*480*2 байт памяти для 640x480, как минимум, используется, что равно 600Кб. Если верить ti, то самое бодрое, что есть - TMS320DM642-720 c 256 KB внутренней памяти. Это получается, что во внешней памяти данные хранятся или я не понимаю всего? Далее, 720e6/640/480/50 = 46.875. То есть, для 640x480x50 раз/сек есть, в лучшем случае, 46 тактов. Реально - еще меньше. Однозначно, работать нужно из внутренней памяти, причем не просто из внутренней памяти, а еще и так, чтобы данные укладывались в кэш первого уровня, иначе, грубо говоря, время выполнения увеличится чуть ли не в два раза. Таким образом необходимо: 1. Создать во внутреней памяти буфера для хранения трех строк: 1.1. Текущая строка 1.2. Предыдущая строка. 1.3. Следующая строка. 2. Настроить канал ДМА, чтобы во время обработки буферов (1.2.) и (1.1.) копировались данные из внешней памяти в (1.3.). 3. Дожидаемся окончания работы ДМА, свопим буфера и запускаем снова канал ДМА, а сами тем временем обрабатываем предыдущие строки. И, думаю, если копнуть глубже, то окажется, что времени не 1/50 секунды есть, а 1/25, не так ли? Реально частота кадров какая? Здесь надо менять очень серьезно все. А уже потом - оптимизировать код, там тоже есть, где разгуляться.
|
|
|
|
|
Nov 19 2007, 20:42
|

Частый гость
 
Группа: Свой
Сообщений: 86
Регистрация: 22-03-07
Из: Санкт-Петербург
Пользователь №: 26 406

|
Как успехи? Вот, набросал на коленке кусочек. Может, пригодиться...
int envPixelsCompareLo = _xpnd4(_cmpgtu4(envPixelsBrightness4,thresold1)); int envPixelsCompareHi = _xpnd4(_cmpgtu4(envPixelsBrightness4,thresold2)); int envPixelsModes4 = envPixelsCompareLo ^ (envPixelsCompareHi & 0xfefefefe);
unsigned char currPixelBrightness=currLine[i]; int currPixelBrightness4=_lo(_mpyli(currPixelBrightness,0x01010101)); int currPixelCompareLo = _xpnd4(_cmpgtu4(currPixelBrightness4,thresold1)); int currPixelCompareHi = _xpnd4(_cmpgtu4(currPixelBrightness4,thresold2)); int currPixelMode4 = currPixelCompareLo ^ (currPixelCompareHi & 0xfefefefe);
int equalModesCompare=_cmpeq4(currPixelMode4,envPixelsModes4); // 1 там, где моды совпадают int absDeltas = _subabs4(currPixelBrightness4,envPixelsBrightness); int minTmp = _minu4(absDeltas,_swap4(absDeltas)); int minAbs4 = _min2(minTmp, _packlh2(minTmp, minTmp)); int minAbsCompare=_cmpeq4(minAbs4, absDeltas); // 1 там, где минимальная разница в яркости
int minAbsPos=31-_norm(minAbsCompare); int equalModesPos=31-_norm(equalModesCompare);
int areaPos=(minAbsPos<=equalModesPos) ? minAbsPos : equalModesPos; int thisPixelArea=(areaPos) ? envAreas[areaPos-1] : lastAreaNo++;
|
|
|
|
|
Nov 20 2007, 13:33
|
Участник

Группа: Новичок
Сообщений: 26
Регистрация: 20-11-07
Пользователь №: 32 502

|
Цитата(Degun @ Nov 8 2007, 04:59)  Что-то я запутался в конвейерах и в глубине конвейера для DM642. В документации по нему сказано, что у него 8 параллельно работающих устройств, выполняющих команды. Т. е. если повезёт, то за один такт может выполниться параллельно до 8-ми команд. Значит 8 - это количество конвейеров или это глубина всего одного конвейера? C4itajte, 4to eto 8 ALU, spezializirovannyh kazdyj pod svoju zada4u (umnozhenie slozhenie adresazija) Kazdoe iz alu imeet svoj konveer. glubina ih razli4na. Umnozhiteli i summatory imejut glubinu - 4 (dlja float) Adresatory - dlinu 5. Programmirovat' eto vse, 4tob rabotalo parallelno, nado o4en' vnimatel'no (esli ru4kami na assemblere - kubik Rubika uzhe upominalsja). Ili usilenno pol'zovat'sja pragmami i optimizaziej compiljatora. 4itajte documentaziju po optimizazii ziklov (fontp privodil spisok). A compiljator k C6000 vpolne gramotno napisan. Sam videl,4to pripravilnoj rasstanovke pragm on delal 4asto kod lu46e, 4em ja ru4kami.
|
|
|
|
|
Nov 20 2007, 17:38
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Цитата(qxov @ Nov 17 2007, 00:53)  Вот так можно загружать процессор по полной программе: Код void calcModes(unsigned char * restrict image, char * restrict modes, int pixelsCount, int thresold1, int thresold2) { ... } Это полезная процедура. Большое спасибо. Но остаётся сложная часть алгоритма, где много if-ов, которая определяет логику создания областей, которую я не понимаю как оптимизировать. Взять хотя бы "нумеризацию" первой строки: Код //Обработка первой верхней строки изображения, начиная со второго пиксела строки for (x=1; x<Width; x++) { //Запоминание номера области и моды для левой точки iPrevAreaNumbL=iCurrAreaNumb; iPrevModeNumbL=iCurrModeNumb; //Номер моды текущей точки iCurrModeNumb=ModeNumb4Pix[Image[0][x]]; //В зависимости от наличия моды if (iCurrModeNumb<0) { iCurrModeNumb=iPrevModeNumbL; iCurrAreaNumb=iPrevAreaNumbL; } else if (iCurrModeNumb==iPrevModeNumbL) { iCurrAreaNumb=iPrevAreaNumbL; } else { //Добавление новой области iCurrAreaNumb=AddNewAreaInfo(iCurrModeNumb); } //Установка номера области текущей точки AreasNumbBuf[0][x] = iCurrAreaNumb; //Инкремент счётчика площади, занимаемой областью AreasInfo[iCurrAreaNumb].iCount++; } Цитата(qxov @ Nov 18 2007, 16:15)  Таким образом необходимо: 1. Создать во внутреней памяти буфера для хранения трех строк: 1.1. Текущая строка 1.2. Предыдущая строка. 1.3. Следующая строка. 2. Настроить канал ДМА, чтобы во время обработки буферов (1.2.) и (1.1.) копировались данные из внешней памяти в (1.3.). 3. Дожидаемся окончания работы ДМА, свопим буфера и запускаем снова канал ДМА, а сами тем временем обрабатываем предыдущие строки. Это несомненно правильный подход. Сейчас как раз разбираюсь с DMA Цитата(qxov @ Nov 18 2007, 16:15)  Здесь надо менять очень серьезно все. А уже потом - оптимизировать код, там тоже есть, где разгуляться. В принципе да, но и оптимизацию до применения DMA можно сделать или параллельно ей, т. к. это, в общем-то, независимые задачи. Цитата(qxov @ Nov 18 2007, 16:15)  И, думаю, если копнуть глубже, то окажется, что времени не 1/50 секунды есть, а 1/25, не так ли? Реально частота кадров какая? Частота кадров именно 50 Гц. Таково, так сказать, ТЗ и некуда от этого не деться. Но можно, до определённых пределов, уменьшить разрешение кадра. Цитата(qxov @ Nov 19 2007, 23:42)  Как успехи? Вот, набросал на коленке кусочек. Может, пригодиться... ... Не совсем понял что делает этот кусок, т. е. какому участку C-го алгоритма он соответствует?
|
|
|
|
|
Nov 22 2007, 14:13
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Уважаемый qxov я сделал вашу функцию calcModes с применением DMA. На изображении с разрешением 640*480 пикселей она выполняется за 10.2 мсек. Код #include <stdlib.h> #include <csl_dat.h>
#define IMG_WIDTH 640 #define IMG_HEIGHT 480
// Буферы для кэширования строк изображения static unsigned char ModesBuf[3][IMG_WIDTH];
//Функция, классифицирующая пикселы изображения по модам void SplitImage2Modes(unsigned char ** restrict image, unsigned char ** restrict modes, int thresold1, int thresold2) { //Рабочие регистры цикла int lockPixels, compareLo, compareHi, result1, result2; unsigned char * restrict pCurrLineBuf; double pixels;
//Open DAT-chanel DAT_open(DAT_CHAANY, DAT_PRI_HIGH, 0);
//Загрузка первой строки изображения Uint32 id_LoadTransfer = DAT_copy(image[0], ModesBuf[0], IMG_WIDTH); //Начальная инициализация идентификатора канала сохранения Uint32 id_SaveTransfer=DAT_XFRID_WAITNONE;
//кольцевой указатель на буфер текущей строки int iCurrBufIndex=0;
//пакеты порогов int thresoldLo=_packl4(_pack2(thresold1,thresold1),_pack2(thresold1,thresold1)); int thresoldHi=_packl4(_pack2(thresold2,thresold2),_pack2(thresold2,thresold2));
//цикл по всем строкам изображения //#pragma MUST_ITERATE(IMG_HEIGHT, IMG_HEIGHT); for(int y=0; y<IMG_HEIGHT; y++) { //Указатель на текущую обрабатываемую строку pCurrLineBuf = ModesBuf[iCurrBufIndex]; //Продвижение индекса на следующую строку iCurrBufIndex++; iCurrBufIndex%=3; //Ожидание окончания загрузки текущей строки изображения while(DAT_busy(id_LoadTransfer)); //Запуск загрузки следующей строки изображения id_LoadTransfer = DAT_copy(image[y+1], ModesBuf[iCurrBufIndex], IMG_WIDTH); //обработка текущей строки //#pragma MUST_ITERATE(IMG_WIDTH/8, IMG_WIDTH/8); for(int x=0; x<IMG_WIDTH; x+=8) { // Загрузим сразу 8 пикселов pixels=_amemd8_const(pCurrLineBuf+x); // D
//Первые четыре пиксела lockPixels=_lo(pixels);
// Если пиксел >= thresold1, то в соответсвующем байте будет 0xff compareLo=_xpnd4(_cmpgtu4(lockPixels,thresoldLo)); // L + M
// Если пиксел >= thresold2, то в соответсвующем байте будет 0xff compareHi=_xpnd4(_cmpgtu4(lockPixels,thresoldHi)); // L + M
// Здесь очень удачно все сложилось - возможны три варианта: // если пиксел>=thresold1 и пиксел<thresold2, то получится 0xff ^ 0x00 = 0xff = -1; // если пиксел>=thresold1 и пиксел>=thresold2, то получится 0xff ^ 0xfe = 0x01 // если пиксел<thresold, то пиксел<thresold2 и получается 0x00 ^ 0x00 = 0x00 result1=compareLo ^ (compareHi & 0xfefefefe); // LSD + LSD
// Здесь все тоже самое, для оставшихся четырех пикселов lockPixels=_hi(pixels);
compareLo=_xpnd4(_cmpgtu4(lockPixels,thresoldLo)); // L + M compareHi=_xpnd4(_cmpgtu4(lockPixels,thresoldHi)); // L + M
result2=compareLo ^ (compareHi & 0xfefefefe); // LSD + LSD
// Сохраним результат _amemd8(pCurrLineBuf+x)=_itod(result2,result1); // D
// Здесь получаем, что 4L и 4M юнита - два цикла; еще осталось 4S и 4D, 2D на загрузку // уйдет -> остается 4S и 2D юнита, для внутренностей цикла надо 4LSD => останется два S // юнита на поддержание цикла. Теоретически, производительность кода - 2 такта на 8 пикселов // или 0.25 такта на пиксел. При этом удалось добиться соответствия со значениями мод из // оригинального кода, добавив лишь одно ограничение по - количество пикселей должно быть кратно 8. // Следует также отметить, что даже при больших размерах изображения, не возникает простоя из-за // того, что данные не находятся в кэше. } //Ожидание окончания сохранения результата while(DAT_busy(id_SaveTransfer)); //Сохранение строки результата id_SaveTransfer = DAT_copy(pCurrLineBuf, modes[y], IMG_WIDTH); }
//Ожидание окончания сохранения результата while(DAT_busy(id_SaveTransfer));
//Close DAT-chanel DAT_close(); } Для сравнения неоптимизированный код для того же изображения выполняется за 102 мсек, т. е. оптимизация даёт десятикратный выигрыш. Код #include <stdlib.h>
//Функция, классифицирующая пикселы изображения по модам void SplitImage2Modes(unsigned char ** restrict image, unsigned char ** restrict modes, int height, int width, int thresold1, int thresold2) { if ((image==NULL)||(modes==NULL)) return;
//цикл по всем пикселам изображения for(int y=0; y<height; y++) for(int x=0; x<width; x++) { int pixel=image[y][x]; if (pixel<thresold1) modes[y][x]=0; else if (pixel>thresold2) modes[y][x]=1; else modes[y][x]=-1; } }
|
|
|
|
|
Nov 22 2007, 14:49
|

Частый гость
 
Группа: Свой
Сообщений: 86
Регистрация: 22-03-07
Из: Санкт-Петербург
Пользователь №: 26 406

|
Цитата(Degun @ Nov 22 2007, 17:13)  Уважаемый qxov я сделал вашу функцию calcModes с применением DMA. На изображении с разрешением 640*480 пикселей она выполняется за 10.2 мсек.
Для сравнения неоптимизированный код для того же изображения выполняется за 102 мсек, т. е. оптимизация даёт десятикратный выигрыш. Скорее всего, простои на ожидание окончания работы DMA + накладные расходы, связанные с DAT модулем. Нужно интегрировать с какими-нибудь другими расчетами - тогда это замедление не будет так сильно сказываться. На глаз раз в 50 сами вычисления быстрее должны производиться, но это - грубо, могу ошибиться, просто спешу сейчас. Но точно сами вычисления значительно быстрее проходят.
|
|
|
|
|
Nov 23 2007, 11:58
|
Частый гость
 
Группа: Новичок
Сообщений: 84
Регистрация: 4-09-07
Из: Москва
Пользователь №: 30 277

|
Цитата(qxov @ Nov 22 2007, 23:00)  А можно ли считать, что за пределами кадра есть рамка размером в один пиксел со значением, скажем, 0? Да вполне. Цитата(qxov @ Nov 22 2007, 17:49)  Скорее всего, простои на ожидание окончания работы DMA + накладные расходы, связанные с DAT модулем. Нужно интегрировать с какими-нибудь другими расчетами - тогда это замедление не будет так сильно сказываться. На глаз раз в 50 сами вычисления быстрее должны производиться, но это - грубо, могу ошибиться, просто спешу сейчас. Но точно сами вычисления значительно быстрее проходят. Попробовал вариант, в котором убрал все обращения к внешней памяти через DMA, а только к внутреннему static буферу. Этот варинат выполнился за 7.9 мсек. Не понимаю тогда где задержка? Код #include <stdlib.h>
#include "cs_common.h"
// Буферы для кэширования строк изображения static unsigned char ModesBuf[3][IMG_WIDTH];
//Функция, классифицирующая пикселы изображения по модам void SplitImage2Modes(unsigned char ** restrict image, unsigned char ** restrict modes, int thresold1, int thresold2) { //Рабочие регистры цикла int lockPixels, compareLo, compareHi, result1, result2; unsigned char * restrict pCurrLineBuf; double pixels;
//кольцевой указатель на буфер текущей строки int iCurrBufIndex=0;
//пакеты порогов int thresoldLo=_packl4(_pack2(thresold1,thresold1),_pack2(thresold1,thresold1)); int thresoldHi=_packl4(_pack2(thresold2,thresold2),_pack2(thresold2,thresold2));
//цикл по всем строкам изображения //#pragma MUST_ITERATE(IMG_HEIGHT, IMG_HEIGHT); for(int y=0; y<IMG_HEIGHT; y++) { //Указатель на текущую обрабатываемую строку pCurrLineBuf = ModesBuf[iCurrBufIndex]; //Продвижение индекса на следующую строку iCurrBufIndex++; iCurrBufIndex%=3; //обработка текущей строки //#pragma MUST_ITERATE(IMG_WIDTH/8, IMG_WIDTH/8); for(int x=0; x<IMG_WIDTH; x+=8) { // Загрузим сразу 8 пикселов pixels=_amemd8_const(pCurrLineBuf+x); // D
//Первые четыре пиксела lockPixels=_lo(pixels);
// Если пиксел >= thresold1, то в соответсвующем байте будет 0xff compareLo=_xpnd4(_cmpgtu4(lockPixels,thresoldLo)); // L + M
// Если пиксел >= thresold2, то в соответсвующем байте будет 0xff compareHi=_xpnd4(_cmpgtu4(lockPixels,thresoldHi)); // L + M
// Здесь очень удачно все сложилось - возможны три варианта: // если пиксел>=thresold1 и пиксел<thresold2, то получится 0xff ^ 0x00 = 0xff = -1; // если пиксел>=thresold1 и пиксел>=thresold2, то получится 0xff ^ 0xfe = 0x01 // если пиксел<thresold, то пиксел<thresold2 и получается 0x00 ^ 0x00 = 0x00 result1=compareLo ^ (compareHi & 0xfefefefe); // LSD + LSD
// Здесь все тоже самое, для оставшихся четырех пикселов lockPixels=_hi(pixels);
compareLo=_xpnd4(_cmpgtu4(lockPixels,thresoldLo)); // L + M compareHi=_xpnd4(_cmpgtu4(lockPixels,thresoldHi)); // L + M
result2=compareLo ^ (compareHi & 0xfefefefe); // LSD + LSD
// Сохраним результат _amemd8(pCurrLineBuf+x)=_itod(result2,result1); // D } } }
|
|
|
|
|
Nov 23 2007, 12:50
|

Частый гость
 
Группа: Свой
Сообщений: 86
Регистрация: 22-03-07
Из: Санкт-Петербург
Пользователь №: 26 406

|
Цитата(Degun @ Nov 23 2007, 14:58)  Да вполне. Попробовал вариант, в котором убрал все обращения к внешней памяти через DMA, а только к внутреннему static буферу. Этот варинат выполнился за 7.9 мсек. Не понимаю тогда где задержка? Точно 7.9 мсек? Просто 7.9 мсек на 500МГц - 3950000 циклов, что многовато для обработки 640*480=307200 пикселов изображения - более 128 циклов на пиксел, а расчетное - около 0.25 с хвостиком должно выходить. Ну пусть в два раза ошиблись, но не на столько же... Оптимизация-то включена?
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|