Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Много мелких функций или одна большая?
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
GYUR22
Есть одна большая функция в программе - которую можно поделить на много мелких ...
Вопрос как выгоднее по скорости исполнения - разделить ее на 18 мелких (по смыслу) или оставить как есть?
Сергей Борщ
Если мелкие сделать принудительно встраиваемыми - то по скорости будет одинаково. А читаемость исходника повысится.
MrYuran
Цитата(Сергей Борщ @ Feb 11 2011, 15:46) *
Если мелкие сделать принудительно встраиваемыми - то по скорости будет одинаково. А читаемость исходника повысится.

А если и не делать - может, ничего страшного и не произойдёт.
(какая разница, сколько процессор бездельничает в цикле IDLE - 97 или 98%..)
GYUR22
Эээ про Idle можно поподробнее?

ps эта функция исполняется раз в секунду но - ее исполнение может быть критичным по скорости ибо после нее идет обработка буфера uart (если он получил маркер - передача окончена) и посылка ответа
MrYuran
Цитата(GYUR22 @ Feb 11 2011, 16:20) *
Эээ про Idle можно поподробнее?

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

Естественно, это всё при отсутствии RTOS.
GYUR22
богатая идея...
надо обмуслить...
тока общаяя схема у меня порушится т.к. 1 секундный маркер....
и втом теле еще несколько обработчиков...
MrYuran
Цитата(GYUR22 @ Feb 11 2011, 16:52) *
тока общаяя схема у меня порушится т.к. 1 секундный маркер....

Ничего не порушится.
Маркер пусть запускает процесс.
Вот для затравки:
CODE
void CalculateParameters(void)
{
static int stage = 0;
if( TimeToCalc)
{
switch(stage++)
{
case 0:
Calculate_T();
break;

case 1:
Calculate_V();
break;

case 2:
pH = GetpH(kappa, t);
break;

case 3:
Calculate_pNa(v);
break;

case 4:
Calculate_CNa();
break;

default:
TimeToCalc = 0;
stage = 0;
if(state == NoMeas)
{
state = NormalWork;
}

break;
}
}
}


CalculateParameters() вставляете в общий суперцикл, флажок TimeToCalc запускает процесс и обнуляется в конце.
Хотя, можно его "вынести за скобки"... Вариаций множество.
_Pasha
Цитата(MrYuran @ Feb 11 2011, 17:19) *
Вот для затравки:

И нафига оно такое надо?
Код
#define run_app(name) do{static bool lock=false; if(!lock){ lock=true;name();lock=false;}}while(0);
void idle(void)
{
  run_app(func_app1);
  run_app(func_app1);
  run_app(CalculateParameters);
}
// где-то тама...
void CalculateParameters(void)
{
    
    if(!TimeToCalc) return;
    Calculate_T();
              idle();
    Calculate_V();
              idle();
    pH = GetpH(kappa, t);
              idle();
    Calculate_pNa(v);
              idle();
    Calculate_CNa();                
              idle();
    TimeToCalc = 0;
}


biggrin.gif И где тут Protothreads, который я "при каждом удобном случае пиарю" ? ЫЫЫ!
demiurg_spb
Цитата(Сергей Борщ @ Feb 11 2011, 15:46) *
Если мелкие сделать принудительно встраиваемыми - то по скорости будет одинаково. А читаемость исходника повысится.

Достаточно их сделать статическими и компилятор их сам встроит если они вызываются лишь по разу.
2ТС:
Разбивайте всё по функциям на отдельные законченные логические блоки, чтобы была видна структура и прозрачны взаимосвязи.
Размер кода при этом не увеличиться ни на йоту.
GYUR22
размер кода пока не пугает ...
оперативки уже мало 95% занято (структурой данных и 2мя буферами uart) ... тока на стэк как я понимаю осталось
с ассемблером я практически не знаком - но каждый вызов функции это прыжок по коду - занимающий циклы процессора если я не ошибаюсь
но если написать что она- inline без static то она интегрируется в то место где вызывается?
_Pasha
Цитата(GYUR22 @ Feb 12 2011, 13:40) *
но если написать что она- inline без static то она интегрируется в то место где вызывается?

