реклама на сайте
подробности

 
 
3 страниц V   1 2 3 >  
Reply to this topicStart new topic
> Лучшее - враг хорошего?, Глюки при оптимизации компилятора KEIL
KnightIgor
сообщение May 30 2012, 08:30
Сообщение #1


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Привет, коллеги.

Следующая предыстория. Есть работающая, но и далее развиваемая программа на STM32F103RC на собственном железе. В ней много всего: поддержка FlashFS на microSD через SPI и на EEPROM через I2C, комбинированное USB устройство (HID/CDC/MassStorage), два радиомодуля на CC1101, и т.п. В какой-то момент код созрел до такой степени, что устаканившуюся часть его я решил "слить" в библиотеку, чтобы исключить длительные перекомпиляции исходников. Для этого создал новый проект, который собирает библиотеку, а основной проект её включает.

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

Возникают вопросы:
1. Какой уровень оптимизации у KEIL по-умолчанию?
2. Глюки программы связаны с ошибками компилятора на высоких уровнях оптимизации?
3. Мой код имеет синтаксические недостатки, которые проявляются лишь при сильной оптимизации (например, нужно ли писать для переменных static volatile или достаточно static)?
4. Можно ли "смешивать" в результирующем коде различные уровни оптимизации?
5. Как откомпилированы в смысле оптимизации библиотеки RL от KEIL?

Каков ваш опыт, коллеги?

Сообщение отредактировал KnightIgor - May 30 2012, 08:32
Go to the top of the page
 
+Quote Post
SII
сообщение May 30 2012, 09:15
Сообщение #2


Знающий
****

Группа: Свой
Сообщений: 549
Регистрация: 13-07-10
Из: Солнечногорск-7
Пользователь №: 58 414



На Си, вообще говоря, не пишу, хотя кой-какие вещички по-быстрому проверял в Кейле. Volatile указывать обязательно, если переменная может быть изменена независимо от воли кода (ну или Вашим же кодом, но так, что транслятор отследить изменения не в состоянии -- например, меняете в обработчике прерывания, а используете в основной части программы). Вот при -O0 отсутствие volatile вряд ли скажется: скорей всего, компилятор тогда не пытается запоминать, какие значения он раньше грузил в регистры, и просто каждый раз заново обращается к памяти (что, собственно, и должен делать для volatile-переменных).
Go to the top of the page
 
+Quote Post
aaarrr
сообщение May 30 2012, 09:35
Сообщение #3


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



1. -O2
2. Нет
3. Возможно. Вопрос в скобках совершенно непонятен - это все равно что спросить, нужно ли объявлять переменную как volatile int, или достаточно int.
4. Можно

Описываемое поведение больше похоже на проблемы со стеком.
Go to the top of the page
 
+Quote Post
kovigor
сообщение May 30 2012, 09:41
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 5 273
Регистрация: 30-03-10
Пользователь №: 56 295



Цитата(KnightIgor @ May 30 2012, 11:30) *
Все пошло. И тут начались неуловимые глюки и странности. Вроде все работает, но иногда файловая система сбоит - например, вдруг носитель якобы не содержит файлов. В некоторых условиях при интенсивном обмене по радиоканалам устройство может сброситься, и т.п. Начинаю скрупулезно сравнивать проекты и вдруг натыкаюсь на факт, что мой "библиотечный" проект имеет в опциях уровень оптимизации "по умолчанию". Так как основной проект собирается с -O0 (пока отлаживаюсь, оптимизация выключена, да и память не вышла вся еще), я включил -O0 и для библиотеки и... всё заработало без проблем.
...
Каков ваш опыт, коллеги?


Были подобные проблемы, когда не хватало быстродействия МК. В моем случае проблема решилась перепрограммированием PLL для получения макс. допустимой для LPC214x тактовой частоты (60 МГц) ...
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение May 30 2012, 11:24
Сообщение #5


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(aaarrr @ May 30 2012, 11:35) *
3. Возможно. Вопрос в скобках совершенно непонятен - это все равно что спросить, нужно ли объявлять переменную как volatile int, или достаточно int.

Описываемое поведение больше похоже на проблемы со стеком.

