Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: многоканальная обработка
Форум разработчиков электроники ELECTRONIX.ru > Цифровая обработка сигналов - ЦОС (DSP) > Алгоритмы ЦОС (DSP)
Doka
в ЦОС при переходе от одноканальных алгоритмов обработки даннных к многоканальным часто приходится иметь дело с контекстом канала - наиболее распространенный способ для этого - завести структуру, содержащий контекст обработки канала. и в программе использовать массив структур размерностью с число каналов.

Однако вызываться они могут поразному:
1. вызываемой процедуре передается номер канала (фактически - индекс массива структур)
2. вызываемой процедуре передается адрес указателя на структуру

интересен выбор оптимального способа в приложении использования на сигнальниках (архитектура TI C5000)
2й способ , как я понимаю позволяет не привязываться к массиву структур - это позволяет (при большом объеме контекста) размещать структуры в карте памяти с разрывом (если поиному не влазиют) либо в разные секции памяти.

какие еще достоинства/недостатки? (в т.ч. с возможным влиянием на производительность алгоритма)
=GM=
Цитата(Doka @ Dec 6 2006, 12:53) *
в ЦОС при переходе от одноканальных алгоритмов обработки даннных к многоканальным часто приходится иметь дело с контекстом канала - наиболее распространенный способ для этого - завести структуру, содержащий контекст обработки канала. и в программе использовать массив структур размерностью с число каналов.

Однако вызываться они могут по разному:
1. вызываемой процедуре передается номер канала (фактически - индекс массива структур)
2. вызываемой процедуре передается адрес указателя на структуру

интересен выбор оптимального способа в приложении использования на сигнальниках (архитектура TI C5000)
2й способ , как я понимаю позволяет не привязываться к массиву структур - это позволяет (при большом объеме контекста) размещать структуры в карте памяти с разрывом (если поиному не влазят) либо в разные секции памяти.

какие еще достоинства/недостатки? (в т.ч. с возможным влиянием на производительность алгоритма)


Из двух предложенных я бы предпочел второй способ, не надо адрес вычислять, а в первом надо брать смещение, добавлять к индексу, проверять рамки...много возни.

Но есть еще третий путь, самый быстрый - использовать DP-адресацию. Сам пользую, очень удобно.

А структура, имхо, сама по себе будет потреблять немного ресурсов, в смысле, чтобы добраться до элемента структуры.
SasaTheProgrammer
Цитата(=GM= @ Dec 6 2006, 16:21) *
Из двух предложенных я бы предпочел второй способ, не надо адрес вычислять, а в первом надо брать смещение, добавлять к индексу, проверять рамки...много возни.

Более того, если используется несколько вызовов подряд, например
Код
extern DATA data[];
/ **/
DATA *ptr = data+i;
foo(ptr);
if (isTRANSPARENT(mode)) bar(ptr);
if (isREPLY(mode)) doReply(ptr);
ptr->callBack(ptr);

то и адрес по индексу вычисляется один раз.
Цитата
Но есть еще третий путь, самый быстрый - использовать DP-адресацию. Сам пользую, очень удобно.

А это что за зверь?!
Цитата
А структура, имхо, сама по себе будет потреблять немного ресурсов, в смысле, чтобы добраться до элемента структуры.

Эт' как сказать. Один мой знакомый компилятор :-) транслирует выражение вроде ptr->subStruct->subField[i]++; в долгое вычисление адреса нужного слова, загружает его в регистр, прибавляет единицу... после чего снова вычисляет адрес - куда положить!!! Явное использование промежуточных указателей дало существенный прирост на таких, казалось бы простых, операциях...
=GM=
Цитата(SasaTheProgrammer @ Dec 7 2006, 00:56) *
Более того, если используется несколько вызовов подряд, например
Код
extern DATA data[];
/ **/
DATA *ptr = data+i;
foo(ptr);
if (isTRANSPARENT(mode)) bar(ptr);
if (isREPLY(mode)) doReply(ptr);
ptr->callBack(ptr);

то и адрес по индексу вычисляется один раз.