Static inline делается для того, чтоб описать в хедере - тогда нету разницы, где и сколько раз оно описано. Все равно будет при вызове встроено
demiurg_spb
Цитата(GYUR22 @ Feb 12 2011, 12:40) *
размер кода пока не пугает ...
оперативки уже мало 95% занято (структурой данных и 2мя буферами uart) ... тока на стэк как я понимаю осталось
с ассемблером я практически не знаком - но каждый вызов функции это прыжок по коду - занимающий циклы процессора если я не ошибаюсь
но если написать что она- inline без static то она интегрируется в то место где вызывается?

Не всегда, а по усмотрению компилятора.

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

Использовать квалификатор inline имеет смысл лишь для замены макросов функциями по большей части в заголовочных файлах.

Придерживаясь этих нехитрых правил можно получить не только оптимальный но и самодокументированный код.

Для гарантированного встраивания функции при любых условиях можно использовать особенности различных компиляторов.
В gcc например есть механизм атрибутов:
Код
#define INLINE          __inline__ __attribute__((always_inline))

INLINE void vsegda_inline_func1(void);
INLINE void vsegda_inline_func2(void);
В других компиляторах используют механизм #pragma ...
Альтернатива - вечно зелёные и неувядающие макросы на все времена года:-)
GYUR22
static inline - не компилируется... пишет что линкер не нашел функций...
inline - все нормально работает
в чем подвох?
alexeyv
Цитата
static inline - не компилируется...