Пусть есть "глобальная" переменная, через которую передаются состояния между участками кода из разных *.C модулей или же переменная внутри одного *.С модуля, через которую "общаются" процедуры внутри его. Обязательно надо volatile [static] <type>?! Вряд ли, т.к. этот модификатор не встречается в общедоступном известном коде как Chan FatFS или contiki, которые, как известно, проверенно работают.

Стек вряд ли, т.к. я не выделяю ограниченую область памяти для него (путем startup_xxxx.s), а, подобно DOS wink.gif , ставлю его на самый верх имеющейся RAM. В моем проекте при 48кБ RAM используется около половины, а остальное сверху - стек. Если это действительно проблема стека, то такое должно означать, что оптимизация требует его больше, что противоречило бы идее.

Что может еще быть? Гипотеза - при вызове функции изнутри другой функции и передаче ей в качестве параметра ссылки на локальную переменную, которая как правило размещена в стеке, для "высоких" уровней оптимизации такая переменная может быть ошибочно размещена в регистре, и ссылаться будет не на что. Это был бы явно глюк компилятора.

Цитата(kovigor @ May 30 2012, 11:41) *
Были подобные проблемы, когда не хватало быстродействия МК. В моем случае проблема решилась перепрограммированием PLL для получения макс. допустимой для LPC214x тактовой частоты (60 МГц) ...

То есть, оптимизированный код работает МЕДЛЕНЕЕ, и ему нужен более производительный процессор?
Кстати, подумал о программых задержках (типа for {i = 10000; i; i--}), которые, напротив, могут оказаться слишком быстыми с оптимизацией, и недожидаться чего-либо. В моем коде я их не использую (а пользую DWT, дающий абсолютное время).
Go to the top of the page
 
+Quote Post
aaarrr
сообщение May 30 2012, 11:34
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(KnightIgor @ May 30 2012, 15:17) *
Пусть есть "глобальная" переменная, через которую передаются состояния между участками кода из разных *.C модулей или же переменная внутри одного *.С модуля, через которую "общаются" процедуры внутри его. Обязательно надо volatile [static] <type>?! Вряд ли, т.к. этот модификатор не встречается в общедоступном известном коде как Chan FatFS или contiki, которые, как известно, проверенно работают.

Нет, не нужно.

Цитата(KnightIgor @ May 30 2012, 15:17) *
Стек вряд ли, т.к. я не выделяю ограниченую область памяти для него (путем startup_xxxx.s), а, подобно DOS wink.gif , ставлю его на самый верх имеющейся RAM. В моем проекте при 48кБ RAM используется около половины, а остальное сверху - стек. Если это действительно проблема стека, то такое должно означать, что оптимизация требует его больше, что противоречило бы идее.

Ну, если стек вне подозрений, остаются еще выходы за границы массивов, несоответствующие действительности прототипы функций и т.п.
После изменения проекта поменялось расположение данных в памяти, вот ошибки и вылезли.

Цитата(KnightIgor @ May 30 2012, 15:17) *
Что может еще быть? Гипотеза - при вызове функции изнутри другой функции и передаче ей в качестве параметра ссылки на локальную переменную, которая как правило размещена в стеке, для "высоких" уровней оптимизации такая переменная может быть ошибочно размещена в регистре, и ссылаться будет не на что. Это был бы явно глюк компилятора.

Поверьте, таких глюков точно нет.
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение May 30 2012, 11:49
Сообщение #7


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(aaarrr @ May 30 2012, 13:34) *
Нет, не нужно.

Тоже в этом уверен.

Цитата
Ну, если стек вне подозрений, остаются еще выходы за границы массивов, несоответствующие действительности прототипы функций и т.п.
После изменения проекта поменялось расположение данных в памяти, вот ошибки и вылезли.

Проект находится на активной фазе развития, меняется много чего, что наверняка приводит к постоянному перемещению кода. Тем не менее все (с -О0) работает.

Цитата
Поверьте, таких глюков точно нет.

Я бы не стал это столь категорично утверждать. Хотя бы по закону Мерфи: "Любая работающая программа содержит по крайней мере одну ошибку" со следствием "Безошибочные программы неработоспособны" wink.gif .
Go to the top of the page
 
