Перейти к содержанию
    

Keil: оптимизация -O3 + -Ot ломает логику программы

11 минут назад, Forger сказал:

Ксатати -o1, 02 и -o3 и др уже мало отличаются, честно говоря, особой разницы по производительности (загрузка задач в %) тоже не заметил, равно как и по объему кода и озу.

В IAR-е тоже - "Medium" и "High" -уровни по производительности/объёму тоже мало отличаются. Но "Medium" часто уже позволяет отлаживать. Так что для "Debug" по дефолту ставлю его, и только в сложных случаях для конкретного файла включаю более низкий уровень оптимизации. Да и бОльший уровень оптимизации лучше "перетряхивает" код, а значит - проявляет некоторые скрытые баги.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, jcxz said:

 Да и бОльший уровень оптимизации лучше "перетряхивает" код, а значит - проявляет некоторые скрытые баги.

В идеале код должен одинаково хорошо работать на любой степени оптимизации.

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

Например, без оптимизации кода требования к стекам значительно возрастают, и потому, если ставить размеры стеков, которых хватает в максимальной оптимизации, в DEBUGе этот же код естественно будет намертво виснуть.

 

Quote

Так что для "Debug" по дефолту ставлю его

Аналогично и в keil: -o1 вполне хватает для DEBUG сборки.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Forger сказал:

Например, без оптимизации кода требования к стекам значительно возрастают, и потому, если ставить размеры стеков, которых хватает в максимальной оптимизации, в DEBUGе этот же код естественно будет намертво виснуть.

По моим наблюдениям как раз наоборот: С увеличением уровня оптимизации глубина использования стека увеличивается. Что логично.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

19 minutes ago, jcxz said:

Что логично. 

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

 

без оптимизации (-o0):

o0.thumb.jpg.d8dd723abe2ee8a00eba17c956c45ba9.jpg

 

 

с оптимизацией (-o1):

o1.thumb.jpg.f4e07de3a35f1e7bdebd6ba80abe1c87.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, Forger сказал:

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

Ну как, вот например - есть статические переменные i1, i2 (в ОЗУ); работаем с i1:

LDR R0, =i1  ;грузим адрес i1 в R0

LDR R1, [R0] ;грузим i1 в R1

;здесь используем загруженное значение i1

;далее - работаем с i2:

LDR R0, =i2  ;грузим адрес i2 в R0

LDR R1, [R0]  ;грузим i2 в R1

;далее - опять нужно работать с i1

Так вот - код без оптимизации опять загрузит её адрес (так как не сохранил адрес от предыдущего использования):

LDR R0, =i1  ;грузим адрес i1 в R0

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

Также например - если у вас есть выражение скажем: ((i1 & 127)*7+122) где-то в начале функции, и в конце. И между ними i1 не меняется. Без оптимизации компилятор дважды его вычислит, а с оптимизацией - вычислит раз, сохранит во временной переменной и в конце функции использует снова (оптимизация по скорости или балансная). На это опять нужен доп.регистр или стек. А значит опять - с оптимизацией стека потребуется больше.

 

PS: А насчёт ваших примеров - попробуйте максимальную оптимизацию - под IAR увеличение расхода стека происходит при переходе с -o1 на -o2 или -o3.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13 minutes ago, jcxz said:

...

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

 

Выше я показал цифры из одного и того же проекта в одинаковых условиях, кроме оптимизации.

У меня практически все проекты написано по аналогичной схеме (на плюсах).

Оперирую теми данными, которые имею. Эти данные получены штатными средствами.

 

14 minutes ago, jcxz said:

попробуйте максимальную оптимизацию - под IAR увеличение расхода стека происходит при переходе с -o1 на -o2 или -o3.

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

Чтобы обойти, нужно ковыряться в коде ARM, лень ))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 минут назад, Forger сказал:

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

Выше я показал цифры из одного и того же проекта в одинаковых условиях, кроме оптимизации.