1. static inline функцию надо писать в h-файле, который надо подключить (#include) туда, где она будет вызываться

2. Пример написания static inline функции в h-файле (WinAVR)
Код
  static inline void delay_10ms (byte ms)  __attribute__((always_inline));
  void delay_10ms (byte ms)
   {
    for(byte i=0; i<ms; i++)
     {
      wdt_reset ();
      _delay_ms (10);
     }
   }
MrYuran
Цитата(_Pasha @ Feb 11 2011, 18:51) *
biggrin.gif И где тут Protothreads, который я "при каждом удобном случае пиарю" ? ЫЫЫ!

sm.gif
где-то валяется в пыльном углу, все никак руки не дойдут асилить...
laughing.gif
beer.gif

Цитата(alexeyv @ Feb 14 2011, 14:25) *
1. static inline функцию надо писать в h-файле, который надо подключить (#include) туда, где она будет вызываться

Можно и в .c, но только "для внутреннего употребления"
Короче говоря, static объявление действительно только внутри модуля, в котором оно объявлено.
GYUR22
еще раз для тупого...
чем лучше static inline
по сравнению с просто inline?
MrYuran
Цитата(GYUR22 @ Feb 14 2011, 15:28) *
чем лучше static inline
по сравнению с просто inline?

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

ps про поверье это пять...
_Pasha
Цитата(MrYuran @ Feb 14 2011, 15:51) *
Есть поверье, что оптимизируется лучше, т.к. все связи у компилятора на виду и ему не надо подразумевать возможности внешних вызовов

Это надежда, а не поверье. Пока что с оптимизацией как раз "болезни роста"
demiurg_spb
Цитата(GYUR22 @ Feb 14 2011, 16:56) *
меня интересует встроится ли код или нет если оставить просто inline
Встроится или нет.
Я же Вам написал что нужно сделать чтобы всегда встраивалось.
Вы что-то не смогли осознать?

Цитата(_Pasha @ Feb 14 2011, 17:09) *
Пока что с оптимизацией как раз "болезни роста"
Думается что это бесконечная болезнь, во всяком случае хочется в это верить.
sergeeff
Цитата(demiurg_spb @ Feb 14 2011, 18:47) *
Я же Вам написал что нужно сделать чтобы всегда встраивалось.


В общем случае нет гарантированного способа "заставить" компилятор встроить inline функцию. Может встроить, а может - нет. Все пляски с прагмами и атрибутами повышают вероятность встраивания.
GYUR22
C++, C99, and GNU C each have support for inline functions. Different compilers vary in how complex a function they can manage to inline. Mainstream C++ compilers like Microsoft Visual C++ and GCC support an option that lets the compilers automatically inline any suitable function, even those not marked as inline functions.

выдержка из википедии (может конечно врут...) но про static ни слова....
про статик более менее понятно стало из другой статья .....что это локальные функции
но честно говоря все очень запутано.....
_Pasha
Цитата(GYUR22 @ Feb 14 2011, 21:27) *
выдержка из википедии (может конечно врут...) но про static ни слова....
про статик более менее понятно стало из другой статья .....что это локальные функции
но честно говоря все очень запутано.....

А что Вам мешает объявить функцию в разных вариантах и самому понять, что почём? И не мучать людей, не являющихся преподами.
GYUR22
честно говоря не собирался никого мучить, даже тема была другая
просто возникший в процессе вопрос нов и непонятен - и как показало общественное мнение результат негарантирован.

имхо:
Результат в моем случае проверить сложно т.к. размер кода - будет один и тотже -функции используются по разу
а скорость отклика - уарта мне точно померять пока не представляется возможным - точность +/- 1 ms уже не устроит

а вот результат по разбиению исполнения массива данных - был виден и дал выигрыш как раз ~1ms - спасибо за наводку

Всем спасибо дальше как говориться куда кривая выведет sm.gif
sergeeff
Цитата(GYUR22 @ Feb 14 2011, 23:22) *
Всем спасибо дальше как говориться куда кривая выведет



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

Если вы удовлетворены своими inline изысками - отлично. Но вы, наверное, поняли, что это вариант слегка от лукавого. Я к тому, что вы в данном случае не на 100% контролируете ситуацию. А это не есть хорошо.
alexeyv
Цитата
чем лучше static inline
по сравнению с просто inline?


Просто инлайн ф-цию компилятор может вставить и как вызов, если сочтет это более подходящим, а статик инлайн он всегда вставляет как надо. Я это где-то прочитал в документации, не помню где.
demiurg_spb
Цитата(sergeeff @ Feb 14 2011, 20:19) *
В общем случае нет гарантированного способа "заставить" компилятор встроить inline функцию. Может встроить, а может - нет. Все пляски с прагмами и атрибутами повышают вероятность встраивания.
С чего такой пессимизм?
Насколько я знаю если в gcc дал атрибут, так он отрабатывает по полной.
Не встречалось в моей практике исключений (я сейчас про gcc только говорю).
Приведите примерчик кода с листингом для убедительности.

Если разить вашу теорию, то и данные предназначенные для помещения в секции EEPROM (это при помощи атрибутов в gcc происходит) ИНОГДА могут оказаться в другой секции.
Это абсурд!
sergeeff
Цитата(demiurg_spb @ Feb 15 2011, 20:43) *
С чего такой пессимизм?
...
Это абсурд!


Поспокойнее сэр!

Это не моя теория.

Читаем, например, http://msdn.microsoft.com/en-us/library/z8...=vs.80%29.aspx:


Цитата
The insertion (called inline expansion or inlining) occurs only if the compiler's cost/benefit analysis show it to be profitable. Inline expansion alleviates the function-call overhead at the potential cost of larger code size.


чуть ниже:

Цитата
The compiler treats the inline expansion options and keywords as suggestions. There is no guarantee that functions will be inlined. You cannot force the compiler to inline a particular function, even with the __forceinline keyword. When compiling with /clr, the compiler will not inline a function if there are security attributes applied to the function.
Petka
Цитата(sergeeff @ Feb 15 2011, 21:26) *
Поспокойнее сэр!

Это не моя теория.
...

У микрософта сносная документация, но не лучшие компиляторы.
Если по теме, то надо приводить выдержки из "С99".
В gcc насколько помню есть атрибут с говорящим названием always_inline.
На моей практике при "-O3" даже inline в большинстве случаев указывать не надо, само автоматически встроится.

sergeeff
Цитата(Petka @ Feb 15 2011, 21:48) *
У микрософта сносная документация, но не лучшие компиляторы.


Да ладно уж! А на чем написано большинство программ под windows всех мастей? По крайней мере их компиляторы достаточно прогнозируемо себя ведут.
dxp
Само указание ключевого слова inline в декларации функции является подсказкой/предложением компилятору встроить тело функции в точке вызова. Но компилятор имеет полное право сделать по своему усмотрению. Это стандартное поведение.

Некоторые компиляторы вводят расширения (само собой нестандартные) для управления поведением компилятора. Про GCC уже сказали выше, у IAR в документации недвусмысленно сказано:
Цитата
#pragma inline[=forced]

Specifying #pragma inline=forced disables the compiler’s heuristics and forces
inlining
. If the inlining fails for some reason, for example if it cannot be used with the
function type in question (like printf), an error message is emitted.

Т.е. при использовании этой прагмы компилятор обязан встроить функцию. Если он не может по техническим причинам этого реализовать, он должен выдать сообщение об ошибке. Это весьма полезное свойство для embedded области применения.
_Pasha
Что характерно - чем вопрос элементарнее, тем дискуссия более оживленная sm.gif
dxp
bb-offtopic.gif
Цитата(_Pasha @ Feb 16 2011, 12:54) *
Что характерно - чем вопрос элементарнее, тем дискуссия более оживленная sm.gif

А это всё в точности по Паркинсону ("Законы Паркинсона", занятная книжка). Там был на эту тему пример про обсуждение на совете директоров трех вопросов: 1. про приобретение какого-то очень дорогостоящего оборудования (на миллионы); 2. строительство навеса для стоянки велосипедов сотрудников; 3. сколько стаканчиков для кофе класть в кофейный автомат. По первому вопросу затраты времени 10 минут, по второму - минут 40, и по третьему - три часа. Объяснение логичное: чем проще вопрос, тем больше народу в нём разбирается. И у каждого есть мнение. sm.gif

Кстати, вопрос про инлайны не такой простой для начинающих, как может показаться. Инлайновые функции должны удовлетворять ряду требований, и для них делаются определённые послабления. Кроме того, обсуждаемые расширения тоже несут с собой часть нюансов использования. Поэтому я бы не считал обсуждение всего этого ненужным.
defunct
Цитата(GYUR22 @ Feb 14 2011, 22:22) *
имхо:
Результат в моем случае проверить сложно т.к. размер кода - будет один и тотже -функции используются по разу
а скорость отклика - уарта мне точно померять пока не представляется возможным - точность +/- 1 ms уже не устроит

Ну так забейте на inline если оно настолько некритично для вашего проекта.
Кстати inline'нинье функций далеко не гарантия наиболее высокой скорости исполнения. Например, в процессорах с кеш памятью команд - бездумное инлайнинье там и сям приведет только к замедлению за счет раздувания кода при константном объеме кеш памяти.

Цитата(demiurg_spb @ Feb 15 2011, 19:43) *
Если разить вашу теорию, то и данные предназначенные для помещения в секции EEPROM (это при помощи атрибутов в gcc происходит) ИНОГДА могут оказаться в другой секции.
Это абсурд!

обилие прагм и атрибутов в проекте - это говнокод, а не абсурд, и - кстати таки да данные могут оказаться совсем не в той секции где ожидаешь, при переносе такого проекта на другой проц.
demiurg_spb
Цитата(defunct @ Feb 17 2011, 17:44) *
Ну так забейте на inline если оно настолько некритично для вашего проекта.
Кстати inline'нинье функций далеко не гарантия наиболее высокой скорости исполнения. Например, в процессорах с кеш памятью команд - бездумное инлайнинье там и сям приведет только к замедлению за счет раздувания кода при константном объеме кеш памяти.
обилие прагм и атрибутов в проекте - это говнокод, а не абсурд,
Идеологически согласен.
Кстати, Вы видели в моём примере много прагм и атрибутов?
Человек спросил как это сделать - я дал однозначный рецепт.
Потом сюда приплели даже микрософтовский компилятор и кеш инструкций, хотя тема находится в подфоруме микроконтроллеры/AVR
и вопрос касался avr-gcc...

Цитата
и - кстати таки да данные могут оказаться совсем не в той секции где ожидаешь, при переносе такого проекта на другой проц.
Какие Ваши предложения на сей счёт? Отказаться от использования PROGMEM, EEMEM, ISR и вообще не включать компьютер?
Атрибуты в gcc - это почти что основополагающий механизм в случае кросс-компиляции для avr.
Если Вы внимательно читали мои посты, то могли бы подметить, что я рекомендовал оставить лишь квалификатор static, ибо это и необходимо и достаточно для локальных функций.
sergeeff
Цитата(demiurg_spb @ Feb 17 2011, 22:18) *
... я рекомендовал оставить лишь квалификатор static, ибо это и необходимо и достаточно для локальных функций.



Да static для локальных функций это просто "хороший тон", чтобы не "заси...ть" пространство имен большого проекта, а вовсе не какая-то необходимость. С одной стороны - хорошо, с другой, их не найдешь в map файле, если понадобится найти точку зависания программы. То, что static - помощь компилятору в деле инлайнинга, вопрос спорный. В одном компиляторе может так, в другом этак.
demiurg_spb
Повторюсь, я говорю о avr-gcc и о контроллерах в которых флеша всегда маловато и рассуждать о удобстве поиска "зависшей программы" мне лично и в голову не приходит. Отладка через консоль на 99 процентов решает любые непонятки, а остальное решается просмотром asm листинга если терзают сомнения в качестве компилятора или в качестве собственной черепушки - что тоже надо признать бывает:-) Это моё ИМХО.

Что-то мы уже по второму кругу пошли...
sergeeff
Цитата(demiurg_spb @ Feb 18 2011, 12:21) *
Повторюсь, я говорю о avr-gcc и о контроллерах в которых флеша всегда маловато ...


Это очень хорошо, что вы знаток avr-gcc. Но я все равно не понимаю, какое отношение имеет объявление локальной функции как static к объему flash памяти на борту. Что это дает на практике? Расскажите, поделитесь сокровенным.
demiurg_spb
В gcc, да и думаю во всех современных компиляторах, идёт однозначное встраивание статической функции если она вызывается лишь один раз и отсутствует её вызов по указателю. Также статическая функция, которая при встраивании сопоставима по сложности с загрузкой аргументов, выполнении инструкций call+ret и выгрузкой её результата, может быть встроена даже при неоднократном её вызове. Естественно баланс оптимизации сильно зависит от грамотности настроек компилятора. Вся эта оптимизация на круг даёт существенный выигрыш как по объёму так и по скорости, за что собственно и ратую.
sergeeff
Про все современные компиляторы, как выше упоминалось, это вовсе не факт. А раз это так устроено для AVR - намотаем на ус.

Вообще многие гуру программирования отмечают, что хорошим стилем является написание коротких функций. Компилятор сам может потом решить встраивать их или нет. Да и их тело оптимизировать проще.
_Pasha
Цитата(sergeeff @ Feb 19 2011, 02:50) *
хорошим стилем является написание коротких функций.

+1
В любом случае, чем меньше повторений кода - тем меньше ошибок, ибо исправления вносятся только один раз. А вложенность статиков пугать не должна, вот с количеством передаваемых параметров - это да, надо с ним воевать.
andron86
Цитата(GYUR22 @ Feb 11 2011, 13:39) *
Есть одна большая функция в программе - которую можно поделить на много мелких ...
Вопрос как выгоднее по скорости исполнения - разделить ее на 18 мелких (по смыслу) или оставить как есть?

ИМХО
Разделяется по функциональности. Например функция:
check_port_a() {
i2c_byte_read();
uart2_send_data();
}
на мой взгляд выглядит не плохо.
Что плохо бы смотрелось, это пример:
check_port_a() {
start_i2c_bit();
set_i2c_address();
set_i2c_cmd_byte();
get_i2c_byte();
stop_i2c_bit();
uart2_send_data();
}

По скорости будет примерно одинаково.
demiurg_spb
Цитата(sergeeff @ Feb 19 2011, 01:50) *
А раз это так устроено для AVR - намотаем на ус.

Небольшая поправочка:
Это устроено в gcc вообще и в avr-gcc в частности.
sergeeff
Цитата(demiurg_spb @ Feb 19 2011, 12:02) *
Небольшая поправочка:
Это устроено в gcc вообще и в avr-gcc в частности.


Свет клином на gcc не сошелся. Он, конечно, не стоит на месте, но заморочек с ним все равно хватает.
_Pasha
Цитата(sergeeff @ Feb 19 2011, 14:11) *
Он, конечно, не стоит на месте, но заморочек с ним все равно хватает.

Каких? biggrin.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.