Могут ли более быстрые процессоры / часы выполнять больше кода?

9

Я пишу программу для работы на ATmega 328, которая работает на частоте 16 МГц (это Arduino Duemilanove, если вы их знаете, это чип AVR).

У меня есть процесс прерывания, работающий каждые 100 микросекунд. Я бы сказал, невозможно определить, сколько «кода» вы можете выполнить за один цикл из 100 микросекунд (я пишу на C, который предположительно преобразуется в сборку, а затем в двоичное изображение?).

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

Верно ли мое понимание того, что мой процессор с тактовой частотой 16 МГц выполняет 16 миллионов циклов в секунду (это означает 16 циклов в микросекунду 16 000 000/1 000/1 000); И поэтому, если я хочу сделать больше в моем цикле на 100 микросекунд, покупка более быстрой модели, такой как версия на 72 МГц, даст мне 72 цикла в микросекунду (72 000 000/1000/1000)?

В настоящее время он работает немного медленнее, то есть он занимает чуть более 100 микросекунд, чтобы сделать цикл (как долго точно сказать трудно, но он постепенно отстает), и я хотел бы сделать немного больше, это нормальный подход, чтобы получить более быстрый чип или я сошел с ума?

jwbensley
источник
.... ATmega328 не является чипом ARM. Это AVR.
Викацу
Ура, исправлено!
Jwbensley

Ответы:

9

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

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

Что касается практических вопросов, для простого устройства, такого как AVR, мой ответ будет более или менее "да". Удвоение вашей тактовой частоты должно вдвое сократить время выполнения любой данной функции. Однако для AVR они не будут работать быстрее, чем 20 МГц, поэтому вы можете «разогнать» свой Arduino еще на 4 МГц.

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

vicatcu
источник
Привет, спасибо за Ваш информативный ответ! Я видел один из них ( coolcomponents.co.uk/catalog/product_info.php?products_id=808 ), вы сказали, что AVR не может работать быстрее, чем 20 МГц, почему? Микросхема на приведенной выше плате ( uk.farnell.com/stmicroelectronics/stm32f103rbt6/… ) представляет собой ARM с частотой 72 МГц. Могу ли я ожидать от этого разумного увеличения производительности, как описано выше?
Jwbensley
2
Удвоение скорости обработки может не увеличить вашу пропускную способность, так как вы можете начать превышать скорость, с которой команды могут быть извлечены из флэш-памяти. В этот момент вы начинаете нажимать «Состояния ожидания флэш», когда процессор останавливается, ожидая получения инструкции от флэш-памяти. Некоторые микроконтроллеры решают эту проблему, позволяя выполнять код из ОЗУ, что намного быстрее, чем FLASH.
Majenko
@Majenko: забавно, мы оба сделали одно и то же.
Джейсон С
Бывает ... у тебя лучше, чем у меня :)
Majenko 28.10.11
1
Хорошо, я пометил ответ Викачу как «ответ». Я чувствую, что это было наиболее уместно в отношении моего первоначального вопроса о скорости, касающегося производительности, хотя все ответы великолепны, и я действительно расстроен ответами каждого. Они показали мне, что это более широкий предмет, чем я впервые осознал, и поэтому все они
многому
8

Ответ @ vicatcu довольно исчерпывающий. Еще одна вещь, на которую следует обратить внимание, - это то, что ЦП может переходить в состояния ожидания (циклы зависания ЦП) при доступе к вводу / выводу, включая память программ и данных.

Например, мы используем DSP TI F28335; некоторые области ОЗУ находятся в состоянии ожидания 0 для памяти программ и данных, поэтому, когда вы выполняете код в ОЗУ, он выполняется с 1 циклом на инструкцию (за исключением тех инструкций, которые занимают более 1 цикла). Однако когда вы выполняете код из флэш-памяти (встроенной EEPROM, более или менее), он не может работать на полных 150 МГц и работает в несколько раз медленнее.


Что касается высокоскоростного кода прерывания, вы должны научиться многим вещам.

Во-первых, познакомьтесь с вашим компилятором. Если компилятор делает хорошую работу, он не должен быть намного медленнее, чем сборка вручную. (где «намного медленнее»: с моей стороны будет хорошо коэффициент 2; коэффициент 10 будет неприемлемым) Вам нужно узнать, как (и когда) использовать флаги оптимизации компилятора, и время от времени вы должны искать на выходе компилятора, чтобы увидеть, как это происходит.

Некоторые другие вещи, которые вы можете сделать компилятором для ускорения кода:

  • используйте встроенные функции (не помню, поддерживает ли это C или это только C ++ - ism), как для небольших функций, так и для функций, которые будут выполняться только один или два раза. Недостатком является то, что встроенные функции трудно отлаживать, особенно если включена оптимизация компилятора. Но они сохраняют вам ненужные последовательности вызова / возврата, особенно если абстракция «функции» предназначена для концептуального проектирования, а не для реализации кода.

  • Посмотрите в руководстве по вашему компилятору, есть ли в нем встроенные функции - это встроенные функции, зависящие от компилятора, которые отображаются непосредственно в инструкции по сборке процессора; У некоторых процессоров есть инструкции по сборке, которые делают полезные вещи, такие как мин / макс / бит в обратном направлении, и вы можете сэкономить на этом время.

  • Если вы выполняете численные вычисления, убедитесь, что вы не вызываете функции математической библиотеки без необходимости. У нас был один случай, когда код был похож y = (y+1) % 4на счетчик с периодом 4, ожидая, что компилятор реализует модуль 4 как побитовое И. Вместо этого это назвало библиотеку математики. Поэтому мы заменили y = (y+1) & 3на то, что хотели.

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

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

