Отдавая себе отчёт о времени публикации данной статьи ( девять лет назад ), буду рад возможности осудить возможность ускорения и повышения точности "рисования", если задача ещё актуальна.
Был опыт создания скоростных растровых графических примитивов (прямые, ломаные, кривые, штриховки, заливки и т.д. )
Однако STM не равно Cortex, а M4 весьма отличается от M0
Вы абсолютно правы.
В угоду Вашей правоты замечу, что задача планировалась под чип STM32L031, но в процессе подготовки текста под рукой оказались другие оценочные платы, на что указывает директива условной компиляции в файле заголовка.
Учить читателя, что-либо доказывать, объяснять - такой цели здесь нет. Есть практический опыт, изложенный в доступной форме, пригодной для проверки, обсуждения, заимствования и улучшения. Лицензия в заголовке исходного кода это позволяет.
В разделе "постановка задачи" никакой постановки задачи нет..
Обременять популярную статью согласно "ГОСТ Р 19*", "ГОСТ Р 34*" или там "ГОСТ Р ИСО 9000, 9001, 9004" и т.п. считаю нецелесообразным.
создатели кортексов наградили их чудесным средством DWT_CYCCNT
Согласен, DWT - интересная вещица.
В статье опыт решения прикладной задачи динамического контроля и корректировки множества, в том числе вложенных, малых интервалов времени, в т.ч. в режиме отладки, с минимальным набором аппаратных ресурсов на бюджетном микроконтроллере M0.
Полученный метод позволяет оперировать вложенными интервалами времени от нескольких тактов до нескольких часов с известной точностью, что и требовалось в прикладной задаче.
Проектирование промышленных программ исключает обстоятельства, характеризуемые словосочетаниями "потом появляется", "вдруг" и т.п., ибо такие программы сопровождаются технической документацией, содержащей обязательный раздел "Технические условия эксплуатации", обоснованные в ТЭП ещё на этапе планирования разработки до создания исходного текста программы.
Время реакции на событие, предсказуемость времени реакции зависит от времени выполнения критических участков кода, обслуживающих событие. Об этом статья.
Практика всегда опирается на результат с заданной степенью точности. Достижение результата с заданной степенью точности всегда удовлетворяет практиков без оглядки на способ получения такого результата.
Приоритет способа в ущерб результату - это область теоретиков и учеников, допускающая непредвиденные обстоятельства типа "потом появляется", "вдруг" и т.д.
Для оценки соответствия машинного кода и кода на "C"; для снижения энтропии системы сборки через исключение модуля искажающего первичную алгоритмическую основу.
Вы правы, есть загрузка из памяти в регистры, и прерывания вклиниваются когда посчитают нужным.
В статье о поиске способа простого планирования и учёта времени работы ответственных участков кода оставаясь в парадигме языка "C".
Говоря о планировании, подразумеваем не обязательство, а цель, допускающую отклонение в пределах установленного допуска.
Это значит - без запретов прерываний, без порочного ожидания "delay()" и т.п.
Предположительное начальное приближения к решению задачи - подсчёт числа простейших арифметических операторов на языке "С" на контрольном участке кода.
Под простейшим оператором понимаем конструкцию типа: A = B + C, где A, B и C - переменные в RAM.
Время срабатывания простейших арифметических операторов "C" поддаётся измерению с удовлетворительно точностью (+/- 1 такт) без понижения абстракции до уровня ассемблера и машинных команд, в контексте синтаксиса "С".
При этом работа оптимизатора - последнее дело. Ибо, важнее на данном этапе смысловая проработка задачи и статистическая устойчивость атомарного замера малых промежутков времени без оглядки на платформу MCU и параметры сборки.
Только, что току с того исходника? Зафиксировали факт. Двигаемся далее.
Разработчики компиляторов - они то же программисты, а это значит, что в их работе то же бывают косяки. :-)
компилятор не может понять, что от него хотят
Компилятор не должен "понимать". Компилятор обязан просто переводить алгоритмы программиста в машинный код, ибо программисту виднее, что он хочет посчитать и как. (imho)
В противном случае, имеем то, что имеем.
Вместо обсуждения прикладной задачи по планированию и учёту сверхмалых интервалов времени (вводная статья), профи с уважаемого форума тратят драгоценное внимание на косяки и тараканы в gcc - плотники до блеска натирают молотки вместо забивания гвоздей. :-)
С другой стороны, замусорить машинный код командами, "съёдающими" время без искажения задачи, в режиме -O0, и демонстрировать удивительную производительность с оптимизацией, - отличный маркетинговый ход! Троекратное "у-ра" маркетологам gcc. /* сарказм */ :-)
Ничего, что команды сложения, вычитания и умножения на M0 выполняются за одинаковое время - 1 такт?
Согласен. В теории Вы правы и документация верна.
На практике простейшая операция выполняется минимум за 4 такта: два такта на загрузку двух операндов из памяти в регистры, один такт, как Вы правильно заметили, уходит на саму примитивную операцию, и один такт на возврат результата из регистра в память.
В эти 4 такта может легко вклинится обработчик прерывания, если ему приспичит. Есть множество других, объективных причин, влияющих на время выполнения простейших операций.
Однако вопрос не в том, как с этим бороться, а в том как это учитывать, планируя гарантированную производительность бюджетного устройства.
В ответе выше дизассемблер машинного кода, полученного штатным gcc из исходника на "C" с опцией -O0.
Допускаю, что ручками на макро-ассемблере можно делать более эффективный код, однако тогда теряется смысл языка высокого уровня, а стоимость разработки поднимется на никому не нужную высоту.
Компилятор: gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12)
Цель - не скорость. Цель - повышение утилизации, интенсивности работы бюджетного MCU.
Оптимизация по размеру (-Os) выбрана на основании требований к памяти - 32Кб (64К крайний предел).
Что происходит - дополнительные издержки на преобразование полуслов в слова и обратно на операциях с данными, менее слова.
Оптимизатор запутать нельзя, т.к. на примитивных операциях он отдыхает. Да, и трудно представить, как оптимизировать формулу: A = B + C, где все слагаемые int ?
Казалось бы ... Отключение оптимизации (-O0) ухудшило арифметику на int16:
про разницу int16 и int32 уже закрадываются сомнения в правильности измерений.
Замедление скорости вычислений для int16 обосновано в ответе на комментарий выше. Там же ассемблерный код для оператора: L3 = L2 + L1, где все переменные с типом int32.
Для той же формулы с переменными int16, в машинном коде появятся дополнительные команды, преобразующие машинное полу-слово в слово и обратно.
На операциях int16 применяются дополнительные команды на преобразование полуслова 16bit в слово в 32. Процессор не может оперировать половиной регистра.
Два операнда по 16bit - две дополнительные команды ассемблера при загрузке данных из памяти в 32bit регистры процессора, плюс ещё одна дополнительная команда при выгрузке результата из регистра процессора в память.
Итого на три такта больше, чем сложение целых в формате 32bit, где всего четыре команды: две загрузки, одно сложение и одна выгрузка:
Применяемая точность измерения сверхмалых интервалов +/- 1 такт, поэтому есть расхождения в 1-2 такта времени срабатывания int16 и int32, однако это не существенно для изложенной темы.
Нет.
Менеджер pip нарушается принцип "чистой среды", загружая из сторонних источников исполняемый код, созданный третьими лицами.
Отдавая себе отчёт о времени публикации данной статьи ( девять лет назад ), буду рад возможности осудить возможность ускорения и повышения точности "рисования", если задача ещё актуальна.
Был опыт создания скоростных растровых графических примитивов (прямые, ломаные, кривые, штриховки, заливки и т.д. )
Вы абсолютно правы.
В угоду Вашей правоты замечу, что задача планировалась под чип STM32L031, но в процессе подготовки текста под рукой оказались другие оценочные платы, на что указывает директива условной компиляции в файле заголовка.
Учить читателя, что-либо доказывать, объяснять - такой цели здесь нет. Есть практический опыт, изложенный в доступной форме, пригодной для проверки, обсуждения, заимствования и улучшения. Лицензия в заголовке исходного кода это позволяет.
Обременять популярную статью согласно "ГОСТ Р 19*", "ГОСТ Р 34*" или там "ГОСТ Р ИСО 9000, 9001, 9004" и т.п. считаю нецелесообразным.
(imho)
Согласен, DWT - интересная вещица.
В статье опыт решения прикладной задачи динамического контроля и корректировки множества, в том числе вложенных, малых интервалов времени, в т.ч. в режиме отладки, с минимальным набором аппаратных ресурсов на бюджетном микроконтроллере M0.
Полученный метод позволяет оперировать вложенными интервалами времени от нескольких тактов до нескольких часов с известной точностью, что и требовалось в прикладной задаче.
Проектирование промышленных программ исключает обстоятельства, характеризуемые словосочетаниями "потом появляется", "вдруг" и т.п., ибо такие программы сопровождаются технической документацией, содержащей обязательный раздел "Технические условия эксплуатации", обоснованные в ТЭП ещё на этапе планирования разработки до создания исходного текста программы.
Время реакции на событие, предсказуемость времени реакции зависит от времени выполнения критических участков кода, обслуживающих событие. Об этом статья.
Практика всегда опирается на результат с заданной степенью точности. Достижение результата с заданной степенью точности всегда удовлетворяет практиков без оглядки на способ получения такого результата.
Приоритет способа в ущерб результату - это область теоретиков и учеников, допускающая непредвиденные обстоятельства типа "потом появляется", "вдруг" и т.д.
При всём уважении к теоретикам, здесь о практике.
Что такое "чистая синтетика"? - сомневаюсь, что правильно понимаю этот термин.
Для оценки соответствия машинного кода и кода на "C"; для снижения энтропии системы сборки через исключение модуля искажающего первичную алгоритмическую основу.
Дополнительная аргументация здесь:
https://habr.com/ru/post/591925/comments/#comment_23758799
Вы правы, есть загрузка из памяти в регистры, и прерывания вклиниваются когда посчитают нужным.
В статье о поиске способа простого планирования и учёта времени работы ответственных участков кода оставаясь в парадигме языка "C".
Говоря о планировании, подразумеваем не обязательство, а цель, допускающую отклонение в пределах установленного допуска.
Это значит - без запретов прерываний, без порочного ожидания "delay()" и т.п.
Предположительное начальное приближения к решению задачи - подсчёт числа простейших арифметических операторов на языке "С" на контрольном участке кода.
Под простейшим оператором понимаем конструкцию типа: A = B + C, где A, B и C - переменные в RAM.
Время срабатывания простейших арифметических операторов "C" поддаётся измерению с удовлетворительно точностью (+/- 1 такт) без понижения абстракции до уровня ассемблера и машинных команд, в контексте синтаксиса "С".
При этом работа оптимизатора - последнее дело. Ибо, важнее на данном этапе смысловая проработка задачи и статистическая устойчивость атомарного замера малых промежутков времени без оглядки на платформу MCU и параметры сборки.
Это нормально.
Забавно иначе, включение оптимизации замедляет работу алгоритма из 7-и строчек. :-)
Такой случай в примере для FPU x86 здесь, включая исходник:
https://habr.com/ru/post/562572/
Только, что току с того исходника? Зафиксировали факт. Двигаемся далее.
Разработчики компиляторов - они то же программисты, а это значит, что в их работе то же бывают косяки. :-)
Компилятор не должен "понимать". Компилятор обязан просто переводить алгоритмы программиста в машинный код, ибо программисту виднее, что он хочет посчитать и как. (imho)
В противном случае, имеем то, что имеем.
Вместо обсуждения прикладной задачи по планированию и учёту сверхмалых интервалов времени (вводная статья), профи с уважаемого форума тратят драгоценное внимание на косяки и тараканы в gcc - плотники до блеска натирают молотки вместо забивания гвоздей. :-)
С другой стороны, замусорить машинный код командами, "съёдающими" время без искажения задачи, в режиме -O0, и демонстрировать удивительную производительность с оптимизацией, - отличный маркетинговый ход! Троекратное "у-ра" маркетологам gcc. /* сарказм */ :-)
Но, я за программистов. :-)
Согласен. В теории Вы правы и документация верна.
На практике простейшая операция выполняется минимум за 4 такта: два такта на загрузку двух операндов из памяти в регистры, один такт, как Вы правильно заметили, уходит на саму примитивную операцию, и один такт на возврат результата из регистра в память.
В эти 4 такта может легко вклинится обработчик прерывания, если ему приспичит. Есть множество других, объективных причин, влияющих на время выполнения простейших операций.
Однако вопрос не в том, как с этим бороться, а в том как это учитывать, планируя гарантированную производительность бюджетного устройства.
Да нет там ничего "военного":
Целочисленные переполнения - с этим тоже всё хорошо - проверенная временем практика непрерывной индексации циклических процессов.
У сборки -O0 cмысл в том, чтобы сузить зону поиска трабла, через исключение влияния оптимизатора кода.
Кстати, а для какого микроконтроллера Вы генерируете проверочный код?
Дизассемблер машинного кода без оптимизации (-O0) для int16 и int32, версия компилятора здесь:
https://habr.com/ru/post/591925/comments/#comment_23756785
Глядя на дизассемблер понятно, откуда набегают лишние такты.
Код на "C" для 16bit (сложение) выглядит так:
Код на "C" для 32bit (сложение) выглядит похоже:
Участки кода для других арифметических операторов выглядят идентично, за исключением арифметического оператора в строке #7.
Да, нормально оно выглядит. :-)
В ответе выше дизассемблер машинного кода, полученного штатным gcc из исходника на "C" с опцией -O0.
Допускаю, что ручками на макро-ассемблере можно делать более эффективный код, однако тогда теряется смысл языка высокого уровня, а стоимость разработки поднимется на никому не нужную высоту.
Ответы в порядке поступления вопросов.
Компилятор:
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12)
Цель - не скорость.
Цель - повышение утилизации, интенсивности работы бюджетного MCU.
Оптимизация по размеру (-Os) выбрана на основании требований к памяти - 32Кб (64К крайний предел).
Что происходит - дополнительные издержки на преобразование полуслов в слова и обратно на операциях с данными, менее слова.
Оптимизатор запутать нельзя, т.к. на примитивных операциях он отдыхает. Да, и трудно представить, как оптимизировать формулу:
A = B + C, где все слагаемые int ?
Казалось бы ...
Отключение оптимизации (-O0) ухудшило арифметику на int16:
Причина замедления арифметики int16 - издержки на выравнивание границ и слов.
Формула на "С":
A = B + C
все переменные int16
Соответствующий машинный код:
Формула на "С":
A = B + C,
Все переменные int32
Соответствующий машинный код:
Как говорится, найдите 10 отличий. :-)
К вопросу о доверии к методу измерения.
Ассемблерные команды семейства Thumb2 выполняется, в основном, за 1 такт.
Если посчитать строчки ассемблера, то их число совпадёт с результатом соответствующих измерений в тактах из таблице выше по тексту.
О том, что в премиальном сегменте "железа" больше возможностей - спору нет.
Однако, в статье речь о бюджетных MCU, когда главное преимущество - низкая цена.
Замедление скорости вычислений для int16 обосновано в ответе на комментарий выше. Там же ассемблерный код для оператора:
L3 = L2 + L1,
где все переменные с типом int32.
Для той же формулы с переменными int16, в машинном коде появятся дополнительные команды, преобразующие машинное полу-слово в слово и обратно.
CubeIDE, gcc (-std=gnu11, --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb - Os)
На операциях int16 применяются дополнительные команды на преобразование полуслова 16bit в слово в 32. Процессор не может оперировать половиной регистра.
Два операнда по 16bit - две дополнительные команды ассемблера при загрузке данных из памяти в 32bit регистры процессора, плюс ещё одна дополнительная команда при выгрузке результата из регистра процессора в память.
Итого на три такта больше, чем сложение целых в формате 32bit, где всего четыре команды: две загрузки, одно сложение и одна выгрузка:
08003146: ldr r2, [r7, #12]
08003148: ldr r3, [r7, #8]
0800314a: add r3, r2
0800314c: str r3, [r7, #4]
Применяемая точность измерения сверхмалых интервалов +/- 1 такт, поэтому есть расхождения в 1-2 такта времени срабатывания int16 и int32, однако это не существенно для изложенной темы.
В качестве эталона для сверки лучше брать стандартную функцию из "math.h". В статье, как эталон, применялась sqrt_fpu(...).
Замечательный, быстрый код.
Вы проверяли алгоритм на отсутствие ошибок при округлении?
Публикуйте, пожалуйста код функции без пропусков (многоточий). Теряется контекст.
Относительно округления результата до ближайшего целого — без вариантов, на всём множестве значений аргумента [ 0… 0xFFFFFFFFU ].