赋值运算符-每个成员或二进制副本

Assignment operator - per member or binary copy?

本文关键字:二进制 副本 成员 赋值运算符      更新时间:2023-10-16

我想知道,只要所有包含的赋值除了将二进制值传输到地址之外没有任何副作用,那么只将字节从源对象指针复制到目标,而不是将每个目标成员分配给每个源成员,这不是更有效吗?

作为一个子问题。。。我想知道读取模式是如何影响内存控制器负载的。显然,当MC从正确对齐的地址读取其全宽时,它是最有效的。也许将所有成员合并为用于对齐的最大宽数据类型序列会更有效,例如,如果MC是64位,成员数据集是10个字节,则复制64位和16位的值,即使成员是10个char,因此这种复制方式将比一次复制每个成员字符更好地使MC饱和。

只要只需要字节复制,就让编译器为您生成默认的
它也会自动优化它,如果最有效的话,使用memcpy

额外的好处是,如果您添加了一个具有更复杂(自包含)语义的成员,编译器仍然会做正确的事情。

如果您添加了一个在语义上不复制其数据的成员,那么您只需要自己进去完成即可。

一般来说,memcpy总是比每个成员拷贝快。一些注意事项:

  • memcpy的工作方式通常是尽可能进行对齐的读/写。然而,这是一个完全依赖于平台的事情,理论上memcpy可能是效率非常低的

  • 如果结构未对齐,则每个成员副本也需要未对齐的访问

  • 压缩结构将更慢,除非它们也对齐(如SSE、NEON等)

  • 如果结构中有填充,memcpy将不可避免地复制比所需更多的字节。

  • 确定答案的最佳方法是在目标机器上构建并运行/配置它,记住不同的结构可能具有不同的特性。

memcpy仅适用于POD类型。一旦有了析构函数、构造函数、虚拟函数、私有数据、继承、内存,对象就会有未定义的行为。所以,它适用于这样的类

class PODType {
public:
    int x;
    int y;
};
class Object {
public:
    PODType m;
    Object& operator=(Object const& x) {
        memcpy(&m, x.m, sizeof(PODType));
        return *this;
    }
};

但现在,随着程序的发展,有人会更改PODType以拥有一个自定义构造函数,因为这很方便,或者添加一个析构函数,或者做一些其他事情使其成为非POD。现在,所有与它关联的类都将具有未定义的行为。在这些情况下,最好使用编译器生成的赋值运算符。

如果你担心性能,

  1. 不要过早进行优化
  2. 使用探查器和度量