C++ 32位嵌入式平台上的memset-slow

C++ 32位嵌入式平台上的memset-slow,c++,embedded,memset,C++,Embedded,Memset,我正在开发一款嵌入式设备(STM32,ARM Cortex M4),希望memset和类似的功能能够优化速度。然而,我注意到比预期慢得多的行为。我正在使用带有-O3优化标志的GNU ARM嵌入式编译器/链接器(ARM-none-eabi-gcc等) 我查看了反汇编,发现memset函数一次写入一个字节,并在每次迭代时重新检查边界 0x802e2c4 <memset>: add r2, r0 0x802e2c6 <memset+2>: mov r3, r0 0x802e

我正在开发一款嵌入式设备(STM32,ARM Cortex M4),希望
memset
和类似的功能能够优化速度。然而,我注意到比预期慢得多的行为。我正在使用带有
-O3
优化标志的GNU ARM嵌入式编译器/链接器(
ARM-none-eabi-gcc
等)

我查看了反汇编,发现
memset
函数一次写入一个字节,并在每次迭代时重新检查边界

0x802e2c4 <memset>: add r2, r0
0x802e2c6 <memset+2>:   mov r3, r0
0x802e2c8 <memset+4>:   cmp r3, r2
0x802e2ca <memset+6>:   bne.n   0x802e2ce <memset+10>
0x802e2cc <memset+8>:   bx  lr
0x802e2ce <memset+10>:  strb.w  r1, [r3], #1
0x802e2d2 <memset+14>:  b.n 0x802e2c8
这个问题与现有问题类似,但不同之处在于它针对的是嵌入式平台


GNU ARM嵌入式软件包中是否有现成的优化memset?如果是,我如何访问它?

不确定GNU Tools ARM Embedded是否有优化的memset,或者如何通过链接器选项访问它,但可以在汇编中手动优化它。定义之后,链接器使用了这个版本,但没有抱怨重新定义的函数,这对我来说很奇怪。整体速度增加约为9倍(即,此版本所需时间约为原始字节方法的11%)

//memset的优化版本
//我们把这个地区分成几个部分
//
//基地ptr
//*存储单个字节
//中1
//*存储单词,每次4个
//mid2
//*存储单词,每次1个
//mid3
//*存储单个字节
//结束
//
//对于大型缓冲区,大部分时间都花在mid1和mid2之间,这是
//高度优化。
void*memset(void*base\u ptr,int x,size\u t长度){
const uint32_t int_size=sizeof(uint32_t);
静态断言(sizeof(uint32_t)==4,“仅支持32位大小”);
//查找第一个单词对齐的地址
uint32_t ptr=(uint32_t)base_ptr;
//获取要设置的内存结尾
uint32_t end=ptr+长度;
//获取起始位置处/之后第一个字对齐地址的位置,但不是
//结束后
uint32_t mid1=(ptr+int_size-1)/int_size*int_size;
如果(中间1>结束){
mid1=末端;
}
//获取结尾处/之前最后一个单词对齐地址的位置
uint32\u t mid3=结束/内部大小*内部大小;
//获取优化节的结束位置
uint32\u t mid2=mid1+(mid3-mid1)/(4*整数大小)*(4*整数大小);
//创建一个字号大小的整数
uint32_t值=0;
对于(uint16_t i=0;i不带
-specs=nano.specs的value链接。这将使用C库版本,其中包括
memset
,该版本针对速度而不是大小进行了优化。这将引入许多其他函数的更大版本(通常怀疑:
printf
malloc
)如果你在C++中开发(如标签所示),你应该更好地使用<代码> STD::填充< /Cult>。这有很大的机会被编译器优化。“NeLIB”C库已经针对速度进行了优化,并进行了32位写入。只有在内存地址正确对齐时才会触发此优化,因此请确保情况属实。基于newlib的发行版中的
libc.a
变体都使用了上述优化。您使用的是哪个发行版,源代码是什么,如何编译ile/link?@SergeyA std::fill的性能与memset相同。为什么人们会期望它比memset优化得更好?@devtk因为memset(除非是内在的)是库提供的函数,可以是任何函数,也可以是泛型函数。当您使用
std::fill
时,编译器可以在站点上进行优化。但是,我必须承认,我在尝试使用godbolt时没有看到优化。@Erlkoenig我正在使用,确切的版本在问题的路径中。链接器命令指定s“-specs=nosys.specs-specs=nano.specs”等等。它说它使用了newlib,但memset函数显然与您链接的函数不匹配。
C:\Program Files (x86)\GNU Tools Arm Embedded\7 2018-q2-update\arm-none-eabi\include\string.h
C:\Program Files (x86)\GNU Tools Arm Embedded\7 2018-q2-update\arm-none-eabi\include\c++\7.3.1\cmath
// optimized version of memset
// we split up the region into several segments
//
// base_ptr
// * store single bytes
// mid1
// * store words, 4 at a time
// mid2
// * store words, 1 at a time
// mid3
// * store single bytes
// end
//
// For large buffers, most of the time is spent between mid1 and mid2 which is
// highly optimized.
void * memset(void * base_ptr, int x, size_t length) {
  const uint32_t int_size = sizeof(uint32_t);
  static_assert(sizeof(uint32_t) == 4, "only supports 32 bit size");
  // find first word-aligned address
  uint32_t ptr = (uint32_t) base_ptr;
  // get end of memory to set
  uint32_t end = ptr + length;
  // get location of first word-aligned address at/after the start, but not
  // after the end
  uint32_t mid1 = (ptr + int_size - 1) / int_size * int_size;
  if (mid1 > end) {
    mid1 = end;
  }
  // get location of last word-aligned address at/before the end
  uint32_t mid3 = end / int_size * int_size;
  // get end location of optimized section
  uint32_t mid2 = mid1 + (mid3 - mid1) / (4 * int_size) * (4 * int_size);
  // create a word-sized integer
  uint32_t value = 0;
  for (uint16_t i = 0; i < int_size; ++i) {
    value <<= 8;
    value |= (uint8_t) x;
  }
  __ASM volatile (
  // store bytes
  "b Compare1%=\n"
  "Store1%=:\n"
  "strb %[value], [%[ptr]], #1\n"
  "Compare1%=:\n"
  "cmp %[ptr], %[mid1]\n"
  "bcc Store1%=\n"
  // store words optimized
  "b Compare2%=\n"
  "Store2%=:\n"
  "str %[value], [%[ptr]], #4\n"
  "str %[value], [%[ptr]], #4\n"
  "str %[value], [%[ptr]], #4\n"
  "str %[value], [%[ptr]], #4\n"
  "Compare2%=:\n"
  "cmp %[ptr], %[mid2]\n"
  "bcc Store2%=\n"
  // store words
  "b Compare3%=\n"
  "Store3%=:\n"
  "str %[value], [%[ptr]], #4\n"
  "Compare3%=:\n"
  "cmp %[ptr], %[mid3]\n"
  "bcc Store3%=\n"
  // store bytes
  "b Compare4%=\n"
  "Store4%=:\n"
  "strb %[value], [%[ptr]], #1\n"
  "Compare4%=:\n"
  "cmp %[ptr], %[end]\n"
  "bcc Store4%=\n"
  : // no outputs
  : [value] "r"(value),
  [ptr] "r"(ptr),
  [mid1] "r"(mid1),
  [mid2] "r"(mid2),
  [mid3] "r"(mid3),
  [end] "r"(end)
  );
  return base_ptr;
}