Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Размер структур, стек и куча
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
kolobochishe
Пишем программу для нового прибора. Коллега создает вот такие структуры:
Код
typedef struct    {
    REC_HEADER Header;
    WORD NoPT;
    WORD NoPL;
    WORD NoAD;
    WORD StationID;
    WORD ModelID;
    WORD ManufactID;
    char Ident[9];
    char Descript[41];
    WORD StatTime;
    WORD NoFR;
    WORD NoRT;
    WORD NoBL;
    BYTE NoKY;
    BYTE NoPR;
    BYTE UnitsCal[7];
    float Units0dB[7];
    char KeyId[3][5];
    char KeyDescr[3][41];
    BYTE KeyGrade[3];
    float Factor[3];
    BYTE KeyTStamp[3][8];
    float KeyNom[3];
    float RPM[3];
    char KeyNotes[3][241];
    char PrId[12][9];
    char PrDescr[12][41];
    char Units[12][7];
    BYTE PrGrade[12];
    BYTE PrTStamp[12][8];
    float PrNom[12];
    float PrVal[12];
    char PrNotes[12][241];
                } RTN;


и вот в таком вот месте программа "виснет"
Код
void make_TA(void)
{
    BYTE    get_status(BYTE);

    RTN train=TRAIN; // ТУТ ЗАВИСАЕТ


Увеличил размер стека и все заработало. Был 3кБ сделал 10кБ. Правда, из SCRTACHPAD пришлось перенести в L1 из-за размера.
Контроллер Blackfin BF-548.

Теперь вопросы:
1. Как вы контролируете переполнение стека?
2. Какой размер считаете оптимальным?
3. В какой памяти его лучше располагать?
4. Как реально происходит приравнивание структур? Это спрятанное от глаз memcpy? И используется ли при этом динамическое выделение памяти?
5. Мириться ли с тем, что коллега создает такие огромные структуры или надо сменить подход? Можно ли их располагать в функции или лучше глобально?
Сергей Борщ
1) Заполнением стека константой перед запуском программы и проверкой, что наверху стека осталось какое-то количество ячеек с этой незатертой константой. Работает не всегда, но чаще всего работает.
2) Достаточный.
4) Да, memcpy. Нет, куча не используется. А зачем она могла бы при этом понадобиться?
5) Если нужна именно такая структура, то что вы тут сможете поменять?
quarter
при вызове Си-шной функции все параметры, которые получает и возвращает функция, передаются через стек.
если не побоитесь, то можно работать с указателями на эти структуры, тогда лишнего копирования можно избежать.
kolobochishe
Цитата(Сергей Борщ @ Dec 22 2015, 22:39) *
4) Да, memcpy. Нет, куча не используется. А зачем она могла бы при этом понадобиться?


Не знаю. Подумал, может создается копия, а потом копируется для случая memcpy с перекрывающимися областями памяти.

Цитата(Сергей Борщ @ Dec 22 2015, 22:39) *
5) Если нужна именно такая структура, то что вы тут сможете поменять?


Ну как-то сделать свою часть программы такой, чтобы не было таких огромных локальных переменных. Пусть они будут, но не локальными переменными же. Если подойти к этому с размахом то там и мегабайтные маассивы начнут локально объявлять. Так никакого стека не хватит.

А про размер - ну просто из опыта работы. Какой делали максимальным на приборе с МК такого класса и 32-64 МБ ОЗУ
AlexandrY
Цитата(kolobochishe @ Dec 22 2015, 19:08) *
А про размер - ну просто из опыта работы. Какой делали максимальным на приборе с МК такого класса и 32-64 МБ ОЗУ


А что у вас за прибор?
Стек он в дивайсах с реальным временем на вес золота. Ибо организуется в быстрой внутренней RAM, даже TCM RAM.
А все такие структуры сносятся в медленную внешнюю RAM в heap.

Имея такие ресурсы как у вас всегда применяют RTOS. А у развитых RTOS всегда есть механизмы наблюдения за стеком.
Причем в реальном времени.
У меня даже на платах с 128 МБ памяти стек задач никогда не превышал 5 Кб. Если нужно больше, то это уже какие-то проблемы с софтом.
Только когда речь шла о слишком замороченном стороннем софте например задачах клиентских сессий со встроенным WEB сервером стек увеличивал до 10 кб.