Пример не наглядный, т.к. ваш указатель используется несколько раз подряд, счастлив ваш бог, и потом, вы сами явно указали процессору вычислять адрес по индексу один раз. А попробуйте хотя бы чередовать ptr1, ptr2, ptr1, ptr2 - увидите что будет(:-). Вот если бы вы передавали просто адрес, скажем адрес указателя на структуру, тогда другое дело, поскольку вычислять вообще ничего не надо!

К слову. Такое впечатление, что мы говорим на разных языках, и главное - пишем, я пишу в основном на ассемблере, поэтому и говорю, в основном, об адресации на языке ассемблера, а вы, похоже, говорите об адресации на си.

Цитата
Цитата

Но есть еще третий путь, самый быстрый - использовать DP-адресацию. Сам пользую, очень удобно.

А это что за зверь?!

direct addressing mode. Заносите в дп-регистр адрес страницы и обращаетесь к 64/128 переменным, используя DP-адресацию. Там для указания переменной надо писать @variable, например,

Код
   movl   @freq,acc   ;переслать 32-битную переменную в ячейку freq
   dec     @cont      ;скрутить счетчик
   tclr   @port,#bit  ;обнулить бит в порту



Цитата
Цитата

А структура, имхо, сама по себе будет потреблять немного ресурсов, в смысле, чтобы добраться до элемента структуры.

Эт' как сказать. Один мой знакомый компилятор :-) транслирует выражение вроде ptr->subStruct->subField[i]++; в долгое вычисление адреса нужного слова, загружает его в регистр, прибавляет единицу... после чего снова вычисляет адрес - куда положить!!! Явное использование промежуточных указателей дало существенный прирост на таких, казалось бы простых, операциях...

Ну вот, я был прав, компилер. Да будь он хоть трижды знакомый, с ним не договоришься, всё равно будет дурь переть(:-). И приходится вам, бедным сишникам, бороться не с программой и алгоритмом, а с дуроломным компилером и его разработчиками(:-). Здесь не надо отвечать, не хочу быть поджигателем очередной религиозной войны.

На ассемблере всё проще, есть такие виды адресации, как, скажем, XARn(ARm), одна команда, раз - и содержимое поля структуры у вас в кармане(:-).
SasaTheProgrammer
Цитата(=GM= @ Dec 7 2006, 14:01) *
Пример не наглядный, т.к. ваш указатель используется несколько раз подряд, счастлив ваш бог, и потом, вы сами явно указали процессору вычислять адрес по индексу один раз. А попробуйте хотя бы чередовать ptr1, ptr2, ptr1, ptr2 - увидите что будет(:-). Вот если бы вы передавали просто адрес, скажем адрес указателя на структуру, тогда другое дело, поскольку вычислять вообще ничего не надо!

Ну, так автор вопроса и спрашивал - что эффективней, адресация или индексирование по глобальному массиву. Во всяком случае, я понял вопрос именно так.
А что должно случиться при чередовании указателей? Или это косвенный намёк на особенности х51 (у кого ещё есть дп? smile.gif )?
Цитата
К слову. Такое впечатление, что мы говорим на разных языках, и главное - пишем, я пишу в основном на ассемблере, поэтому и говорю, в основном, об адресации на языке ассемблера, а вы, похоже, говорите об адресации на си.

Хмм... Думаю, что в данном контексте это несущественно. Сформулируем так: если обработка многостадийная, то адрес по индексу лучше вычислить один раз, а не передавать индекс в каждую процедуру. Независимо от языка и уровня реализации.
Цитата
Ну вот, я был прав, компилер. Да будь он хоть трижды знакомый, с ним не договоришься, всё равно будет дурь переть(:-). И приходится вам, бедным сишникам, бороться не с программой и алгоритмом, а с дуроломным компилером и его разработчиками(:-). Здесь не надо отвечать, не хочу быть поджигателем очередной религиозной войны.

На ассемблере всё проще, есть такие виды адресации, как, скажем, XARn(ARm), одна команда, раз - и содержимое поля структуры у вас в кармане(:-).

