Есть ли еще преимущество в производительности для переопределения стандарта, такого как memcpy? - PullRequest
0 голосов
/ 22 февраля 2019

Мои вопросы довольно просты, но я не могу найти четкого ответа, поэтому я здесь.

В настоящее время компиляторы C более эффективны, чем это могло быть несколько лет назад.Есть ли еще какое-то преимущество в переопределении функций, таких как memcpy или memset, в новом проекте?

Чтобы быть более точным, давайте предположим, что целевой MCU в проекте - это 32-битное ядро ​​ARM, такое как Cortex M или A. И GNUARM toolchain используется.

Спасибо

Ответы [ 4 ]

0 голосов
/ 25 февраля 2019

Здесь есть несколько моментов, возможно, уже упомянутых выше:

  • Сертифицированные библиотеки: обычно они не сертифицированы для работы в условиях безопасности.Разработанный в соответствии с определенным уровнем ASPICE / CMM, как правило, никогда не предоставляется, и поэтому эти библиотеки не могут быть использованы в таких средах.
  • Реализации, специфичные для архитектуры: Возможно, ваша собственная реализация использует некоторые очень специфические функции, которые библиотеки могутне предоставлять, например, конкретные инструкции по загрузке / сохранению (SIMD, векторные инструкции), или даже реализацию на основе DMA для больших данных, или использовать разные реализации в случае многопроцессорных систем с различными архитектурами ядра (например, NXP S32 с ядрами e200z4 и e200z7, илиARM M5 против A53), и библиотека должна была бы выяснить, на каком ядре она вызывается, чтобы получить лучшую производительность
  • Поскольку встраиваемая разработка в соответствии со стандартом C "автономна", а не "размещена",большая часть стандарта «определена реализацией» или даже «не определена», и это включает библиотеки.
0 голосов
/ 22 февраля 2019

Нет, переопределять memcpy не выгодно.Проблема в том, что ваша собственная функция не может работать как стандартная библиотека memcpy, потому что компилятор C знает, что функция с именем memcpy является той, которая (C11 7.24.2.1p2)

[...] копирует n символов из объекта, на который указывает s2, в объект, на который указывает s1.Если копирование происходит между объектами, которые перекрываются, поведение не определено.

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

Если вы определяетеИмея superduperfastmemcpy в ассемблере, компилятор C не будет знать о том, что он делает, и будет рабски вызывать его всякий раз, когда его об этом попросят.


Чем может может быть полезен, тем не менее, имеется специальная процедура для копирования больших блоков памяти, когда, например, известно, что адрес источника и получателяделится на 1k, а все длины всегда делятся на 1k;в этом случае может быть несколько альтернативных подпрограмм, которые могут быть рассчитаны при запуске программы, и будет выбрана самая быстрая из них.Конечно, копирование большого количества памяти вокруг является признаком, в основном, плохого дизайна ...

0 голосов
/ 23 февраля 2019

Вопрос только подотчетен, а не вопрос мнения, потому что вы были конкретны в отношении цели и набора инструментов.Невозможно обобщить (и никогда не было).

В цепочке инструментов GNU ARM используется библиотека Newlib C.Newlib разработан, чтобы быть независимым от архитектуры и переносимым.Как таковой он написан на C, а не на ассемблере, поэтому его производительность определяется генерацией кода компилятора и, в свою очередь, параметрами компилятора, применяемыми при сборке библиотеки.Можно построить для очень конкретной архитектуры ARM или для более общего набора команд ARM;это также повлияет на производительность.

Более того, сам Newlib может быть собран с различными вариантами условной компиляции, такими как PREFER_SIZE_OVER_SPEED и __OPTIMIZE_SIZE__.

Теперь, если вы можете генерировать лучший код ассемблера ARM(и есть время), чем компилятор, то это здорово, но такие навыки кодирования кунг-фу становятся все более редкими и, откровенно говоря, все более и более ненужными.Достаточно ли у вас опыта ассемблера, чтобы победить компилятор;у вас есть время, и вы действительно хотите сделать это для каждой архитектуры, которую вы можете использовать?Это может быть преждевременной оптимизацией и быть довольно непродуктивным.

В некоторых случаях для целей с возможностью может быть целесообразно настроить передачу DMA из памяти в память.Компилятор GNU ARM не будет генерировать код DMA, поскольку он зависит от производителя микросхемы и не является частью архитектуры ARM.Однако memcpy является общим назначением для произвольного выравнивания размера копии и безопасности потока.Для конкретных обстоятельств, где DMA является оптимальным, лучше, возможно, определить новую подпрограмму с другим именем и использовать ее там, где это необходимо, а не переопределять memcpy и подвергнуть риску ее неоптимальность для небольших копий, которые могут преобладать, или для многопоточных приложений.

Например, реализацию memcpy() в Newlib можно увидеть здесь .Это разумная идиоматическая реализация и поэтому сочувствующая типичному оптимизатору компилятора, который обычно лучше всего работает с идиоматическим кодом.Альтернативная реализация может работать лучше при неоптимизированной компиляции, но если это «необычно», оптимизатор может работать не так хорошо.Если вы пишете это на ассемблере, вам просто нужно быть лучше компилятора - вы будете редким, хотя и не обязательно ценным (коммерчески) товаром.Тем не менее, глядя на эту конкретную реализацию, она выглядит гораздо менее эффективной для больших невыровненных блоков в реализации скорость-по-размеру.Можно было бы улучшить это за несколько небольших затрат, возможно, за счет более распространенных выровненных копий.

0 голосов
/ 22 февраля 2019

Такие функции, как memcpy, принадлежат стандартной библиотеке и почти наверняка реализованы в ассемблере, а не в C.

Если вы переопределите их, то, несомненно, будет работать медленнее.Если вы хотите оптимизировать memcpy, вы должны либо использовать memmove, либо объявить указатели как restrict, чтобы сказать, что они не перекрываются, и обрабатывать их так же быстро, как memmove.

Эти инженерыкоторый написал стандартную библиотеку C для данной архитектуры, чтобы точно использовать существующую функцию ассемблера для ускорения перемещения памяти.

EDIT:

Принимая замечания из некоторых комментариев, каждое поколение кода, которое хранитсемантика копирования (включая замену memcpy на mov-инструкции или другой код) разрешена.

Для алгоритмов копирования (включая алгоритм, который используется newlib ) вы можете проверить эта статья .Цитата из этой статьи:

Особые ситуации Если вы знаете все о копируемых данных, а также о среде, в которой работает memcpy, вы можете создать специализированную версию, которая работает очень быстро

...