То что с оптимизацией стека должно требоваться больше. По логике.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 minutes ago, jcxz said:

То что с оптимизацией стека должно требоваться больше. По логике.

Наверно, это сильно зависит от кода.

У меня в проектах довольно много мелких функций (методов), порой довольно глубокое дерево вызовов, лямбда-выражения есть. Может именно поэтому после оптимизации получается такой результат.

Допускаю, что в другом коде, может быть обратная ситуация.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

21 минуту назад, Forger сказал:

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

Вы смотрите не там где надо. Смотреть нужно листинги. Взял один из своих проектов, debug -o1:

Спойлер

   Maximum stack usage in bytes:                                                  
                                                                                  
   .cstack Function                                                               
   ------- --------                                                               
72   JMethodSntp(CbData *, void const *, void *)                                  
  72   -> CbData::WriteStr1(char const *, uint)                                   
  72   -> JbReadSrvPort(void const *, char *)                                     
  72   -> StampAdd(void const *, uint)                                            
  72   -> __aeabi_memcpy                                                          
16   JMethodTemp(CbData *, void const *, void *)                                  
  16   -> CbData::WriteFNum(void const *, uint)                                   
  16   -> CbData::WriteStr1(char const *, uint)                                   
  16   -> GetStrByIxZ(char const *, int)                                          
24   JMethodTermoCalibr(CbData *, void const *, void *)                           
  24   -> CbData::WriteINum(void const *, uint)                                   
  24   -> JbReadINum(void const *, uint, void *)                                  
  24   -> StampAdd(void const *, uint)                                            
 8   JMethodTermoCurve(CbData *, void const *, void *)                            
   8   -> JMethodTermoReduceCurve(CbData *, void const *, CfgTermo::Adc *, int)   
 8   JMethodTermoReduce(CbData *, void const *, void *)                           
   8   -> JMethodTermoReduceCurve(CbData *, void const *, CfgTermo::Adc *, int)   
48   JMethodTermoReduceCurve(CbData *, void const *, CfgTermo::Adc *, int)        
  48   -> CbData::BoxClose()                                                      
  48   -> CbData::BoxOpen(uint, uint)                                             
  48   -> CbData::WriteFNum(void const *, uint)                                   
  48   -> CbData::WriteINum(void const *, uint)                                   
  48   -> CfgTermo::Common::TestReduce() const                                    
  48   -> JbReadFNum(void const *, uint, void *)                                  
  48   -> JbReadINum(void const *, uint, void *)                                  
  48   -> StampAdd(void const *, uint)                                            
  48   -> __iar_FDtest                                                            
16   JMethodTitle(CbData *, void const *, void *)                                 
  16   -> CbData::WriteStr1(char const *, uint)                                   
  16   -> JbReadStr1(void const *, char *, uint)                                  
  16   -> Ltrim(char const *, uint)                                               
  16   -> Rtrim(char const *, uint)                                               
  16   -> StampAdd(void const *, uint)                                            
  16   -> __aeabi_memmove                                                         
  16   -> __aeabi_memset

 

release -o3:

Спойлер

   Maximum stack usage in bytes:                                                  
                                                                                  
   .cstack Function                                                               
   ------- --------                                                               
80   JMethodSntp(CbData *, void const *, void *)                                  
  80   -> JApiSaveFault                                                           
  80   -> ParseSrvPort(char const *, uint)                                        
  80   -> StampAdd(void const *, uint)                                            
  80   -> __aeabi_memcpy                                                          
  80   -> __aeabi_memmove                                                         
16   JMethodTemp(CbData *, void const *, void *)                                  
  16   -> CbData::WriteFNum(void const *, uint)                                   
  16   -> CbData::WriteStr1(char const *, uint)                                   
  16   -> GetStrByIxZ(char const *, int)                                          
