Использование SPI в основной программе и прерывании одновременно
- Войдите на сайт для отправки комментариев
При работе над проектом http://arduino.ru/forum/proekty/analog-analogovogo-sintezatora#comment-261101
возникла идея использовать для регулировки огибающей сигнала цифровые потенциометры, управляемые по SPI.
Но в проекте уже есть несколько устройств, управляемые по SPI.
Сложность в том, что нужно:
- читать сигнал с АЦП по SPI на частоте 2 МГц из цикла loop(),
- передавать данные в DSP VS1053B по SPI на частоте 1 МГц из цикла loop(),
- задавать режим цифровых потенциометров по SPI на частоте 4 МГц из прерывания по таймеру с частотой 3 кГц или выше.
Первые два варианта производятся по очереди и проблем не вызывают, а вот как их совместить с работой через SPI из прерывания?
Первая мысль - обернуть цикл взаимодействия по SPI в loop() командами запрещения и разрешения прерывания. Но не хотелось бы тормозить прерывание на десятки мкс пока закончится этот цикл взаимодействия.
я запретил.
Думаю можна работать не задумываясь о проблеме. Дело в том, что вхождение в прерывание довольно медленое, пока все регистры посохраняются, то и SPI доработает. По крайней мере байт на 2МГц выпихнет. Если же скорость ниже и/или данных больше - можна делать предпроверку освобождения шины с ожиданием его. И просто запретом прерывания тут не обходится, необходимо еще и помнить о варианте пихонули данные из прерывания, завершили его, и в лупе сразу попали снова на передачу. Но опять таки на 4МГц SPI доработает быстрей, чем выйдем из прерывания.
В общем писал бы без учета проблемы, но старался бы обработчик прерывания делать так, чтоб непосредственно работа с шиной была ближе к его концу а в начале всякое вспомогательное, чтоб дать больше времени на завершение отправки на 1-2Мгц, а на 4МГц и так успеет. Дальше глянул бы дизассемблер что вышло, прикинул бы время и протестировал с пристрастием. При появлении проблемы - предпроверка с циклами ожидания в обработчик прерывания. Запрет прерывания тоже бы сделал но небольшой, сколько там по логике функции отправки получилось бы, ну допустим 2-3мксек. В общем бы и получилось что в худшем случае (прерывание сразу после отправки на 1МГц) прерывание подзадержалось бы на пару мксек, еще пару мксек входило бы в него еще несколько делало бы вспомогательные действия в прерываниии и на крайняк чуть-чуть подождало бы, что случалось бы крайне редко.
andriano,
сам не пробовал, но думаю, что должны помочь транзакции, они для того и делалались. Вы пробовали с ними? Не работает?
Logic, что-то я немного не понял логики работы.
Вот, скажем, я передаю данные в loop() порциями по 6 байт с частотой 1 МГц (что по времени существенно больше, чем время входа в прерывание), а из прерывания - порциями по 4 байта на частоте 4 МГц.
Скажем, я в loop() установил скорость передачи в 1 МГц и начал передавать данные. Случилось прерывание. Если у меня нет запрета на прерывания, то loop() будет прерван посередине, частота переустановлена в 4 МГц, будут переданы данные и произойдет возврат из прерывания, после чего передача оставшегося в loop() должна продолжиться уже на 4 МГц, т.к. в 1 МГц ее никто не перевел.
Кроме того, сигнал CS для 1-МГц устройства на время прерывания тоже никто не отключил, поэтому это устройство честно попытается прочитать с шины то, что предназначено другому устройству.
Я правильно рассуждаю или нет?
Правильно. Это я исходил из того, что вы передаете один байт, ктож знал что их аж 6 будет? )) Тогда написаное мной относится только к последнему байту. Если 6 байт - то конечно запрещать прерывание. Можна не все, а только SPI-ное ну или таймера из которого SPI трогаете.
ПС. Хотя можна еще устроить очередь. Т.е. если прерывание таймера произошло во время когда занята шина, то только взводим флаг, а по завершению обмена в лупе проверяем этот флаг и делаем работу таймера по SPI. А если луп достаточно бытрый, то можна вобще забить на таймер по микрос следить за временем и задача сведется к предыдущей ранее решенной)))
В прерывании только ставите нужные данные в очередь, передачу, при наличии данных в очереди, делаете из loop().
Это единственно грамотное решение, остальное - костыли.
В прерывании только ставите нужные данные в очередь, передачу, при наличии данных в очереди, делаете из loop().
Я бы сделал наоборот, в прерывании отсылал данные для которых флаг (в очереди стоят), а в основной программе контролировал набивку в очередь, тогда время отправки данных будет чётко детерминоровано. Естественно байты с высоким приоритетом и требованиям по джитеру уходят первыми, все кому без разницы по времени - потом.
Цифровые поты, где-то читал, трещат, надо при переходе через ноль менять установки
ЕвгенийП, честно говоря, не понял, какдолжны взаимодействовать между собой beginTransaction(), endTransaction() и usingInterrupt().
Нужны ли первые две при работе из прерывания?
ПОследняя вызывается один раз в setup() или в каждом прерывании вместо beginTransaction()?
Какой номер передавать в последнюю при использовании прерывания таймера?
По поводу параметра, если Вы откроете исходник библиотеки SPI, там есть вот такой комментарий как раз про таймер
По поводу всего остального. Да, usingInterrupt вызывается один раз из сетапа. Я этого ни разу не делал. А вот всё остальнео делал, вот работающий пример (см. функцию update)
В прерывании только ставите нужные данные в очередь, передачу, при наличии данных в очереди, делаете из loop().
Это единственно грамотное решение, остальное - костыли.
Не. Прерыания слишком часто, 3Кгц значить луп в среднем должен быть не дольше 333мкс, а это тяжолое условие, не всегда получится. И максимальное прохождение лупа тоже ограничено сильно. Или же прийдется в лупе этот флаг много раз проверять в разных местах, что тоже не блеск. Вариант с установкой флага только приконфликте потому предпочтительней.
ПС. Глянул я на эти "транзакции" из SPI.cpp. Чуда не случилось, они просто запрещают между begin и end. В общем см. первый пост темы )))
2Logic: Не, ну кто в XX! то веке передает по одному байту?!?
2ЕвгенийП: Спасибо, исходноки я, конечно, смотрел. ПРавде, видать, не очень внимательно - насчет номера 255 не отложилось. Ну так все равно непонятно: по сути мы лишь сообщаем системе, что в неизвестно каком прерывании будет использоватья SPI с неизвестно какими установками. В общем, понятность изложения в этом фрагменте я оцениваю как "ниже среднего". Поэтому и возникают вопросы.
Прилагаемый исходник, увы, ничего нового мне не сообщил: я сейчас вот точно так же с beginTransaction работаю с двумя разными устройствами на разных частотах из одного цикла. Сегодня буду добавлять к ним третье.
А код у Вас неоптимальный. В 19-й строке Вы каждый раз вызываете конструктор SPISettings, а это достаточно ресурсоемкая функция. Она преобразует, используя в частности цепочку условных операторов три входных параметра в 2 байта, соответствующих двум регистрам. Лучше это сделать один раз в статической переменной, а beginTransaction передавать только адрес этой переменной, по которому уже будут лежать два готовых байта.
2All: По поводу собрать всю работу с SPI в одно место: либо в прерывании, либо в loop(). Мне эта идея не нравится. Потому что по логике есть достаточно ограниченный объем работы, которую нужно делать вовремя и есть достаточно большой объем, которую нужно делать хотя бы когда-то. Переносить все в прерывание - нереально, просто прерывание не будет успевать сделать все, что нужно. А городить еще внутри прерывания диспетчер, который будет определять, какую часть работы сделать сейчас, а какую оставить на потом - тоже не хочется: прерывание не резиновое.
Перемещать работу по управлению устройствами из прерывания в loop() тоже не хочется - слишком велики будут отклонения от периодичности управляющего процесса.
Собственно, на данный момент расклад по времени выглядит примерно так (проект: Аналог аналогового синтезатора):
- прерывание происходит по таймеру каждые 332 мкс (частота ~3кГц), в эти моменты времени вычисляются корректировки в характеристики звука: частота, амплитуда, добротность и резонанс фильтра, после чего эти корректировки сразу заносятся в управляемые устройства. Кроме того, в прерывании же организованы программные генераторы LFO (3шт) и ADSR (4шт). Среднее время выполнения прерывания 160 мкс, зафиксированный максимум 204 мкс. Это "внутреннее" время прерывания, т.е. не учитывает время вхождения в прерывание и выхода из него. В дальнейшем, вероятно, это время еще увеличится мкс на 50-60. Т.е. максимум вместе с временем вхождения может достичь ~275 мкс, оставляя на loop() лишь около 55 мкс из каждых 332. Впихнуть сюда еще дополнительно обмен по SPI, да еще с диспетчеризацией, мне кажется, нереально.
- цикл loop() занимает порядка 5-8 мс и включает опрос всех датчиков, среди которых 4 микросхемы АЦП, опрашиваемые по SPI (всего 32 аналоговых канала), 13 каналов встроенного АЦП Атмеги, 4 микросхемы входного сдвигового регистра для опроса 24 кнопрок, а также отслеживание изменений во всех этих показаниях и вывод их на экран по I2C.
- в цикле loop() имеется высокоприоритетная операция - опрос MIDI-входа и реакция на него. Для оперативности вызовы проверки MIDI в количестве нескольких десятколв разбросаны внутри loop() так, чтобы задержка не превосходила ~300 мкс. Сейчас максимальная задержка составляет ~180 мкс + время отобранное прерыванием. Собственно, уже не очень укладываемся. Время реакции на приход MIDI-сообщения - до 70 мкс. Собственно, именно в эту "реакцию" я постарадся вынести максимум того, что было можно из прерывания.
Уже сейчас максимальная задержка может зашкаливать за 500 мкс: 180 + время двух прерываний. Думаю, для процесса с периодичностью 332 мкс задержка более длины периода - это слишком. Да и второй приоритетный процесс в loop() организовывать не хочтся.
Т.е. сгруппировать всю работу с SPI где-то в одном месте не получается. Но и по-другому - не очень: по сути, если мы запретим прерывание на время больше 55 мкс, у нас уже одно прерывание "наедет" на другое. В общем, куда ни крути, везде тесно.
2Волшебник: Собственно из за этого "треска" сейчас и думаю, как переделать схему. Пока использовался цифровой регулятор громкости M62429 с оригинальным способом управления (похоже на SPI, но без CS, поэтому SPI использовать нельзя). Шаг логарифмический (что хорошо) по 1 дБ (что плохо) и максимальная частота 0.25МГц (что тоже плохо). Собственно, 1дБ - это предел различимисти человеческого уха, но из-за изменения коэффициента передачи в произвольные моменты времени возникает помеха порядка -20дБ. Переход на цифровые потенциометры обещает даже без специальных усилий увеличить это отношение примерно до -50дБ. Ну и есть схемы, где отслеживается переход сигнала через 0, но для работы тех схем, что я видел, нужен сигнал CS - именно по нему происходит переключение коэффициента передачи. Т.е. после окончания передачи CS придерживается до тех пор, пока сигнал не пересечет 0.
В общем см. первый пост темы )))
Первый пост:
я запретил.
Та нинадо там никакого диспетчера. Просто все из лопа работают по единому сценарию, лучше вобще одними функциями. При входе ставят флаг "шина занята" после передачи последнего байта проверяют флаг "прерывание хотело", и если он установлен - делают то что надо было из прерывания - собственно вызывают ту же ф-ию что и прерывание должно было вызвать. Прерывание в свою учередь устанавливает флаг "прерывание хотело" только при обнаруженом активном "шина занята", иначе молча делает свое дело. Все просто, на диспетчера оно не тянет.
Хотя при тех загрузках, что Вы расписали, не грех про другой проц задуматся. Заниматся таким можна больше из любви к искуству.
А код у Вас неоптимальный. В 19-й строке Вы каждый раз вызываете конструктор SPISettings, а это достаточно ресурсоемкая функция. Она преобразует, используя в частности цепочку условных операторов три входных параметра в 2 байта, соответствующих двум регистрам. Лучше это сделать один раз в статической переменной, а beginTransaction передавать только адрес этой переменной, по которому уже будут лежать два готовых байта.
Видимо, Вы изучали программирование в те же времена, что и я, а с тех с теорией компиляции жизнь Вас не сталкивала. То, что Вы говорите, было бы верно в 70-80-ые годы прошлого века, но с тех пор всё шагнуло далеко вперёд, а gcc - вполне приличный современный компилятор.
В современных компиляторах, если параметры inline конструктора константы, а результат передаётся другой inline функции, то лучше делать так, как я делал. При моём подходе получаем выигрыш по памяти и "шило на мыло" по скорости выполнения.
Об этом ( о том, что с константами так лучше) написано и в документации библиотеки. Но мы с Вами лучше сами проверим, чтобы знать точно. Итак, сначала текст на память. В моей версии IDE получилось так.
и
Как видите, выигрыш по памяти программы довольно приличный. В другой версии цифры могут быть иные, но подход с константами выиграет всегда.
А вот тест на время выполнения (в теории должно быть практически шило на мыло):
Как видите, вариант с константами даже на кроху выиграл, но скорее всего это из-за перехода на "нецелую миллисекунду", в общем в теории должно быть равенство - практически его мы и наблюдаем.
---------------------
Если интересно, я могу рассказать почему так (что там за теория такая), но, сами понимаете, это уже узко-специальные вещи - как пишут на Лурке "много матана".
Какие нафиг "специальные вещи"!? Оптимизация константных выражений, просто ранее компиляторы умели это не для конструкторов, а с некоторых пор вспомнили что иниченый константами конструктор - тоже в принципе константа и на него константные выражения распостранили, ну и инлайн удачно подвернулся. Но надо учитывать что инлайн - не догма, компилятор может и передумать, например при нескольких вызовах в разных местах SPI.beginTransaction(SPISettings(F_CPU/2, MSBFIRST, SPI_MODE0)); вместо выиграша будет пролет.
Но даффайте от темы не уходить, она довольно интересная а SPI.beginTransaction вобще ниче не дает.
Евгений, вынос из тела цикла того, что не зависит от параметна цикла - с таким компиляторы, действительно, справляются уже давно. Но у Вас другой случай: в цикле вызывается функция update(), причем отнюдь не inline. И в этом случае внутренность функции, насколько мне известно, за пределы цикла, из которого вызывается функция, не выносится.
Соответственно, Ваша модельная задача совершенно не моделирует основную.
А городить еще внутри прерывания диспетчер, который будет определять, какую часть работы сделать сейчас, а какую оставить на потом - тоже не хочется: прерывание не резиновое.
Теперь понятно, у Вас проблема не технического, а психологического плана. Налицо осознание необходимости внедрения диспетчера (супервайзера, планировщика - как угодно) и нежелание это делать. А придётся, у меня не было проэктов такой сложности, диспетчера на ДУЕ я ставил и в более простых скетчах. Есть ресурсы, и есть борьба за них. К ресурсам относятся аппаратные устройства и процессорное время. Хорошо что память пока не точка конфликта. Без старого доброго тоталитаризма, в лице диспетчера, никак. Демократия погубит проэкт. Планировщик не ставится в прерывание, он заполняет таблицы кому чего посчитать/ отправить, а прерывание тупо эти таблицы процесит.
Так что перестаньте метаться, паниковать, берите ручку и расписывайте приоритеты,/ иерархию, кому сколько ресурсов выделять. Планируйте бюджет, так сказать.
... у Вас проблема не технического, а психологического плана.
Спасибо, доктор. )
Если все именно так, то еще есть время на то, чтобы смириться с этой мыслью. Каждая очередная итерация проекта включает:
1. Анализ существующих недостатков и выбор способа их преодоления.
2. Составление принципиальной схемы исправленного устройства.
3. Заказ деталей нав Али.
4. Ожидание доставки.
5.Получение посылки.
6. Изготовление платы.
7. Адекватное изменение софта.
Пока я еа первой стадии текущей итерации, а "перестать метаться" нужно только к 7-й. )
Евгений, вынос из тела цикла того, что не зависит от параметна цикла - с таким компиляторы, действительно, справляются уже давно. Но у Вас другой случай: в цикле вызывается функция update(), причем отнюдь не inline. И в этом случае внутренность функции, насколько мне известно, за пределы цикла, из которого вызывается функция, не выносится.
Соответственно, Ваша модельная задача совершенно не моделирует основную.
Дело там вовсе не в выносе из тела цикла, а в том, что вместо честного конструктора туда попадает уже готовая(ые) константа(ы) и попадает прямо "куда надо", т.к. все вычисления выполняются на этапе компиляции. Так что там уже н ечего выносить из тела цикла. Абсолютно точно моделирует.
UPD: я тут подумал, если Вы считаете, что это вынос из цикла, попробуйте убрать циклы из функций, а сами функции вызывать в циклах. Можете даже функции static объявить, чтобы не де Бог не с'inline'ились. И увидите, что ничего существенно не изменится. Вызов конструктора в параметре в данном случае даёт выигрыш (разумеется, параметры конструктора должны оставаться константами).
Какие нафиг "специальные вещи"!? Оптимизация константных выражений, просто ранее компиляторы умели это не для конструкторов, а с некоторых пор вспомнили что иниченый константами конструктор - тоже в принципе константа и на него константные выражения распостранили, ну и инлайн удачно подвернулся.
Вашу точку зрения о том, что
я знаю. Вы не видите там науки и не понимаете её, потому считаете. что её там нет. Ваше право.
Только не зная основ, очень трудно делать правильные выводы. Например, Ваш замечательный пассаж
разбивается о простую проверку. Давайте, чтобы не болтать голословно, посмотрим. Даже усложним задачу - поставим в каждом вызове разные константы, чтобы не казалось, что он один раз посчитал и везде подставил. Итак, вызываем 15 раз с разными константами
а теперь те же 15 раз с заранее определённой переменной
Ну, и где пролёт? Нет никакого пролёта и не будет потому, что там проводится реальная оптимизаци на основе частичных вычислений. То, что в программмировании нет науки и теории, расскажите кому-нибудь другому.
"Застав дурака богу молится..." - Ваш дивиз, ЕвгенийП.
А из каких научных соображений Вы остановились на 15 вызовах? Надо было продолжать исследование в сторону больших чисел )))
Проверим, имеет ли премущество оптимизация константных выражений.
Да имеет, 54 байта экономим.
Продолжим писать код, а то пустой луп плохо смотрится.
Ой, а что сталось с экономией?
Так что продолжайте ЕвгенийП, наращивать кол-во одинаковых вызовов ))) Пилите, ЕвгенийП, пилите... Или на брайнфаке чего напишите, и обязательно так, чтоб данные исполнялись ;) Пудрить мозги безполезной фигней - Ваше кредо и ваша наука.
Но только НЕ НАДО ЗАФЛУЖИВАТЬ ТЕМУ!!!!
ПС. Наблюдается еще занятный момент в этом примере. Если константы в SPISettings set(F_CPU/2, MSBFIRST, SPI_MODE0); и SPI.beginTransaction(SPISettings(F_CPU/2, MSBFIRST, SPI_MODE0)); взять не одинаковые, а разные, то памяти расходуется.. меньше на 2 байта 8/ Но то мелочи не достойные внимания.
.
Logik,
сравните Ваши картинки - левую верхнюю и левую нижнюю.
Вы ведь в нижней сделали не константы в двух разных местах, а к константному вызову добавили неконстантый. Он и съел Вашу экономию. Поставьте два константын и экономия останется.
Вы просто ошиблись? Или это уже умышленный подлог, чтобы не признавать свою неправоту? Если второе, то я здорово разочарован. Вы казались мне умнее. Очень надеюсь, что Вы просто ошиблись.
А чего это я должен везде в коде только константы использовать? Не дождетесь! Есть фрагмент кода который как бы чего экономит. Ну вот пусть и экономит, а не диктует остальной программе как её писать.
Если я в коде запишу 2+2, то компилятор его чесно заменит на 0х04 и соответствено немного сэкономит. И результат никак не зависит от моих последующих писаний. Это нормально. А предложение писать код только на константах... Вы вобще себя нормально чувствуете?
Мало того, даже писать на одних константах не поможет, если inline функция достаточно большая, компилятор очень быстро от инлайности откажится и экономии опять не будет.
Да он то экономит!
Ну, смотрите, я тут битый час объясняю, что собственно экномия берётся за счёт того, что при константах ВСЁ до готовых значений считается компилятором и не нужно вставлять никакого кода для расчёта в исполняемую программу. А при переменных вставлять код для расчёта нужно. Вот она и экономия отсюда берётся.
Вы же добавили переменные, ну так для них код и сгенерировался.
Понимаете?
ВСЁ до готовых значений считается компилятором и не нужно вставлять никакого кода для расчёта в исполняемую программу.
Я рад что вы это понимаете, но рассматриваемый случай - не 2+2. Здесь вызов конструктора, который в общем случае может делать много разного (распределять память, создавать списки и пр.. ) его код, именно последовательность команд достаточно большой и компилер сам будет решать, делать инлайн с константной оптимизацией или нет и обойтись вызовом подпрограммы и соответственно константной оптимизации не будет. Если вызовов много, конструктор большой и иногда при вызовах могут и переменные быть то выбор будет в пользу подпрограммы. Что я и продемонстрировал.
Еще раз обращаюсь к Вам, ЕвгенийП! Хватит флудить! Хотите узнать больше про тонкости оптимизации- открывайте для этого тему, спрашивайте, обясню. Но тут тема и без того очень интересная.
Если я в коде запишу 2+2, то компилятор его чесно заменит на 0х04 и соответствено немного сэкономит. И результат никак не зависит от моих последующих писаний.
Что значит не зависит? Вся Ваша последующая программа не будет занимать места?
Он и здесь заменил на вычисленные константы (точно как на Вашу 4), а потом Вы вставили другую конструкцию, которая заняла своё место и заняла его независимо от того, что там выше было написано. Вставьте ещё что-нибудь, оно тоже место займёт.
-------
В общем, у меня твёрдое ощущение. что Вы уже всё поняли, а продолжаете что-то возражать ... ну, Вы понимаете.
Да, кстати, заявление, что наука в программировании - "бесполезная фигня" напоминает позицию свиньи, которой дуб был пофигу, а нужны только жёлуди.
Если вызовов много, конструктор большой и иногда при вызовах могут и переменные быть то выбор будет в пользу подпрограммы. Что я и продемонстрировал.
Нет, Вы этого не продемонстрировали. В Вашем примере в в верхнем вызове было все соптимизировано до констант, а в нижнем - нет. Ничего другого Вы не продемонстриировали.
И запомните, компилятор сам ничего не решает. Он пишется на основе определённой теории, которую Вы называете бесполезной фигнёй.
Еще раз обращаюсь к Вам, ЕвгенийП! Хватит флудить!
Кто начал?
спрашивайте, обясню.
:)))))))))) Давно так не смеялся. Спасибо, Всё что могли, Вы уже объяснили в этой теме.
Смех без причины раньше был признаком эсэсовца, теперь и у Вас истерики?
Он и здесь заменил на вычисленные константы (точно как на Вашу 4), а потом Вы вставили другую конструкцию, которая заняла своё место и заняла его независимо от того, что там выше было написано.
Как видите инлайна нет. Как и мистической "другую конструкцию". Есть просто вызов подпрограммы call 0xa8 ; 0xa8 <_ZN8SPIClass16beginTransactionE11SPISettings>. И это очевидно, т.к. код даже с издержками на передачу параметров занимает места меньше чем... её же код плюс остатки от удачной константной оптимизации инлайнового конструктора. И удачненько завершение лупа получилось к тому же.
Блин, вот скажите всё-таки, Вы и впрямь не понимаете, что в верхнем случае это ВЕСЬ код, а в нижнем - есть ещё кусок, который остался во второй строке Вашей программы - вызове конструктора и который Вы здесь не привели? И вот именно на тот код и была экономия по памяти, т.к. в верхнем случае его нет, а в нижнем - есть?
Боюсь, что Вы всё понимаете, просто "один раз ляпнул, а теперь "усрусь, но не признаю, что не прав"". Мне такая форма ведения дискуссии (я это называю подлогом) немного претит. До свидания, не буду больше Вас утомлять.
Блин, вот скажите всё-таки, Вы и впрямь не понимаете, что в верхнем случае это ВЕСЬ код, а в нижнем - есть ещё кусок, который остался во второй строке Вашей программы - вызове конструктора и который Вы здесь не привели?
А нервы у Вас ни к черту ;) Мысль стала сбивчивая, речь путаная.
Что я не привел? Приведены setup и loop полученые при компиляции скетча
Того самого, что и во втором моем скрине слева. В котором исчезла экономия места т.к. компилятор похерил константные вычисления. В ассемблере это видно однозначно. Конструктор вызывается подпрограммой, константы передаются как и переменные, через регистры 24 и 25.
///вызове конструктора и который Вы здесь не привели?
Что я не привел? В сишном два вызова конструктора, с константой в сетапе и переменной в лупе. Ассм - аналогично. Константной оптимизации и инлайна не обнаружено. Компилятор все похерил и перешел к наиболее общему способу выполнения.
Не, ну я всё понял и у же попрощался. Эта музыка будет вечной. Я написал, что с константами также по времени и оптимальнее по памяти. Вы же смешали в кучу и то и другое вместе и что-то доказываете. Доказывайте кому-нибудь другому.
Ладно, в последний раз, поытаюсь объяснить смысл частичных вычислений на более простом примере. Просто я привык относитья к Вам более серьёзно, чем к Архату (тому бы ничего объяснять не стал) и, всё же надеюсь, что здесь недоразумение, а не упёртость.
Вы всё время говорите про какую-то оптимизацию констант. Поймите, речь идёт не о замене 2+2 на 4. Вовсе не об этом. Это прошлый век. Современная теория компиляции включает в себя некоторые (пока не все) элементы алгебры программ, а именно теории частичных вычислений. Суть её в том, что константы подставляются в код и код вычисляется до тех пор, пока это возможно. Результатом может стать то, что алгоритм из квадратичного превратится в линейный а некоторые функции исчезнут вовсе. Например Если у Вас есть функция выдающая сумму a+b, а Вы обратитесь к ней sum(2, b), она вполне может скомпилироваться в функцию с одним аргументом, которая прибавляет к аргументу двойку. А если Вы к ней обратитесь sum(2,3), она просто может замениться на 5. Заметьте не 2+3 заменилось на 5, а вызов функции заменился на уже готовый её результат.
Это очень продвинутые техники и поначалу они реализовывались только на специально для этого созданных эзотерических языках, на которых это делать было удобно. Сейчас это уже потихоньку переходит в промышленные языки. Например, частично это реализовано в gcc. Посмотрите, как лихо он заменяет рекурсивное (!!!) вычисление факториала на готовый результат. Не берите в голову присваивание порту B - смысла не имеет, зато операция простая - дизасемблер не загаживает. Нас интересует как он факториал скомпилирует, а куда присвоить результат - какая разница. Смотрите:
А вот во что это дизасемблировалось
Как видите, компилятор просто вычислил эту рекурсивную функцию сам и в коде оставил только готовый результат - 120. Вот это - современная оптимизация, а не 2+2 на 5 заменять.
Если Вы считаете, что это результат изобретательности авторов компилятора и науки здесь нет - Ваше право, мне всё равно. Если же Вы скажете, что это опять нафиг никому не нужное изнасилование Вашего мозга, то у нас разные взгляды на красоту в программировании. Просто не читайте моих постов - берегите девственность мозгов.
P.S. Я несколько раз подчёркивал. что функция рекурсивная потому, что в этом особая сложность - нет никакого способа заранее узнать завершится ли она когда-нибудь.
Еще раз обращаюсь к Вам, ЕвгенийП! Хватит флудить! Хотите узнать больше про тонкости оптимизации- открывайте для этого тему, спрашивайте, обясню. Но тут тема и без того очень интересная.
шо за фигня тут происходит?
флудер в законе здесь я. а, ЕвгенийП - чепуха пидагогическая.
Так к Вам, Клапауций уже все привыкли, при всех недостатках у Вас огромный плюс - краткость ;)
Дело там вовсе не в выносе из тела цикла...
Проверил - действительно, я был не прав: компилятор подвергает интерпретации не только константные выражения, но и константные алгоритмы с ветвлением.