Наконец, есть очень важный навык, который приходит с опытом - как общим, так и с конкретными комбинациями процессор / компилятор: знание, когда и когда не следует оптимизировать . В общем, ответ не оптимизировать. Цитата Дональда Кнута часто публикуется в StackOverflow (обычно это только последняя часть):

Мы должны забыть о малой эффективности, скажем, в 97% случаев: преждевременная оптимизация - корень всех зол

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

Признаки того, что части вашего кода хорошо подходят для кодирования ассемблера:

  • функции, которые хорошо содержатся, четко определенные небольшие подпрограммы вряд ли изменятся
  • функции, которые могут использовать определенные инструкции по сборке (минимальное / максимальное / правое смещение / и т. д.)
  • функции, которые вызываются много раз (вы получаете множитель: если вы сохраняете 0.5usec на каждый вызов, и он вызывается 10 раз, это экономит вам 5 usec, что важно в вашем случае)

Изучите соглашения о вызовах функций вашего компилятора (например, где он помещает аргументы в регистры и какие регистры он сохраняет / восстанавливает), чтобы вы могли писать подпрограммы на C-callable.

В моем текущем проекте у нас есть довольно большая кодовая база с критическим кодом, который должен работать с прерыванием 10 кГц (100usec - звучит знакомо?), И не так много функций, которые написаны на ассемблере. Это вычисления CRC, очереди программного обеспечения, компенсация усиления / смещения АЦП.

Удачи!

Джейсон С
источник
Хороший совет по эмпирическим методам измерения времени выполнения
Викацу
Еще один отличный ответ на мой вопрос, большое спасибо Jason S за этот потрясающий кусок знаний! Две вещи очевидны после прочтения этого; Во-первых, я могу увеличить прерывание с каждых 100 мкс до 500 мкс, чтобы дать коду больше времени для выполнения, и я понимаю, что сейчас мне не очень удобно, когда я так быстро. Во-вторых, я думаю, что мой код может быть слишком неэффективным, с более длительным временем прерывания и лучшим кодом все может быть в порядке. Stackoverflow - лучшее место для размещения кода, поэтому я опубликую его там и поставлю здесь ссылку, если кто-то захочет взглянуть и дать какие-либо рекомендации, пожалуйста, сделайте: D
jwbensley
5

Еще одна вещь, на которую стоит обратить внимание - вероятно, вы можете выполнить некоторые оптимизации, чтобы сделать ваш код более эффективным.

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

Мне удалось значительно увеличить скорость, привязав переменную главного счетчика к регистру с помощью (на моем µC и компиляторе - по-другому у вас):

register unsigned int pointer asm("W9");

Я не знаю формат для вашего компилятора - RTFM, но вы сможете кое-что сделать, чтобы ускорить выполнение вашей рутины, не переключаясь на сборку.

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

Majenko
источник
LOL Я "одновременно" прокомментировал свой собственный ответ о настройке ассемблера и распределении регистров :)
vicatcu
Если он потребляет 100 мегапикселей на процессоре 16 МГц - он, очевидно, довольно большой, так что это много кода для оптимизации. Я слышал, что современные компиляторы производят примерно в 1,1 раза больше кода, чем сборка, оптимизированная вручную. Абсолютно не стоит для такой огромной рутины. Для бритья 20% от функции 6 строк, возможно ...
DefenestrationDay
1
Не обязательно ... Это может быть всего 5 строк кода в цикле. И дело не в размере кода, а в эффективности кода . Вы можете написать код по-другому, чтобы он работал быстрее. Я знаю, что сделал для меня в режиме прерывания. Например, жертвуя размером ради скорости. Выполняя один и тот же код 10 раз подряд, вы экономите время, необходимое для выполнения цикла - и связанных переменных счетчика. Да, код в 10 раз длиннее, но работает быстрее.
Majenko
Привет Majenko, я не знаю ассемблера, но я думал о том, чтобы изучить его, и думал, что Arduino будет менее сложным, чем мой настольный компьютер, так что это хорошее время для изучения, особенно если я хочу знать больше о том, что происходит, и более низкий уровень. Как уже говорили другие, я бы не стал переписывать все это только определенными частями. Насколько я понимаю, я могу входить и выходить из ASM в C, верно ли это, можно ли достичь такого сочетания C и ASM? Я опубликую на stackoverflow для специфики, только после общей идеи.
Jwbensley
@javano: Да. Вы можете включать и выключать ASM в C. Многие встроенные системы были написаны так - в смеси C и ассемблера - главным образом потому, что было несколько вещей, которые просто нельзя было сделать в примитивных компиляторах C, доступных на время. Однако современные компиляторы C, такие как gcc (который является компилятором, используемым Arduino), теперь обрабатывают большинство и во многих случаях все, что раньше требовало ассемблера.
Дэвидкари