В общем случае не совсем так. На ассемблере действительно, чаще всего можно написать и компактней и эффективней. И в ряде случаев это приходится делать, получая очень существенный выиграш. Но наглядность и отлаживаемость кода резко падают, зато трудоёмкость растёт. А если код ещё и большой, то в конечном итоге компилятор с ЯВУ обеспечивает более эффективный код, чем одуревшая команда программистов biggrin.gif . Причём это происходит не только (а может быть и не столько) на "скромных" архитектурах вроде х51, но и на тяжеловозах АРМ (мой случай biggrin.gif ). Т.е. на самом деле важно найти "узкое место" - 5..10 процентов кода - и ассемблером их, ассемблером! Остальное так, как себе, любимому, проще, на эффективности это почти не скажется. И никаких войн!
=GM=
Цитата(SasaTheProgrammer @ Dec 7 2006, 23:40) *
Или это косвенный намёк на особенности х51 (у кого ещё есть дп? smile.gif )?

Цитата
Причём это происходит не только (а может быть и не столько) на "скромных" архитектурах вроде х51, но и на тяжеловозах АРМ (мой случай biggrin.gif ).

Вы несколько раз упомянули об архитектуре х51, это что i5051?
SasaTheProgrammer
Цитата(=GM= @ Dec 8 2006, 11:59) *
Цитата(SasaTheProgrammer @ Dec 7 2006, 23:40) *

Или это косвенный намёк на особенности х51 (у кого ещё есть дп? smile.gif )?

Цитата
Причём это происходит не только (а может быть и не столько) на "скромных" архитектурах вроде х51, но и на тяжеловозах АРМ (мой случай biggrin.gif ).

Вы несколько раз упомянули об архитектуре х51, это что i5051?

Да. А резве подразумевалось что-то другое?
Edmundo
Цитата(=GM= @ Dec 8 2006, 12:59) *
Вы несколько раз упомянули об архитектуре х51, это что i5051?

Может все-таки классическое ядро 8051... Или я не в теме?..
Doka
Цитата(Edmundo @ Dec 9 2006, 00:55) *
Может все-таки классическое ядро 8051... Или я не в теме?..

для присоединившихся:
изначально под C5000 я имел в виду С54хх и С55хх от TI.
интересовала реализация на языке Си.

хотя использование Си в какой-то мере подразумевает CPU-independent обсуждение.

благодаря ответам, вопрос прояснился.. теперь немного об автоматизации второго способа (через передачу указателя на адрес структуры):
если надо обрабатывать 32 канала, то в 1м способе просто могли организовывать цикл по 32, а переменную цикла передавать в функцию обработки ка кномер канала.
а в ситуации с передачей адреса указателя на структуру быть как?..
первое что приходит: заводить контекст канала всеже как _массив_ структур, а адрес структуры вычислять как:

Код
TYPE_OF_STRUC_CH_CONTEXT  struct_ch_context[32];
int *curr_struc_addr;
void  call_ch_process(int **paddr);
...
for (i = 0; i < 32;  i++)
{
  curr_struc_addr = struct_ch_context + i*sizeof(TYPE_OF_STRUC_CH_CONTEXT);  
  call_ch_process(*curr_struc_addr);
}


вот как-то так чтоли получается..
SasaTheProgrammer
Цитата(Doka @ Dec 9 2006, 14:12) *
хотя использование Си в какой-то мере подразумевает CPU-independent обсуждение.

Угу. Я, собственно, именно из таких соображений и отвечал.
Цитата
Код
TYPE_OF_STRUC_CH_CONTEXT  struct_ch_context[32];
int *curr_struc_addr;
void  call_ch_process(int **paddr);
...
for (i = 0; i < 32;  i++)
{
  curr_struc_addr = struct_ch_context + i*sizeof(TYPE_OF_STRUC_CH_CONTEXT);  
  call_ch_process(*curr_struc_addr);
}


вот как-то так чтоли получается..

