默认初始化和值初始化结构的不同性能

Posted

技术标签:

【中文标题】默认初始化和值初始化结构的不同性能【英文标题】:Different performance for default initialized and value initialized struct 【发布时间】:2016-07-04 21:58:03 【问题描述】:

我有一个带有数组的简单结构:

struct A

  uint32_t arr[size];
;

我有两个函数,使用默认初始化和值初始化创建它:

template<class T>
void testDefault()

  T* pa = new T;  // Default
  use(*pa);
  delete pa;


template<class T>
void testValue()

  T* pa = new T();  // Value
  use(*pa);
  delete pa;

我面临着这些功能的不同性能。有趣的是,性能差异取决于我如何声明结构的默认构造函数。我有三种方式:

struct A

  uint32_t arr[size];
  // Implicit constructor
;

struct B

  uint32_t arr[size];
  B() ;  // Empty constructor
;

struct C

  uint32_t arr[size];
  C() = default;  // Defaulted constructor
;

我认为从编译器的角度来看它们都是一样的。我从来没有这么错。我确实使用结构ABC 运行了testDefault()testValue() 多次并测量了性能。这是我所拥有的:

Default initialization (implict constructor) done in 880ms
Value initialization (implict constructor) done in 1145ms
Default initialization (empty constructor) done in 867ms
Value initialization (empty constructor) done in 865ms
Default initialization (defaulted constructor) done in 872ms
Value initialization (defaulted constructor) done in 1148ms

请注意,对于隐式和默认构造函数,性能显然更差。对于两种不同的初始化形式,只有空构造函数正确显示相同的性能。

我用 VC++、gcc 和 clang 对此进行了测试。见online demo for gcc。时间安排相当持久。

我的假设是:

    UDT 的默认值和值初始化是一回事 定义默认构造函数的所有演示方法都在做同样的事情 这些结构的默认构造函数应该使数组的内容处于不确定状态

由于所有编译器都表现出相同的时序,我似乎遗漏了一些东西。谁能解释一下这些时间?

(另请参阅我关于同一主题的问题Why compilers put zeros into arrays while they do not have to?。我在那里提供了一些指向 cppreference 的链接。)

【问题讨论】:

【参考方案1】:

我们来看看value-initialize的定义:

对 T 类型的对象进行值初始化意味着:

如果 T 是没有默认构造函数 (12.1) 或用户提供或删除的默认构造函数的(可能是 cv 限定的)类类型,则该对象是默认初始化的; 如果 T 是(可能是 cv 限定的)类类型,没有用户提供或删除的默认构造函数,则该对象为零初始化 [...]; 如果T是数组类型,那么每个元素都是值初始化的; 否则,对象被零初始化。

还让我们回顾一下这里用于构造函数的形容词:

用户声明 - 你声明了构造函数 用户提供 - 你声明了构造函数并且没有将它设置为= default= delete 默认 - 可以不带参数调用 声明为默认 - 标记为 = default; 或隐式生成

看看你的课程:

A 有一个默认构造函数,它被隐式声明为默认的,而不是用户提供的。 C 有一个默认构造函数,它是用户声明为默认的,而不是用户提供的。

所以 value-initialize 定义中的第二个要点适用。对象是零初始化的,这意味着arr 被清零。

B 有一个用户提供的默认构造函数。

所以 value-initialize 的第一个要点适用于B; value-initialization 和这里的 default-initialization 一样,arr 不会被清零。

您的时间安排似乎与预期相符:AC 的值初始化将 arr 清零,而其他情况则不然。

【讨论】:

注意:提到聚合是一个红鲱鱼;只有在使用列表初始化时才会发挥作用。 另外,为了简单起见,我已经使用 [,,,] 删除了对具有非平凡构造函数的类类型成员的类的行为描述,以保持简单 很好的答案!我想知道为什么这里提到了没有默认构造函数和已删除默认构造函数的类型。是否可以对此类类进行值初始化? @Mikhail 我认为这仅指聚合(聚合初始化绕过任何构造函数),因此AGG agg = ; 将在那里进行值初始化 这解释了删除的默认构造函数。但是一个类没有默认构造函数的唯一其他原因是它有其他用户定义的构造函数。但是,如果一个类有一个用户提供的构造函数,它就不再是一个聚合。所以你认为这行是指具有用户定义的非默认构造函数的类,这些构造函数不是用户提供的?【参考方案2】:

其中一个构造函数在值初始化下表现不同。在这个版本中,

B() ;

B 被初始化时,数组B::arr 没有被初始化。与其他人一样,它是。这是否解释了性能差异是另一回事。

因此,B::arr 不会通过值初始化进行零初始化,而 A::arrC::arr 会。这三种情况在默认初始化下都有相同的行为,即arr得到default-initialized,即不进行初始化。

【讨论】:

为什么显式默认的构造函数会这样做。和空的不一样?这里没有进行聚合初始化,不是吗? @BaummitAugen 这是个好问题。可能是因为它试图尽可能接近编译器合成一个。在default 之前,不可能恢复这种行为。默认值和值初始化。

以上是关于默认初始化和值初始化结构的不同性能的主要内容,如果未能解决你的问题,请参考以下文章

C++默认初始化和值初始化:哪个是哪个,啥时候调用以及如何可靠地初始化一个模板类型成员

零初始化、静态初始化和值初始化有何不同?

不可复制的对象和值初始化:g++ vs msvc

具有默认初始化的 C 结构定义?

C++中的结构体案例练习和引用传递和值传递

结构体——定义,实例化,初始化