+Quote Post
aaarrr
сообщение May 30 2012, 12:04
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(KnightIgor @ May 30 2012, 15:49) *
Я бы не стал это столь категорично утверждать. Хотя бы по закону Мерфи: "Любая работающая программа содержит по крайней мере одну ошибку" со следствием "Безошибочные программы неработоспособны" wink.gif .

Просто слишком очевидная была бы ошибка, чтобы ее не изловили в зародыше. Посмотрите release notes - ошибки, которые исправляются от релиза к релизу, носят весьма сложный характер, просто так не наступишь.
Свои программы всегда собираю исключительно с опцией -O3. На ошибку компилятора в этой связи наступил, помнится, только однажды.
Go to the top of the page
 
+Quote Post
vlad_new
сообщение May 30 2012, 12:07
Сообщение #9


Местный
***

Группа: Участник
Сообщений: 218
Регистрация: 24-06-10
Пользователь №: 58 127



1. Уровень по default 0
2. Вопрос конечно интересный. Были у меня проблеммы с массивами. К примеру так: volatile char Mas[100]; В некоторых местах, при оптимизации, где
стояло выражение: Mas[0]=0; компилятор заменял его регистром. Хотя по всем кононам не имел такого права. Лечилось либо холостым чтением:
tmp=Mas[0]; либо заводил указатель на массив, либо просто через промежуточную переменную static. Причем в примитивных примерчиках такой эффект воссаздать не удавалось. Только в большом проекте, где массив используется сразу в кучу местах.
Хотя может в последних версиях KEILа уже что то и подправили.
3. В локальных только static. Если будет volatile static, то KEIL все проигнорирует и выделет либо регистр либо кусок стека. Покрайне мере так было в ранних версиях.
4. Я смешиваю.
5. Не знаю.
Go to the top of the page
 
+Quote Post
_Артём_
сообщение May 30 2012, 12:25
Сообщение #10


Гуру
******

Группа: Свой
Сообщений: 2 128
Регистрация: 21-05-06
Пользователь №: 17 322



Цитата(vlad_new @ May 30 2012, 15:07) *
3. В локальных только static.


А что просто написать
Код
uint8_t tmp;
нельзя?

Обязательно писать
Код
static uint8_t tmp;
?
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение May 30 2012, 12:49
Сообщение #11


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(_Артём_ @ May 30 2012, 14:25) *
А что просто написать
Код
uint8_t tmp;
нельзя?

Обязательно писать
Код
static uint8_t tmp;
?

Речь также о "видимости" переменной между модулями. static видна только в своем модуле. Это означает, что в других можно использовать то же имя, если хочется. Не объявленная как static будет глобальной и "видна" и доступна всем модулям программы. Могут случиться казусы.

Цитата(vlad_new @ May 30 2012, 14:07) *
1. Уровень по default 0

Таки -O2. Это видно в строке ключей в итоге, как я обнаружил только что.

Цитата
2. Вопрос конечно интересный. Были у меня проблеммы с массивами.
Хотя может в последних версиях KEILа уже что то и подправили.

Вот именно на это я и "грешу". Похоже, что массивы (буферы) ведут себя странно.
Go to the top of the page
 
+Quote Post
aaarrr
сообщение May 30 2012, 13:07
Сообщение #12


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(KnightIgor @ May 30 2012, 16:49) *
Вот именно на это я и "грешу". Похоже, что массивы (буферы) ведут себя странно.

Тогда добейтесь сперва стабильного воспроизведения ошибки. Без этого никак.

Вообще, RVCT позволяет себе некоторые "вольности" с volatile (например, выбрасывает пустые циклы вида for(volatile int n = 100; n > 0; --n)), но чтобы до нерабочего состояния довести программу - как-то сомнительно.

Характер ошибок ("иногда что-то ломается") указывает скорее на порчу данных из-за ошибки в программе.
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение May 30 2012, 14:01
Сообщение #13


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(aaarrr @ May 30 2012, 15:07) *
Тогда добейтесь сперва стабильного воспроизведения ошибки. Без этого никак.

Во-во. Именно это не получалось: лишь в одном из 10 случаев происходили какие-то странности...