НЕЕЕЕТ!!! Во-первых, не нужно умножать на размер структуры - компилятор сделает это сам - мы ведь о Си говорим? Т.е. это просто ошибка, указатель унесёт страшно даже подумать куда smile.gif .
Во-вторых, функция объявлена как принимающая указатель на указатель на int (откуда?!), а что ей передаётся в действительности?!
Edmundo
Цитата(Doka @ Dec 9 2006, 15:12) *
для присоединившихся:
изначально под C5000 я имел в виду С54хх и С55хх от TI.
интересовала реализация на языке Си.

хотя использование Си в какой-то мере подразумевает CPU-independent обсуждение.

благодаря ответам, вопрос прояснился.. теперь немного об автоматизации второго способа (через передачу указателя на адрес структуры):
если надо обрабатывать 32 канала, то в 1м способе просто могли организовывать цикл по 32, а переменную цикла передавать в функцию обработки ка кномер канала.
а в ситуации с передачей адреса указателя на структуру быть как?..
первое что приходит: заводить контекст канала всеже как _массив_ структур, а адрес структуры вычислять как:

Код
TYPE_OF_STRUC_CH_CONTEXT  struct_ch_context[32];
int *curr_struc_addr;
void  call_ch_process(int **paddr);
...
for (i = 0; i < 32;  i++)
{
  curr_struc_addr = struct_ch_context + i*sizeof(TYPE_OF_STRUC_CH_CONTEXT);  
  call_ch_process(*curr_struc_addr);
}


вот как-то так чтоли получается..

Я просто не встречал ядро i5051, а про С5000 понятно.

Но про код не понял -- почему не сделать так?

Код
TYPE_OF_STRUC_CH_CONTEXT  struct_ch_context[32];
void  call_ch_process(void *paddr);
...
for (i = 0; i < 32;  i++)
{
  call_ch_process(&struct_ch_context[i]);
}
...
void  call_ch_process(void *paddr)
{
    TYPE_OF_STRUC_CH_CONTEXT *pstruct = (TYPE_OF_STRUC_CH_CONTEXT *) paddr;
    pstruct->...
    ...
}

Или вам внутри call_ch_process нужно обязательно знать номер канала? Тогда проще добавить его как аргумент функции.
Doka
Цитата(Edmundo @ Dec 10 2006, 11:19) *
Но про код не понял -- почему не сделать так?

Код
TYPE_OF_STRUC_CH_CONTEXT  struct_ch_context[32];
void  call_ch_process(void *paddr);
...
for (i = 0; i < 32;  i++)
{
  call_ch_process(&struct_ch_context[i]);
}
...
void  call_ch_process(void *paddr)
{
    TYPE_OF_STRUC_CH_CONTEXT *pstruct = (TYPE_OF_STRUC_CH_CONTEXT *) paddr;
    pstruct->...
    ...
}


Или вам внутри call_ch_process нужно обязательно знать номер канала? Тогда проще добавить его как аргумент функции.


за пример большое спасибо - просто почти не работал со структурами на Си - поэтому некий сумбур в голове.

номер канала действительно желательно знать - ибо call_ch_process вызывает из себя процедуры, обработка в некоторых из них также завязана на контекст канала. Но номер канала планирую хранить как поле структуры , прописывая его при инициализации (ну а там уж передавать - либо номер канала, либо указатель на структуру).

еще такой исследовательский вопрос: когда я смотрел как это в принципе делают, то в нек-х случаях передают адрес указателя на структуру, а не указатель на структуру - для чего это может быть полезно? (единственно что напрашивается - возможность изменять само содержимое указателя)
Edmundo
Цитата(Doka @ Dec 10 2006, 11:52) *
за пример большое спасибо - просто почти не работал со структурами на Си - поэтому некий сумбур в голове.

номер канала действительно желательно знать - ибо call_ch_process вызывает из себя процедуры, обработка в некоторых из них также завязана на контекст канала. Но номер канала планирую хранить как поле структуры , прописывая его при инициализации (ну а там уж передавать - либо номер канала, либо указатель на структуру).