А коллегу вашего надо переучивать. Факт.
jcxz
Цитата(kolobochishe @ Dec 22 2015, 22:07) *
1. Как вы контролируете переполнение стека?
2. Какой размер считаете оптимальным?
3. В какой памяти его лучше располагать?
4. Как реально происходит приравнивание структур? Это спрятанное от глаз memcpy? И используется ли при этом динамическое выделение памяти?
5. Мириться ли с тем, что коллега создает такие огромные структуры или надо сменить подход? Можно ли их располагать в функции или лучше глобально?

5. Коллеге за такое в embedded без внешней памяти, надо не просто по рукам бить, а ампутировать их. Лучше не данные, а коллегу расположить где-нить в вынь-программинге.
2. Оптимальных размеров стека нет, есть необходимые.
1. Контроль - заполнением шаблоном.
3. Лучше внутренней, особенно касаемо DSP с его огромными контекстами.
4. Согласно языка си: поэлементно. Распределение памяти и инициализация - вещи суть разные и почти несвязанные.

Цитата(quarter @ Dec 22 2015, 22:55) *
при вызове Си-шной функции все параметры, которые получает и возвращает функция, передаются через стек.

ложь.

Цитата(AlexandrY @ Dec 23 2015, 03:03) *
Стек он в дивайсах с реальным временем на вес золота. Ибо организуется в быстрой внутренней RAM, даже TCM RAM.