Цитата
Характер ошибок ("иногда что-то ломается") указывает скорее на порчу данных из-за ошибки в программе.

Я тоже так и понял, потому поддержал "тему" массивов. Ищу...

P.S. Прикол теперь в том, что я вернул установки оптимизации в якобы проблемные, загрузил, а всё работает! Конечно, между делом кое-что развивалось в программе.
Не знаю, радоваться или огорчаться....

Сообщение отредактировал KnightIgor - May 30 2012, 14:15
Go to the top of the page
 
+Quote Post
Albun
сообщение May 30 2012, 18:44
Сообщение #14


Участник
*

Группа: Участник
Сообщений: 22
Регистрация: 21-07-08
Пользователь №: 39 116



Была у меня когда-то тоже похожая проблема с оптимизацией (правда в IAR-е, но может и для KEIL-а это будет тоже) : без оптимизации работало нормально, а с оптимизацией появились хаотические ошибки и зависания. Причем не стабильно - здесь и сейчас - а время от времени, без никакой видимой системы. Но потом после мучительных поисков нашел так-ки причину - у меня не корректно сохранялись регистры в блоке обработки IRQ прерываний. Я не сохранял регистр R12, а как оказалось компилятор этот регистр тоже использует как регистр общего назначения, т.е. в тех процедурах в которых он используется, компилятор не генериует команды на сохранение его при в ходе процедуру и восстановлении его при выходе из процедуры (как это делается с регистрами R4-R11)
Соответственно причина была в том что без оптимизации R12 вообще не использовался компилятором, а оптимизированный код изредка использует R12. И весь хаос получался тогда, когда так стекались обстоятельства, что прерывание возникало в процедуре которая пользовалась R12. Обработчик IRQ в моем коде (MyIrqHandler) тоже компилировался (при оптимизации) с R12. А поскольку компилятор не генерирует код сохранения этого регистра при входе, то при выходе из MyIrqHandler R12 был "сбит", вот и получалось что R12 рушился но происходило это редко и хаотично.
Проверьте, может и у вас та-же проблема.

Код
IRQ_Handler_Entry:
    PUSH {R0,R1}
    MRS R1,CPSR
    BIC R0,R1,#0x1F
    ORR R0,R0,#ARM_MODE_SVC
    MSR CPSR_C,R0
              ...
    PUSH {R1-R3,LR} --> а про R12 я забыл, т.е. потом поправил так:  PUSH {R1-R3,R12,LR} /POP {R1-R3,R12,LR}
                        ^ в зависимости от того какие регистры выше R3 будут использованы в MyIrqHandler, компилятор сам их сохранит и восстановит (кроме R12
    BL MyIrqHandler (MyIrqHandler - процедура на Cи)
    POP {R1-R3,LR}
              ...
    MSR CPSR_C,R1
    POP {R0,R1}
    SUBS PC,LR,#04


Сообщение отредактировал Albun - May 30 2012, 18:46
Go to the top of the page
 
+Quote Post
EvilWrecker
сообщение May 30 2012, 19:13
Сообщение #15


ядовитый комментатор
******

Группа: Свой
Сообщений: 2 765
Регистрация: 25-06-11
Пользователь №: 65 887



Также в свое време потратил немало усилий на отыскания причн кривой работы программы после оптимизации. В результате выявились несколько эмпирических установок, например использовать volatile если есть сомнения по поводу "сохранности" переменных, а также адаптацию механизма try-catch под баг-репорт нужды: обычно это позволяет обойти некоторые проблемы, столь простые что до них нельзя вообще додуматься, хотя и с более сложными тоже неплохо справляется. Ко всему это приплюсовалось вездесущая, я бы сказал занудная работа по пре-структуризации всех мест( по мере возможного) где на ваш взгляд что то может пойти не так( инициализация переменных,указатели,проверка типов и др)- здесь очень полезно довольно большое число директив из С.

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

-
Go to the top of the page
 
+Quote Post

3 страниц V   1 2 3 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 10th July 2025 - 10:37
Рейтинг@Mail.ru


Страница сгенерированна за 0.01498 секунд с 7
ELECTRONIX ©2004-2016