48   JMethodTermoCalibr(CbData *, void const *, void *)                           
  48   -> JApiSaveFault                                                           
  48   -> StampAdd(void const *, uint)                                            
  48   -> __aeabi_memcpy                                                          
 0   JMethodTermoCurve(CbData *, void const *, void *)                            
   0   -> JMethodTermoReduceCurve(CbData *, void const *, CfgTermo::Adc *, int)   
 0   JMethodTermoReduce(CbData *, void const *, void *)                           
   0   -> JMethodTermoReduceCurve(CbData *, void const *, CfgTermo::Adc *, int)   
72   JMethodTermoReduceCurve(CbData *, void const *, CfgTermo::Adc *, int)        
  72   -> CbData::BoxClose()                                                      
  72   -> CbData::BoxOpen(uint, uint)                                             
  72   -> CbData::WriteFNum(void const *, uint)                                   
  72   -> CfgTermo::Common::TestReduce() const                                    
  72   -> JApiSaveFault                                                           
  72   -> StampAdd(void const *, uint)                                            
  72   -> __aeabi_l2f                                                             
  72   -> __aeabi_memcpy                                                          
  72   -> __aeabi_memset                                                          
  72   -> __iar_FDtest                                                            
  72   -> powf                                                                    
24   JMethodTitle(CbData *, void const *, void *)                                 
   0   -> CbData::WriteStr1(char const *, uint)                                   
  24   -> Ltrim(char const *, uint)                                               
  24   -> Rtrim(char const *, uint)                                               
   0   -> StampAdd(void const *, uint)                                            
  24   -> __aeabi_memclr                                                          
  24   -> __aeabi_memmove                                                         

 

Конечно я выбрал участки с наибольшими различиями, но всё же....

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 minutes ago, jcxz said:

Вы смотрите не там где надо.

А вот как раз там где надо.

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

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

То же самое, кстати, касается стека прерываний: заметил, что без оптимизации он также больше заполняется, чем в сравнении с небольшой оптимизацией.

 

Как и говорил, тут сильно зависит от кода и его построения. Очевидно, что очень сильно ))

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

27 минут назад, Forger сказал:

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

В листинге показан максимальный размер стека используемый функцией. Какая разница как она будет вызываться - косвенно или нет? Количество регистров в PUSH от этого не изменится.

И какая разница эта функция - делегат или что-то ещё? Для компилятора это обычная функция, на входе в которую стек занимается на выходе - освобождается.

Так что сравнивать 2 одинаковые функции скомпилённые с разными ключами - это и есть правильно.

А в реалтайм вы смотрите какую-то среднюю температуру по больнице: сейчас она столько, а если временнЫе параметры каких-то событий обрабатываемых ПО чуть сдвинутся - будет уже другая.

 

Цитата

Дело в том, что в моем коде это дает гораздо более точный результат, поскольку очень активно использую делегаты (своего рода указатели на функции)

Вы думаете от этого изменится количество регистров в PUSH/POP?  :biggrin:

 

Но если почему-то больше доверяете реал-тайм измерениям, вот мои:

debug:  stack-debug.png.1e34edd8305ea7869e4f654a3b0fae49.png

 

release:  stack-release.png.a2b737806e3bf810ac1b2b6a23e6bc4a.png

 

Во втором синем столбце использовано/свободно стека (32-битных слов) в разных задачах РТОС.  Как нетрудно заметить - в среднем release требует больше стека. "exceptions" - стек исключений, тоже под release больше запачкан.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 часов назад, Arlleex сказал:

Обнаружил, что со сменой компилятора перестали работать переходы по функциям/определениям и т.д.

По нажатию "Go to definition" внизу слева пишется No information available for the selected symbol. Галка "Browse information" в настройках стоит.

Rebuld делали?

Просто убрать все галки на дополнительные файлы, перебилдидь, поставить все галки, перебилдить.

Удалить .uvguix. файлы.

Попробовать ручками удалить папку с всеми промежуточными и дополнительными файлами, а потом сделать build.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