В принципе можно и во внешней памяти стек располагать, даже на устройствах где есть жёсткий реалтайм.
Всё зависит от требуемого быстродействия данной задачи ОС и архитектуры CPU.
Если архитектура такова, что при возникновении прерывания контекст сохраняется на другом стеке (как на Cortex) или вообще не сохраняется (как в ARM TDMI), то стек некоей медленной задачи расположенный во внешней ОЗУ никак не затормозит ISR, но может затормозить переключение контекста данной медленной задачи ОС.
Как обстоит дело с сохранением контекста в Блэкфинах я не знаю.
psL
Цитата(kolobochishe @ Dec 22 2015, 19:07) *
Код
void make_TA(void){

    RTN train=TRAIN; // ТУТ ЗАВИСАЕТ


у вас в стеке создается копия статической или глобальной структуры. Лучше использовать указатель
Код
void make_TA(RTN* train){

    train->NoPt = ...;


или ссылку, если это С++
SlavaV
Вообще конструкция тип НовыйОбъект = Объект
говорит создать временный объект типа "тип" (вот он видимо и создаётся на стеке) и скопировать его в НовыйОбъект (именно скопировать, если не перегружен оператор= происходит почленное копирование)
PS это утверждение верно для С++ так как там структуры и классы одна сущность только с разными свойствами по умолчанию

Другого использование стека в приведённом коде я не вижу

PPS все функции работы с памятью memcpy malloc работают с кучей
AlexandrY
Цитата(SlavaV @ Dec 23 2015, 08:52) *
PPS все функции работы с памятью memcpy malloc работают с кучей


А это не закон. Как в программе переопределено так и будет.
Скажем RTOS-ы сразу переопределяют эти функции.
А там у кого как, у кого куча, а у кого и много куч. Но все со своими оригинальными движками.

Кстати не додумал, но коллега действительно мог использовать стек именно для ускорения своей процедуры.
Либо вообще копипастить из среды моделирования один в один. Тогда это оправдано и придираться к нему нельзя.
jcxz
Цитата(SlavaV @ Dec 23 2015, 12:52) *
Вообще конструкция тип НовыйОбъект = Объект
говорит создать временный объект типа "тип" (вот он видимо и создаётся на стеке) и скопировать его в НовыйОбъект (именно скопировать, если не перегружен оператор= происходит почленное копирование)

Не обязательно.
Если TRAIN - имеет тип RTN и перегружен оператор присваивания:
RTN & RTN::operator =(RTN &);
то (имхо) компилятор сделает вызов конструктора по умолчанию RTN::RTN(), а затем для созданного объекта вызовет этот перегруженный оператор присваивания: train(TRAIN)
SlavaV
Цитата(jcxz @ Dec 23 2015, 17:14) *
Не обязательно.
Если TRAIN - имеет тип RTN и перегружен оператор присваивания:
RTN & RTN::operator =(RTN &);
то (имхо) компилятор сделает вызов конструктора по умолчанию RTN::RTN(), а затем для созданного объекта вызовет этот перегруженный оператор присваивания: train(TRAIN)


вот этот объект и создаётся на стеке (вернее в автоматической памяти, а она обычно на стеке)

здесь можно обойтись без стека (автоматической памяти) Вы правильно сказали используя конструктор, но в коде нет этого, да и вопрос был почему не работает, а не как сделать чтоб работало

kolobochishe
Цитата(AlexandrY @ Dec 23 2015, 02:03) *
...
Имея такие ресурсы как у вас всегда применяют RTOS. А у развитых RTOS всегда есть механизмы наблюдения за стеком.
...


Решили не использовать, т.к. задача одна, а библиотека драйверов наличия ОС не требует.

Забыл упомянуть - пишем на СИ без плюсов.

Цитата(AlexandrY @ Dec 23 2015, 11:58) *
...
Кстати не додумал, но коллега действительно мог использовать стек именно для ускорения своей процедуры.
Либо вообще копипастить из среды моделирования один в один. Тогда это оправдано и придираться к нему нельзя.


Не думаю, что для ускорения.
Пишет под WIN32, потом передает мне те части, которые платформо-независимы
Сергей Борщ
Цитата(psL @ Dec 23 2015, 09:44) *
у вас в стеке создается копия статической или глобальной структуры. Лучше использовать указатель
Кому лучше? Если там создается копия - наверное программисту нужна именно копия, он хочет ее менять, а содержимое оригинала потом потребуется в другом месте. А если можно работать с содержимым оригинальной струкьтуры, то и в этом случае указатель не нужен - можно работать прямо с самой структурой.
Цитата(SlavaV @ Dec 23 2015, 09:52) *
PPS все функции работы с памятью memcpy malloc работают с кучей
Каким местом memcpy связан с кучей?
Цитата(SlavaV @ Dec 23 2015, 11:29) *
Вы правильно сказали - используя конструктор,
Судя по "typedef struct" там голый Си, без плюсов.
AlexandrY
Цитата(kolobochishe @ Dec 23 2015, 11:08) *
Не думаю, что для ускорения.
Пишет под WIN32, потом передает мне те части, которые платформо-независимы


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

Вы портируете эти куски, вы и отвечаете за стек.

Просто портировать лень, да?
jcxz
Цитата(Сергей Борщ @ Dec 23 2015, 15:10) *
Судя по "typedef struct" там голый Си, без плюсов.

Никто не мешает использовать "typedef struct" под с++.

Цитата(AlexandrY @ Dec 23 2015, 15:21) *
В Win32 он может писать как ему вздумается, и ничего ему за это не будет.
Вы портируете эти куски, вы и отвечаете за стек.

Именно так, согласен. Я сразу и написал, что это виндузятный подход, а не ембеддед.
psL
Цитата(Сергей Борщ @ Dec 23 2015, 12:10) *
Кому лучше? Если там создается копия - наверное программисту нужна именно копия

Стеку лучше. Какой же вы, однако, телепат )
kolobochishe
Цитата(AlexandrY @ Dec 23 2015, 14:21) *
Ну так бы сразу и сказали.
А то вводите публику в заблуждение.
В Win32 он может писать как ему вздумается, и ничего ему за это не будет.

Вы портируете эти куски, вы и отвечаете за стек.

Просто портировать лень, да?


Лень конечно rolleyes.gif зачем делать лишнюю работу и разбираться в дебрях чужой программы, если можно договориться об общих правилах. Если мы делаем сейчас опытную версию и мне выдают постоянно новые версии файлов, то я просто не успею еще чем-то заниматься, кроме постоянной переработки чужой программы. И все вопросы после такой переработки уже будут ко мне. Делаем так, потому что VisualDSP денег стоит и рабочих мест с ней у нас 2. У меня, другого программиста, который пишет измерительную часть и вот еще одно WIN-рабочее место для того, чтобы оболочку писать, UI. В общем вся работа отлажена, но вот есть некоторые нестыковки про которые я и спрашиваю, потому что опыта не так много. Также эти файлы используются для эмуляции работы прибора на ПК.
ASN
jcxz
Как-то работали с профессиональным программистом: много серийных изделий (в том числе и для европейских заказчиков).
Так он для DSP (TMS320C55x), где внутренняя память - ценный ресурс, выделял под стек половину внутренней памяти и размещал там просто огромные массивы и структуры.
Меня это удивило и поинтересовался зачем так делают?
Оказалось, что стек используется как куча с автоматическим освобождением. Типа стековой машины. Для данных, которым нужен быстрый доступ, оказалось очень удобно - нет лишнего копирования из внешней памяти во внутреннюю.
Правда, структуры были достаточно "хитрые" - с выравниванием по границе массивов.
jcxz
Цитата(ASN @ Dec 23 2015, 17:27) *
Как-то работали с профессиональным программистом: много серийных изделий (в том числе и для европейских заказчиков).
Так он для DSP (TMS320C55x), где внутренняя память - ценный ресурс, выделял под стек половину внутренней памяти и размещал там просто огромные массивы и структуры.

Так на C55xx нету флешь, соответственно оставшейся половины хватало на код и всё прочее??? Очевидно программа была достаточно простая.
"Европейскость" программиста не говорит о его профессионализме wink.gif Повидал и попеределывал достаточно кода после в том числе и "европейских" программистов...

Цитата(ASN @ Dec 23 2015, 17:27) *
Меня это удивило и поинтересовался зачем так делают?
Оказалось, что стек используется как куча с автоматическим освобождением. Типа стековой машины. Для данных, которым нужен быстрый доступ, оказалось очень удобно - нет лишнего копирования из внешней памяти во внутреннюю.
Правда, структуры были достаточно "хитрые" - с выравниванием по границе массивов.

А зачем в ембеддед устройствах куча? Зачем в автономном устройстве, в котором нет ОС, запускающей различные чужие, заранее неизвестные задачи выделять и освобождать память???
За более чем десяток лет работы в этой сфере и множество разработанных устройств, я ни разу не сталкивался с необходимостью кучи (под линух не писал). Большинство моих проектов содержат десятки тысяч строк кода.
Что мешало этому самому программисту объявить все эти "хитрые" структуры static?
Если на разных этапах работы ПО эта память используется для разных структур, то что мешает для каждого этапа объявить свою структуру и объединить их в union???

А для C55xx такой подход тем более ущербный, если вспомнить, что память в этих ядрах разбита на смежные блоки, каждый из которых имеет отдельную шину данных.
Соответственно - если например необходима обработка двух массивов одновременно (проход КИХ-фильтра например), то расположив их в разных блоках памяти, можно было добиться большей скорости работы кода, убирания stall-ов доступа к памяти.
Если например у Вас в один массив DMA пишет сэмплы принимаемые с SPI, в другом массиве - коэффициенты фильтра, в третьем - выходные данные фильтра и Вам надо обработать это КИХ-фильтром высокого порядка как можно быстрее (желательно по два порядка фильтра за такт CPU или по два сэмпла за такт), то Вам нужно все эти три массива расположить желательно в 3-х разных блоках ОЗУ. А код чтоб был в 4-м блоке.
Как Вы это сделаете зафигачив стек на полпамяти??? Как Вы в стеке будете определять где закончился один блок памяти и начался другой?
А вот разложить эти массивы определив их в static и назначив каждому свой блок посредством компоновщика - всё красиво и аккуратно и работать будет на максимальной скорости и без stall-ов.

На C55xx у меня практически весь тяжёлый код сигнальной обработки использовал все шины CPU по максимуму, за счёт раскидывания рабочих массивов по разным блокам ОЗУ.
И я немало повидал говнокода из DSPLIB C55xx, который на простых алгоритмах использовал ядро C55xx всего процентов на 30 его возможностей. И попереписывал его.
sigmaN
Кажется такие вопросы решаются выработкой спецификации взаимодействия ПК с устройством, после чего каждый может заниматься своим делом.
На лицо проблема проектирования. ИМХО, с таким подходом как сейчас, вы намучаетесь гораздо больше, чем если бы сделали все "правильно" и отделили мух от котлет(эмбеддед от вин32).
Для симуляции работы девайса на ПК делается наоборот - в эмбеддед код в нужных местах ставятся заглушки и все это весело запускается на ПК и начинает делать ввод/вывод в файлы и т.д. Нужно визулизировать какую-нибудь панель с тумблерами или датчиками - не вопрос, пишется для этого морда и опять же данные через файлы(/пайпы/бог знает как еще), поступают в эмбеддед код и результаты выводятся аналогично. Для того чтобы подмена нужных частей происходила без мучений - нужна продуманная архитектура. Тут мы приходим к основам основ... Для начала гуглить less coupling more cohesion.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.