Оптимизация, быстродействие кода
- Войдите на сайт для отправки комментариев
Ср, 21/07/2021 - 08:44
Всем привет! У меня вопрос по быстродействию выполнения кода.
Всем привет! У меня вопрос по быстродействию выполнения кода.
Перепишу по другому, скопировал из другого форума.
У меня очень быстро должно сработать прерывание Таймера, в нем присутствует такая команда:
PORTB |= (1 << SOLENOID_DEFECT)
SOLENOID_DEFECT определён как:
#define SOLENOID_DEFECT 3 // соленоид дозатора
Были мысли такие:
(1 << SOLENOID_DEFECT) состоит, по моим предположениям, из НЕ одной команды (точно не знаю сколько), берём 0х01 и сдвигаем на 3 бита влево. То есть тратится время на сдвиг.
Теперь идея такая, что, чтобы не тратиться на сдвиг, берём число 0х08 и проделываем следующее:
PORTB |= 0х08;
Но почему-то в последнем варианте команда получается на один такт больше... Замерял по счётчику Таймера. Я правильно рассуждал? Но ассемблер или компилятор решил по своему или работа его/их состоит по другому, не как я думал?
Умнее компилятора быть не получится. Там наверняка используется одна команда SBI.
Где именно, в первом или втором варианте? И если можно, то где по Вашему устанавливается бит, в каком месте, ну то есть своими словами описать если
Первый и второй вариант одно и тоже. Перед трансляцией (!) 1<<3 превращается в 8.
Приведите минимальный код пригодный для трансляции, тогда я покажу конкретнее.
С этим согласен. Но у меня мысли примерно такие.
Берём вариант со сдвигом. Я думаю ассемблерные команды там такие, что, в регистр записываем число 0х01, потом применяем команду сдвига на 3 влево. То есть уже 2 команды.
Теперь берём вариант без сдвига (PORTИ |= 0x08). Тут, мне кажется, должно быть на одну команду меньше. Берём уже готовое число 0х08 и производим манипуляцию с портом PORTB (PORTB |= ... )
Я не прав?
Код такой для сравнения:
PORTB |= (1 << 3);
и
PORTB |= 0x08;
Не прав. См. выше.
То есть, при трансляци, (она же компиляция, если я правильно понимаю) операция сдвига 1 << 3 заменяется уже готовым числом 0х08? Компилятор то есть по своему код вставляет?
Оптимизирующий компилятор
Компилятор то есть по своему код вставляет?
оптимизатор может очень сильно менять код, вплоть до того что просто выкинуть две трети вашего скетча, если посчитает его ненужной. А уж такие простые вещи, как заменить в коде операцию с константами типа 2*2 на заранее вычисленный результат 4 - это для него семечки.
И скорее всего ваши "измерения" о разнице в один такт - тоже результат оптимизации, а не реальная разница между операторами.
А листинг глянуть и/или нам показать - религия не позволяет ???
Реакция на прерывание у AVR и так не самая быстрая, так что если скорость реакции критична - надо обязательно смотреть что там компилятор нагородил !
Чё то ржу...
Это что за соленоид такой, что для него нужно такты считать ?
какой-какой - дефектный...
#define SOLENOID_DEFECT 3 // соленоид дозатора
А листинг глянуть и/или нам показать - религия не позволяет ???
Реакция на прерывание у AVR и так не самая быстрая, так что если скорость реакции критична - надо обязательно смотреть что там компилятор нагородил !
Я изучал давно ассемблер, но чтобы смотреть код ассемблерный, компилятор который делает, у меня опыта в нём разобраться боюсь не хватит...
Тут не для соленоида такты считаются. Прерывание вызывается каждые 10 мкс, проверяется монета на подлинность, то есть через 160 тактов в Ардуине Мега. При этом у меня должно отображаться что-то на дисплее (подсчитанные монеты). И если в обработчике прерывания выявился суррогат монеты, срабатывает соленоид отбраковки вышенаписанной команды.
Прерывание вызывается каждые 10 мкс, проверяется монета на подлинность,
Родное сердце. Ты точно МИКРО секунды и МИЛЛИ секундами не перепутал? ;))))
За 10 мкс звук в воздухе пройдет 3.4 мм. ;)) Монета, в монетоприемнике у тебя со скоростью звука движется? Фирменная вещь!!!
(Пипец, какой-то!)
Я изучал давно ассемблер, но чтобы смотреть код ассемблерный, компилятор который делает, у меня опыта в нём разобраться боюсь не хватит...
Так покажи нам тогда. Мы то на ассемблере собаку съели ...
Нет, не перепутал. Монета проверяется индуктивным датчиком. На катушку подается импульс, затем подсчитываются затухающие колебания через компаратор (он отдельный слава богу). И вот через каждые 10 мкс смотрим, сколько импульсов компаратора пришло на счётный пин Таймера (счётчик таймера отдельного инкрементруется через пин МК)
Я изучал давно ассемблер, но чтобы смотреть код ассемблерный, компилятор который делает, у меня опыта в нём разобраться боюсь не хватит...
Так покажи нам тогда. Мы то на ассемблере собаку съели ...
Примерный код обработчика такой, ещё не до конца написанный...
И что ?
После этого монета испаряется и на ее месте тут же материализуется другая ?
В общем или система неправильно спроектирована или с башкой у кого то проблемы и этот кто то не может сформулировать что же ему нужно
Примерный код обработчика такой, ещё не до конца написанный...
это не обработчик, а полный бред. И он никак не может вызываться каждые 20мкс, не обманывайте себя. У вас прерывание на прерывание налезает.
Обработчик прерывания должен быть максимально коротким, буквально пара строк кода. В вашем случае в нем должен быть только счетчик импульсов и НИЧЕГО БОЛЬШЕ,
Все проверки, а тем более запуск и остановка мотора - должны быть в основной программе.
и после этого вы еще говорите, что у вас два варианта кода на такт различаются??? - не смешите
Кстати, операторы SOLENOID_DEFECT_ON; STOP_MOTOR; - это что? макросы? - покажите их код
Это вот этот обработчик ты планируешь вызывать каждые 10 мкс???
Не нужно тут продолжать. У нас, вероятно, квалификации не хватит, чтобы тебе помочь.
У нас, вероятно, квалификации не хватит, чтобы тебе помочь.
сарказм? - хотя нет, в этом случае человеку помочь действительно сложно :)
Сегодня, в процессе написания своих вопросов, я тоже начал задумываться о максимально коротком обработчике. Говорят - одна голова хорошо, а две лучше. Продумаю логику программы конечно ещё раз хорошенько, знаю примерно, что за секунду через датчик пролетает около 20 монет.
"Все проверки, а тем более запуск и остановка мотора - должны быть в основной программе."
Согласен с Вами. Но хотелось бы максимально быстро остановить мотор и застопорить монету сердечником соленоида. У меня в основной программе, при входе в функцию в цикле loop, проверяется ещё факт наличия/отсутствия на сенсор экрана, кнопку останова машины вручную, а это тоже время, поэтому и хотелось бы что-то впихнуть в обработчик.
Операторы SOLENOID_DEFECT_ON; STOP_MOTOR; это и есть вопрос с самого начала:
Операторы SOLENOID_DEFECT_ON; STOP_MOTOR; это и есть вопрос с самого начала:
в таком обработчике разница между этими операторами (даже если она и есть) - не имеет ровно никакого значения. Этот код занимает СОТНИ и ТЫСЯЧИ тактов, а вы задались вопросом, как сэкономить один такт? - займитесь лучше переносом обработки в программу
На осцилограмме затухающих колебаний там как раз эти колебания в этот диапазон временных рамок и влезает. Программа была написана давно на ассемблере. Мне дали задание внедрить сенсорный экран, вот я и стараюсь. Но пишу на Си++, а кода ассемблерного этой машины у меня нет, поэтому придумываю всё сам с "нуля". Так что сильно не критикуйте, а давайте советы :-) (хотя к любой критике готов)
в любом случае физическая реакция соленоида - это десятки МИЛЛИсекунд, а не микро. Так что никакого смысла хвататься за него в прерывании - нет
Никаких тут тысяч тактов нет, обычное прерывание.
Никаких тут тысяч тактов нет, обычное прерывание.
пока считает (выполняется ветка if) - обычное прерывание, а как закончит (ветка else, строка 9) - так дальше бред
Операторы SOLENOID_DEFECT_ON; STOP_MOTOR; это и есть вопрос с самого начала:
в таком обработчике разница между этими операторами (даже если она и есть) - не имеет ровно никакого значения. Этот код занимает СОТНИ и ТЫСЯЧИ тактов, а вы задались вопросом, как сэкономить один такт? - займитесь лучше переносом обработки в программу
Не соглашусь с Вами, с приведенными числами "СОТНИ и ТЫСЯЧИ тактов". У меня же тут проверками-ветвлениями, switch-case-ми. Работает максимально быстро. Если приглядеться, то в самом начале, если число импульсов компаратора разнятся с буфером, тут же выходим из прерывания. Если равны (импцльсы закончились), через switch-case (что быстрее чем if-else) число импульсов сравнивается с эталоном и далее реакция на 3 команды. Ну и переключение на частоту высокую или низкую (противоположную)
Я бы сделал так:
Подаём импульс на индуктивный датчик запускаем таймер, врубаем прерывание по INT0/INT1.
В нем считаем импульсы, обнуляем таймер.
Как только импульсы кончились и таймер не обнулился, делаем выводы , основываясь на результатах счётчика.
Это цикл проверки.
Только он должен быть быстрым, в нем два действия всего. При выполнении проверки все остальные прерывания должны быть выключены.
В цикле в это время должно крутится только что то типа этого:
switch (mode){
case check:
if(millis()-timer>=interval){
mode=nextStep;
}
}
Теперь у нас идёт процесс замены монеты и всего остального, в том числе и отрисовка дисплея
Изменил немного начало, убрал пару команд:
через switch-case (что быстрее чем if-else)
вы опять забыли про оптимизацию. Вполне возможно, что компилятор приводит оба варианта к одному машинному коду...
насчет длительности прерывания больше спорить не буду.
Эта логика сразу непригодна. Если я правильно понял, что "врубаем прерывание по INT0/INT1", то это подсчёт импульсов с компаратора. Эти импульсы приходят очень быстро - только на них всё и повиснет. А так, по моей задумке, я через 10 мкс сравниваю число этих импульсов с буфером и, если они разнятся, то импульсы продолжают поступать, а если они равны с числом в буфере, то импульсы закончились.
Далее, потом, когда код будет боле-менее рабочим, мне сказали сделать всё на СТМ, что тогда исключает Ардуиновские команды millis() и analogRead(), что я и переписываю на свой лад (милис через таймер, аналогРэад вручную настраиваю биты в регистрах АЦП)
мне сказали сделать всё на СТМ, что тогда исключает Ардуиновские команды millis() и analogRead(), что я и переписываю на свой лад (милис через таймер, аналогРэад вручную настраиваю биты в регистрах АЦП)
в СТМ тоже есть и миллис и analogRead()
Есть ардуино для СТМ32, все команды поддерживаются. А критичные места вы можете напрямую через регистры и таймеры написать
Какая частота колебаний ?
Запустите код в Proteus - там есть счетчик тактов и посмотрите сколько тактов от входа до выхода.
насчет длительности прерывания больше спорить не буду.
Зря. Это прерывание примерно на 10 мкс. Вход-выход уже около 80 тактов и столько же (минимум) внутри. Каждый такт 1/16 мкс ;)))
10 микросекундные действия в монетоприемнике - изначально бред. Я же написал - вне моей квалификации. Мои познания в коррекционной педагогике не дают мне никаких шансов помочь пациенту.
Мужики, ну ведь среда только!
насчет длительности прерывания больше спорить не буду.
Я же написал - вне моей квалификации.
Да, вне твоей квалификации. Сколько раз ты собираешься это повторить? Ищи тему, где по силам.
Прерывание написано достаточно коротко, потому как при каждом прохождении срабатывает одна из веток. А их общее число на дело не влияет.
AlexBajdin59rus, оба варианта должны давать одинаковый код. Но это все на совести компилятора и его настроек оптимизации. Напрямую писать SBI однозначней. Но основная проблема тут не в этом такте, а в сохранении и восстановлении регистров при входе выходе в прерывание. Местные аборигены не компетентно. Иди на сайт Гувера, ищи Архата, тот когда то говорил что разобрался с этим. Привет передавай.
Топикстартер изобретает собственный монетоприемник? Зачем? Современные монетоприемники стоят не так много денег, но ответственность за правильный прием монет берёт на себя их производитель. Тем более, что все фальшивые сразу отсеиваются. Не понимаю людей, которые замахиваются на прерогативу государства. Пипец.
Напрямую писать SBI однозначней. Но основная проблема тут не в этом такте, а в сохранении и восстановлении регистров при входе выходе в прерывание. Местные аборигены не компетентно. Иди на сайт Гувера, ищи Архата, тот когда то говорил что разобрался с этим. Привет передавай.
Спасибо за совет, схожу туда, как найду Архата, привет передам!
Начал искать, но может пока ищу, Вы мне быстрее подскажите как например выставить на порту В пин в высокий уровень...?
Пробую так:
не получается...
Топикстартер изобретает собственный монетоприемник? Зачем? Современные монетоприемники стоят не так много денег, но ответственность за правильный прием монет берёт на себя их производитель. Тем более, что все фальшивые сразу отсеиваются. Не понимаю людей, которые замахиваются на прерогативу государства. Пипец.
Давайте не будем писать такие сообщения. Что делаю, то делаю. АСМ (автомат счёта монет) изготавливают у нас, спроектировали его 20 лет назад. Там отображение данных цифрами на семисегментных индикаторах - режимы работы, сколько монет подсчитано, уровни проверок подлинности, настройки. Всё это отображается точками, черточками, числами. Разработали в своё время на ассемблере. Мне дали задание внедрить сенсорный экран, чтобы, сами понимаете, можно было словами отображать все данные АСМ. Кода асм нет, поэтому пишу на Си++ и всё пишу с нуля.
Ок. Так получилось, что я немножко понимаю именно в низкоуровневом программировании АВР контроллеров.
Напиши следующее:
1. С какой частотой приходят импульсы в обоих режимах.
2. Сколько их всего должно прийти.
3. Сколько времени монета находится в зоне теста.
----------
Пока хватит. Я примерно понял, что ты делаешь, но хочется понять точнее. Сейчас я ещё не полностью проснулся ;))). В Москве 7-30. Встану часов в 11 и подумаю.
У меня скудная информация на твои вопросы.
1) Частота около 150-200 кГц. Период колебаний разнится, так как состав материала датчика имеет погрешность. Смотрел на осцилограмме, на низкой частоте период около 7 мкс, на высокой около 5 мкс.
2) Колебания с датчика отсекаются компаратором на уровне 0,2В. Компаратор выдаёт также разное количество импульсов, погрешность датчика и разный состав сплава монеты, в среднем около 10. Это когда монета в зоне датчика, когда её нет, число импульсов больше.
3) номинал 1 копейка, (хоть они и вышли из оборота, но в АСМ они предусмотрены), что минимум по диаметру, таких монет пролетают около 20 штук в секунду.
Вопросик: есть переменная, как мне присвоить ей значение 0х00 через ассемблерную вставку?
Пробую так - acm volatile (" clr var");
Не получается...
Я пока не понял, задача написать для AVR или STM?
Если для AVR, я бы посоветовал установить AVR STUDIO 7. В ней можно отлаживать полученный код и можно увидеть полученный ассемблерный код. И если вы там найдете не оптимальный (по вашему) код на ассемблере, тогда и думайте, как его улучшить.
ОК.
Давай начнем.
1. Просто замечание. Я увидел, что ты интересуешься ассемблерными вставками. Вот тебе ссылка:
http://microsin.net/programming/avr/avr-gcc-inline-assembler.html
но я бы не советовал погружаться туда глубоко. Компилятор сделает код ТОЧНО быстрее, чем неопытный человек. Чтобы "обнулить" переменную нужно записать по её адресу 0, если переменная однобайтовая или несколько нолей, по размеру переменной. С учетом того, что компилятор один регистр оставляет для константы 0, каждая запись в ОЗУ - 2 такта. При компиляции программы, если переменная не имеет глобального применения, компилятор часто размещает её в регистре. Тогда все меняется.
Вывод из п.1. - не стоит городить ассемблерные вставки, тем более если ты не знаешь РИСК-ассемблера и особенности компилятора. Ты потратишь очень много времени на обучение.
2. Теперь к сути задачи: При тестировании получаем примерно 10 (пусть 20) импульсов по не более 10 мкс, так? На все меньше 500 мкс, так? Сама идея ожидать паузу между импульсами, вызывая прерывания каждые 10 мкс - порочна. в самом минимуме такое прерывание (аккуратно написанное) займет от 5 мкс, то есть ПОЛОВИНУ процессорного времени, на остальную работу ничего не останется.
В этом случае делают так: 1 вариант - остановить (Обязательно!) другие прерывания, так как в АВР нет приоритета прерываний, на время тестирования и в этот момент не делать, кроме ожидания прекращения импульсов, ВООБЩЕ НИЧЕГО.
2 вариант, более правильный: смотрим по таблице ожидаемых результатов и видим, что (к примеру) 20 импульсов уже точно не соответствуют никакой подлинной монете. Значит мы можем посчитать импульсы на 200 мкс и сравнить полученное количество с таблицей, так? Импульсы нужно считать таймером, если мы не хотим запрещать прерывания. 200 мкс можно просто отмерить встроенной функцией micros(). 200-500 мкс не заметит никакая другая часть твоей программы. Ни дисплей, ни вывод в сериал, при правильном программировании. Монета в тракте за пол миллисекунды сместится максимум на 1 мм.
Этот прием в программировании используется часто. Мы заранее жертвуем часть ресурса, в данном случае фиксируем максимальное время проверки и пренебрегаем возможностью закончить тестирование раньше, поскольку трата ресурсов на отслеживание точного завершения проверки в десятки раз больше, чем просто зафиксировать время теста в, скажем, 300 мкс.
----------------------
Понял ли и сможешь ли написать такое сам?
------------------------
ЗЫ: я выше по тексту увидел, как ты заявил, что switch быстрее серии else if. Для АВР это совершенно наоборот. Видно, что ты неплохо знал особенности работы на обычном ПК. Там другая архитектура и там выбор switch case был верен.
Исследователи кишок компилятора пишут, что switch не столько медленнен, сколько непредсказуем, так как компилятор на основании собственных алгоритмов строит таблицу переходов так, что case не всегда отрабатываются в той очередности, что находятся в исходнике.
Т.е. может и быстрее отработать пачки if, может медленнее, но программист, работающий на уровне "экономии тактов", скоростью отработки switch не управляет,
Медленнее не может
основная проблема тут не в этом такте, а в сохранении и восстановлении регистров при входе выходе в прерывание. Местные аборигены не компетентно.
а можешь обьяснить - нафига конкретно в этом коде сохранять и восстанавливать регистры? Желательно честко и ясно, а не в стиле архата, который чуть что не так - включает обиженку