我可以防止非 POD 类中数组数据成员中元素的零初始化吗?

Posted

技术标签:

【中文标题】我可以防止非 POD 类中数组数据成员中元素的零初始化吗?【英文标题】:Can I prevent zero initialization of the elements in an array data member in a non-POD class? 【发布时间】:2010-11-12 18:03:40 【问题描述】:

非 POD 派生类 PayloadMessage 包含一个数组数据成员 (_payload),其元素似乎在构造时初始化为零。出于性能/效率的原因,我不希望这种情况发生——它是一个大数组。建议? (可能是新位置?)我使用的是旧的 g++ 编译器,3.4.6。

#include <iostream>
class BaseMessage 
public:
  enum CCC_MessageType   START_THREAD, KILL_THREAD ;
  CCC_MessageType _type;
  virtual ~BaseMessage()   // class has other non-POD class stuff
;
class PayloadMessage : public virtual BaseMessage 
public:
  uint16_t _payload_length;
  uint8_t  _payload[3000];
;
int main(int argc, char* argv[]) 
  PayloadMessage* m = new PayloadMessage;
  size_t i = 0;
  for(; i < 3000; i++)  
    std::cout << ' ' <<  static_cast<int>(m->_payload[i]); // all zeros, not what I want
  

编辑:好的,根据 cmets/answers 和一些测试(如下所示),数组没有被初始化。 (有谁知道是什么原因导致内存显示为零?)

为了进行性能测试,我将 main() 更改为以下内容:

int main(int argc, char* argv[]) 
  PayloadMessage* m = new PayloadMessage;
  size_t i = 0;
  for(; i < 1400000; i++)   // allocates just under 4GB, for 32-bit compatibility
    m = new PayloadMessage;
  

然后编译并运行定时测试:

$ g++ -O3 test.cpp
$ time ./a.out
real    0m7.068s
user    0m1.393s
sys     0m4.730s

然后我添加了一个构造函数来为 _payload 进行显式值初始化并再次运行测试:

PayloadMessage() : _payload() 

$ g++ -O3 test.cpp
$ time ./a.out
real    0m10.361s
user    0m3.582s
sys     0m5.797s

是的,带有显式初始化的第二个版本需要更长的时间,所以我假设第一个版本没有进行初始化(看起来就是这样)。谢谢大家的帮助。

【问题讨论】:

我得问问。 真的会导致性能损失吗?您是否对其进行了分析以确定? 你确定缓冲区真的被零初始化了吗?它不应该。您是否尝试开启高优化(可能是 -O3)? 如果你得到的都是零,那纯属巧合,除非它是一些调试选项。 PayloadMessage 在命名空间范围内定义或作为本地/类静态时,缓冲区仅初始化为零。 @caveman,感谢您的分析想法(应该想到这一点)。 【参考方案1】:

POD 仅在使用 ()(C++ 标准中的 8.5)时显式初始化值,否则默认初始化。 POD 的默认初始化意味着它不对内存做任何事情。如果operator new 返回零初始化内存,那么PayloadMessage 的构造函数不会做任何额外的工作。出于好奇,您是否检查了PayloadMessage 的构造函数的反汇编以确定它是否真的在做任何昂贵的事情?

【讨论】:

不,我没有看反汇编。好主意,但性能测试(请参阅问题中的编辑)更容易。【参考方案2】:

没有理由将此数组初始化为零:我认为您应该检查特定实现的文档(可能是调试选项?)。

【讨论】:

【参考方案3】:

您可以改用std::vectorpush_back(可选reserve)吗?这使您可以在需要时准确地构造对象。

编辑:这也消除了对长度变量的需要。

EDIT2:假设您当前的代码正常工作,您是否通过分析确认初始化实际上是您代码中的瓶颈?这可能是严格意义上的过早优化。

【讨论】:

_payload 将用于存储从 UDP 套接字读取的字节,因此在此处使用 push_back() 效率不高。感谢您的分析想法。【参考方案4】:

在编辑中回答您的问题“有谁知道可能导致内存显示为零的原因?”

您正在分配大量内存,因此 operator new 正在向操作系统请求一个新的虚拟内存块。作为一项安全功能,所有当前的操作系统都确保在进程可以读取之前清除虚拟分配的内存。这是为了确保进程隔离,以便您无法读取先前写入并由另一个进程释放的内存。

避免内存在第一次访问时归零是不可能的,但您可以使用自定义内存分配器来重用您从操作系统请求的虚拟内存池。这将允许后续请求避免清除。保留虚拟内存与 operator delete 的默认行为相反,后者是将虚拟内存返回给操作系统以进行大分配。也可以在执行虚拟分配和解除分配时调整默认分配器的阈值,但这取决于系统。

【讨论】:

以上是关于我可以防止非 POD 类中数组数据成员中元素的零初始化吗?的主要内容,如果未能解决你的问题,请参考以下文章

具有所有私有成员的类可以是 POD 类吗?

LeetCode 283 Move Zeroes(移动全部的零元素)

关于类中静态成员函数和静态成员变量的知识点

防止在 C++ 中构造/初始化数组元素

对static静态成员的理解

JavaSE8基础 抽象类中可以有 非抽象的成员方法