еще такой исследовательский вопрос: когда я смотрел как это в принципе делают, то в нек-х случаях передают адрес указателя на структуру, а не указатель на структуру - для чего это может быть полезно? (единственно что напрашивается - возможность изменять само содержимое указателя)

У меня тоже только это напрашивается. Например если память под структуру выделяется (или перемещается) внутри этой функции.

А вообще про то, как организовывать многоканальную обработку, можно посмотреть в техасовском
XDAIS (если вы еще с ним не знакомы, конечно smile.gif). Сам стандарт в нашей лаборатории по разным причинам не пошел, но кое-какие идеи оттуда почерпнуть имхо можно.
SasaTheProgrammer
Начнём с конца.
Цитата(Doka @ Dec 10 2006, 10:52) *
еще такой исследовательский вопрос: когда я смотрел как это в принципе делают, то в нек-х случаях передают адрес указателя на структуру, а не указатель на структуру - для чего это может быть полезно? (единственно что напрашивается - возможность изменять само содержимое указателя)

Указатель передают в двух случаях. Во-первых, есть существеноое различие, для которого применяют специальные термины - "передача по ссылке"/"передача по значению". При "передаче по значению" функция получает свою копию данных, с которой можно делать всё что угодно. При "передаче по ссылке" любые изменения производятся в самих данных. Для коротких, неструктурных данных принято "передавать по ссылке" только те данные, которые нужно менять внутри функции (так, чтобы изменения были видны снаружи). В этом случае передача по ссылке - необходимые накладные расходы, на "разименование указателя" тратятся драгоценные такты.
Во-вторых, если речь идёт о структурах/объединениях/массивах (если передать в функцию массив, то на самом деле она получит указатель на первый элемент). Тут оказывается выгоднее передать указатель, чем запихивать структуру в регистры/стек. Т.е. простое программистское правило: "видишь передаваемый указатель - это точно out-параметер, возможно он ещё и in" в данном случае нарушается. Нужно очень вниматеьно обращатся с переданой таким образом структурой и обязательно (!!!) документировать в комментариях меняет ли функция что-либо, а если да - то какие поля.
Цитата
номер канала действительно желательно знать - ибо call_ch_process вызывает из себя процедуры, обработка в некоторых из них также завязана на контекст канала. Но номер канала планирую хранить как поле структуры , прописывая его при инициализации (ну а там уж передавать - либо номер канала, либо указатель на структуру).

Вся прелесть передачи указателя в том, что номер знать не нужно. Другим процедурам также передаётся указатель. Структуры могут вообще не лежать в массиве и нахождение указателя по номеру может быть нетривиальной задачей. Но она будет выполняться отлько один раз, "на самом верху" обработки.
Цитата
за пример большое спасибо - просто почти не работал со структурами на Си - поэтому некий сумбур в голове.

Это не структуры, это адресная арифметика. К этому нужно привыкнуть, да.
Edmundo
Цитата(SasaTheProgrammer @ Dec 10 2006, 14:35) *
Тут оказывается выгоднее передать указатель, чем запихивать структуру в регистры/стек. Т.е. простое программистское правило: "видишь передаваемый указатель - это точно out-параметер, возможно он ещё и in" в данном случае нарушается. Нужно очень вниматеьно обращатся с переданой таким образом структурой и обязательно (!!!) документировать в комментариях меняет ли функция что-либо, а если да - то какие поля.

Вопроса о передаче структуры "по значению" не стояло. Прочитайте вопрос внимательнее:
Цитата(Doka @ Dec 10 2006, 11:52) *
... адрес указателя на структуру, а не указатель на структуру ...


Цитата(SasaTheProgrammer @ Dec 10 2006, 14:35) *
Вся прелесть передачи указателя в том, что номер знать не нужно.

