重新定义诸如memcpy之类的标准仍有性能优势吗?

时间:2019-02-22 09:23:59

标签: c optimization embedded

我的问题很简单,但是我找不到明确的答案,所以我在这里。

如今,C编译器比几年前更有效。在新项目中重新定义memcpy或memset之类的功能仍然有任何优势吗?

更具体地说,假设项目上的目标MCU是32位ARM内核,例如Cortex M或A。并使用GNU ARM工具链。

谢谢

4 个答案:

答案 0 :(得分:4)

否,重新定义memcpy无益。问题是您的 own 函数不能像标准库memcpy一样工作,因为C编译器知道名称为memcpy的函数就是(C11 7.24.2.1p2)的函数。

  

[...]将n指向的对象中的s2个字符复制到s1指向的对象中。如果在重叠的对象之间进行复制,则行为是不确定的。

,并且明确允许构造行为as if的任何等效程序,此类函数被称为。有时,它甚至会导致甚至不接触内存的代码,memcpy被寄存器副本替换,或者使用未对齐的加载指令将值从存储器加载到寄存器中。

如果您在汇编程序中定义自己的superduperfastmemcpy,则C编译器将不知道其功能,并在需要时会奴役地调用它。


可能是有益的,但是有一个特殊的例程可以在以下情况下复制 large 个内存块:已知源地址和目标地址都可以被1k整除,并且所有长度始终可以被1k整除;在这种情况下,可能有几种可供选择的例程,可以在程序启动时进行计时,并选择使用最快的例程。当然,在周围复制大量内存是大多数不良设计 ...

的标志

答案 1 :(得分:4)

该问题仅能作为见解而回答,因为您对目标和工具链非常了解。不可能一概而论(从未如此)。

GNU ARM工具链使用Newlib C库。 Newlib旨在与架构无关且可移植。这样,它是用C而不是用汇编器编写的,因此它的性能由编译器的代码生成以及构建库时应用的编译器选项确定。可以针对非常特定的ARM体系结构进行构建,也可以针对更通用的ARM指令子集进行构建。也会影响性能。

此外,Newlib本身可以使用各种条件编译选项来构建,例如PREFER_SIZE_OVER_SPEED__OPTIMIZE_SIZE__

现在,如果您能够生成比编译器更好的ARM汇编器代码(并且有时间),那就太好了,但是这样的kung-foo编码技能越来越稀少,坦率地说,也越来越不必要。您是否有足够的汇编程序专业知识来击败编译器?您有时间吗,您是否真的想为您可能使用的每种体系结构做到这一点?这可能是过早的优化,并且没有效果。

在某些情况下,在具有此功能的目标上,可能值得设置内存到内存的DMA传输。 GNU ARM编译器将不会生成DMA代码,因为它是芯片供应商所依赖的,而不是ARM体系结构的一部分。但是,memcpy是通用的,目的是实现任意的副本大小对齐和线程安全。对于DMA最佳的特定情况,最好定义一个新的不同名称的例程,并在需要的地方使用它,而不是重新定义memcpy并冒着它对于可能占主导地位或多线程的小副本不理想的风险。应用程序。

例如,可以在here中看到memcpy()在Newlib中的实现。这是一个合理的惯用实现,因此对通常在惯用代码上效果最好的典型编译器优化程序表示同情。替代实现在未优化的编译中可能会表现更好,但如果它是“异常”,则优化器可能无法正常工作。如果要用汇编器编写它,则必须要比编译器要好-您将是一种稀有的东西,尽管不一定有价值(商业上)。也就是说,查看此特定的实现,对于超大尺寸实现中的大型未对齐块,它的效率确实要低得多。可以以少量费用将其改进为更常见的对齐副本。

答案 2 :(得分:3)

memcpy之类的函数属于标准库,并且几乎可以确定它们是在汇编器中实现的,而不是在C中实现的。

如果重新定义它们,则它的工作速度肯定会变慢。如果要优化memcpy,则应改用memmove或将指针声明为restrict,以指示它们不重叠并以与memmove一样快的方式处理。

为给定的技术编写标准C库的那些工程师,可以确保他们使用现有的汇编器功能来更快地移动内存。

编辑:

以一些评论为基础,允许保留复制语义的每一代代码(包括用mov指令或其他代码替换memcpy)。

对于复制算法(包括newlib正在使用的算法),您可以选中this article。本文引用:

  

特殊情况如果您了解所有要复制的数据   以及运行memcpy的环境,您也许可以   创建运行速度非常快的专业版本

答案 3 :(得分:0)

这里有几点,也许上面已经提到过:

  • 经过认证的库:通常在安全受限的环境中未经过认证可以运行。通常永远不会提供根据某些ASPICE / CMM级别开发的库,因此这些库不能在此类环境中使用。
  • 特定于架构的实现:也许您自己的实现使用了某些libs无法提供的,非常有针对性的特定功能,例如特定的加载/存储指令(SIMD,基于矢量的指令),甚至针对更大数据的基于DMA的实现,或者在具有不同核心架构的多处理器的情况下使用不同的实现(例如,具有e200z4和e200z7内核的NXP S32,或ARM M5 vs. A53),并且lib需要找出调用哪个内核以获得最佳性能
  • 由于嵌入式开发是根据C标准的“独立”而非“托管”的,因此该标准的很大一部分是“实现定义的”或什至“未指定的”,其中包括库。