34 minutes ago, jcxz said:

В листинге показан максимальный размер стека используемый функцией. Какая разница как она будет вызываться - косвенно или нет? Количество регистров в PUSH от этого не изменится.

Конечно может изменится, делегат - это по сути указатель ))

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

При старте они вообще все указывают в hardfault, пока не будут проинициализированы в коде ))

 

Quote

И какая разница эта функция - делегат или что-то ещё? Для компилятора это обычная функция, на входе в которую стек занимается на выходе - освобождается.

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

 

Quote

Так что сравнивать 2 одинаковые функции скомпилённые с разными ключами - это и есть правильно.

Я веду сравнение для всего проекта, меня интересует расход стека для всей задачи, а не отдельно взятых функций. Поскольку, если стека не хватит зависнет весь проект.

В данном случае (в моих проектах) без оптимизации стека жрется куда больше, чем с ней. И это хорошо подтверждается измерениями в реальном времени.

 

Не пойму, почему спорите?

Я привел реальные цифры из реального проекта (пока не законченного), замеры сделаны штатными средствами самой среды, никакой самодеятельности.

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13 минут назад, Forger сказал:

Конечно может изменится, делегат - это по сути указатель ))

Каким образом? Каким образом у Вас в runtime могут меняться команды, из которых состоит функция???

Цитата

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

Какая разница где находится указатель на функцию? Если она всё равно вызывается. Вызывается и в debug и в release.

Цитата

Я веду сравнение для всего проекта, меня интересует расход стека для всей задачи, а не отдельно взятых функций. Поскольку, если стека не хватит зависнет весь проект.

А расход стека разве состоит не из расхода отдельно взятых функций? Т.е. - не максимального расхода по самой глубокой ветке?

Цитата

Я привел реальные цифры из реального проекта (пока не законченного), замеры сделаны штатными средствами самой среды, никакой самодеятельности.

Эти замеры (реального времени) - не объективны. Потому что если есть какие-то ветвления, и по разным путям этих ветвлений вызываются разные функции, то будет разница в использовании стека. И чтобы получить максимальный раход - нужно заставить пройтись по всем путям. В ваших измерениях нет никакой достоверной гарантии что это выполнено.

 

PS: Хотя конечно если у Вас даже количество PUSH-ей в функциях меняется в зависимости от того как они вызываются :biggrin::biggrin::biggrin: , тогда да - нет больше вопросов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

20 minutes ago, jcxz said:

Каким образом? Каким образом у Вас в runtime могут меняться команды, из которых состоит функция???

Вы точно прикалываетесь )) 

Я вам про одно, вы мне - про другое.

 

Quote

Какая разница где находится указатель на функцию? Если она всё равно вызывается. Вызывается и в debug и в release.

Вы не понимаете - внутри вызываемой через делегат функции, могу быть другие вызовы. На этапе компиляции компилятор не может правильно выстроить дерево вызовов через такие указатели. Это можно сделать только в runtime.

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

 

Я уже не знаю как еще на пальцах объяснить очевидные вещи ))

 

Quote

 В ваших измерениях нет никакой достоверной гарантии что это выполнено.

Я понимаю, что бодаться будете до последнего :dash3:    Замечено неоднократно ))

В данном случае измерения проводились на одном и том же проекте в абсолютно равных условиях. 

Замерялось максимальное потребление стека. В сравнении очень хорошо видно насколько активно используется стек с оптимизацией или без.

Я это и так знал и сталкивался с зависаниями в debug, когда в release все было тип топ

Просто, сделал этот простой замер, чтобы для себя лично убедиться, что дело в стеках. Поделился итогами. Для некоторых это создало риск смены религиозных взглядов :biggrin:

 

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

 

Тут стоит добавить, что множественные вложенные вызовы компилятор может оптимизировать вообще убрав вызовы/возвраты. Это касается примитивных inline функций. А у меня их тоже хватает ))

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...