Нужно ли знать номер канала -- зависит от конкретной задачи. Передать его внутри структуры -- вполне красивое решение.
Doka
Цитата(SasaTheProgrammer @ Dec 10 2006, 14:35) *
Указатель передают в двух случаях. Во-первых, есть существеноое различие, для которого применяют специальные термины - "передача по ссылке"/"передача по значению". При "передаче по значению" функция получает свою копию данных, с которой можно делать всё что угодно. При "передаче по ссылке" любые изменения производятся в самих данных. Для коротких, неструктурных данных принято "передавать по ссылке" только те данные, которые нужно менять внутри функции (так, чтобы изменения были видны снаружи). В этом случае передача по ссылке - необходимые накладные расходы, на "разименование указателя" тратятся драгоценные такты.
ну в ЦОС вовсе они не драгоценные - там "драгоценные" тратятся "на что надо")) т.е. по сравнению с тем, сколько кушают сами циклы ЦОС - оверхед при вызове функций и запихивании в стек параметров - это мизер. По кр.мере по тактам. А вот памяти никогда не бывает много - по кр.мере выбором того или иного способа передачи параметров код можно было бы немного "ужать".

Цитата(SasaTheProgrammer @ Dec 10 2006, 14:35) *
Во-вторых, если речь идёт о структурах/объединениях/массивах (если передать в функцию массив, то на самом деле она получит указатель на первый элемент). Тут оказывается выгоднее передать указатель, чем запихивать структуру в регистры/стек. Т.е. простое программистское правило: "видишь передаваемый указатель - это точно out-параметер, возможно он ещё и in" в данном случае нарушается. Нужно очень вниматеьно обращатся с переданой таким образом структурой и обязательно (!!!) документировать в комментариях меняет ли функция что-либо, а если да - то какие поля.
да . с этим уже столкнулся - чем дальше растёт исходник - тем сложнее в нем ориентироваться. шас вот курю doxygen manual. надеюсь его использованием закрыть часть проблем с "глобальным" документтированием.
=GM=
To SasaTheProgrammer
1) i8051 конечно, прошу прощения, рука пронесла(:-)

2) >>(у кого ещё есть дп? smile.gif<<
Я имел в виду процессоры Тексас Инструментс, конкретно TMS320F28xx серию, хотя, насколько знаю, у всех современных тексасов есть дп-адресация. А вот у i8051, по-моему, нет такой адресации, да и разбиения на страницы тоже нет, разбиение было в i8048, но опять же только в смысле программного счетчика.
SasaTheProgrammer
Цитата(Doka @ Dec 10 2006, 17:30) *
Цитата(SasaTheProgrammer @ Dec 10 2006, 14:35) *
на "разименование указателя" тратятся драгоценные такты.
ну в ЦОС вовсе они не драгоценные - там "драгоценные" тратятся "на что надо")) т.е. по сравнению с тем, сколько кушают сами циклы ЦОС - оверхед при вызове функций и запихивании в стек параметров - это мизер. По кр.мере по тактам. А вот памяти никогда не бывает много - по кр.мере выбором того или иного способа передачи параметров код можно было бы немного "ужать".

"Случаи разные бываают" smile.gif . У нас - с точностью до наоборот. Памяти хватает и с запасом, а вот замена
Код
ptr1->ptr2->s++;
if (ptr1->ptr2->s2 != ptr1->s3)
/* и т.д. */

на
Код
struct S2 *ptrS2 = ptr1->ptr2;
ptrS2->s++;
if (ptrS2->s2 != ptr1->s3)
/* и т.д. */

даёт очень заметный выиграш. Впрочем, компилятор тоже небезгрешен smile.gif .


Цитата(=GM= @ Dec 11 2006, 14:27) *
To SasaTheProgrammer
1) i8051 конечно, прошу прощения, рука пронесла(:-)

Да я тоже как-то не обратил на это внимания и в результате... понял правильно smile.gif
Цитата
2) >>(у кого ещё есть дп? smile.gif<<
Я имел в виду процессоры Тексас Инструментс, конкретно TMS320F28xx серию, хотя, насколько знаю, у всех современных тексасов есть дп-адресация. А вот у i8051, по-моему, нет такой адресации, да и разбиения на страницы тоже нет, разбиение было в i8048, но опять же только в смысле программного счетчика.

У 8051 был один единственный 16-разрядный регистр, через который можно было адресовать "большую" память. А про тексасы я просто не курсе. Теперь буду знать!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.