这是正常的memcpy覆盖它刚刚写入的数据吗



我使用memcpy()向设备写入数据,使用逻辑分析器/PCIe分析器,我可以看到实际的存储。

我的设备获得了比预期更多的存储空间。

例如,

auto *data = new uint8_t[1024]();
for (int i=0; i<50; i++){
memcpy((void *)(addr), data, i);
}

对于i=9,我看到这些商店:

  • 4B从字节0到3
  • 4B从字节4到7
  • 3B从字节5到7
    • 1B仅对齐,重新写入相同的数据->低效和无用的存储
  • 1B字节8

最终,所有9字节都被写入,但memcpy创建了一个额外的3B存储区,重新写入它已经写入的内容,仅此而已。

这是预期的行为吗?问题是对于C和C++,我很想知道为什么会发生这种情况,它似乎效率很低。

这是预期的行为吗?

预期的行为是,只要结果符合C抽象机的规则,它就可以做任何它想做的事情(包括写过末尾,尤其是在"向寄存器中读取8个字节,修改寄存器中的第一个字节,然后写8个字节"的方式中(。

使用逻辑分析器/PCIe分析器来查看实际存储远远超出了本发明的范围;就好像遵循了抽象机器的规则一样工作;有任何期望都是不合理的。

具体而言;你不能假设写入将以任何特定的顺序发生,不能假设任何单个写入的大小,不能假设写入不会重叠,不能假设不会有超过区域末尾的写入,不能假设实际上会发生写入(没有volatile(,并且甚至不能假设CHAR_BIT不大于8(或者memcpy(dest, source, 10);不要求写入20个八位字节/"8位字节"(。

如果您需要关于写入的保证,那么您需要自己强制执行这些保证(例如,可能创建volatile字段的结构以强制编译器确保写入按特定顺序进行,可能使用带有显式栅栏/屏障的内联汇编,等等(。

以下说明了memcpy可以以这种方式实现的原因。

为了复制9个字节,从4字节对齐的地址开始,memcpy发出以下指令(称为伪代码(:

  • 从源+0加载四个字节,并将四个字节存储到目标+0
  • 从源+4加载四个字节,并将四个字节存储到目标+4
  • 从源+5加载四个字节,并将四个字节存储到目标+5

处理器在硬件中通过以下数据传输实现存储指令:

  • 由于目标+0已对齐,因此将4个字节存储到目标+0
  • 由于目的地+4是对齐的,所以将4个字节存储到目的地+4
  • 由于目的地+5未对齐,因此将3个字节存储到目的地+3,并将1个字节存储至目的地+8

这是一种简单有效的写入memcpy:的方法

  • 如果长度小于四个字节,则跳转到相应的单独代码
  • 循环复制四个字节,直到剩余的字节少于四个
  • 如果长度不是四的倍数,则将四个字节从源+length−4复制到目标+length‑4

复制最后几个字节的单个步骤可能比在不同情况下分支到三个不同的情况更有效。

最新更新