Zero a pod-struct object

有时我们希望要将一个结构对象清零。此时我们常用的方法是调用memset。来看一下代码

#include <cstring>

struct pod_struct {
#ifdef BIG_POD_STRUCT
    int a[128];
#else
    int a;
    int b;
#endif
};

int main()
{

    pod_struct pod;
    std::memset(&pod, 0, sizeof(pod));
}

BIG_POD_STRUCT的定义在后续中会用到。我们知道,memset其实是有开销的,对于比较小的内存块,使用简单的赋值可能更快。因为memset需要根据目标和源地址计算对齐属性,选择最佳的一种方案进行copy。

pod.a = 0;
pod.b = 0;

这里简单地赋值,效率其实更高。我们来简单做一下对比。分别使用memset和赋值的方法,迭代1000, 0000次,计算总时间。

#include <cstring>
#include <clock.h>

struct pod_struct {
#ifdef BIG_POD_STRUCT
    int a[128];
#else
    int a;
    int b;
#endif
};

int main()
{
    using namespace my_util;

    const int COUNT = 10000000;
    pod_struct pod;
    SCOPED_CLOCK_BEGIN()
    for (int i = 0; i < COUNT; ++i) {
        std::memset(&pod, 0, sizeof(pod));
    }
    SCOPED_CLOCK_END()

    SCOPED_CLOCK_BEGIN()
    for (int i = 0; i < COUNT; ++i) {
        pod.a = 0;
        pod.b = 0;
    }
    SCOPED_CLOCK_END()
}

这里的scoped_clock_*宏只是用来测量时间的,实现使用clock()函数完成的,可以精确到毫秒。测量的结果是

 memsetassignment
time(ms)10931

很显然,用赋值的方法所用的cpu时间更短。由此我们想到能不能有什么方法可以根据对象的大小来选择这两种方法呢?其实编译器可以帮我们做这些事情。

使用一个initializer对一个pod对象进行初始化,就可以很简单地把一个对象清零。有时我们会这么把数组进行清零。

int a[100] = {0}; 

这里,第一个元素初始化为0,剩余元素自动被编译器清零。其实更简单的方法是,连0都不指定,直接使用一个空的初始化列表。标准规定,

If there are fewer initializers in the list than there are members in the aggregate, then each member not
explicitly initialized shall be value-initialized (8.5).

value-initialized对于内建的类型而言,就是zero-initialized,也就是简单地清零。所以,我们可以这么初始化pod_struct

pod_struct pod = {};

使用这个结构,编译器会根据目标对象的大小选择最佳的初始化方法,即使用memset或者赋值。来看一下编译器在BIG_POD_STRUCT打开/关闭情况下生存的汇编代码就一目了然了(VC10)。

 ; BIG_POD_STRUCT not defined
 xor         eax,eax  
 mov         dword ptr [pod],eax  
 mov         dword ptr [ebp-8],eax

这里编译器把eax的值赋值给了[pod],也就是pod_struct.a,之后再赋值给了pod_struct.b。再看一下BIG_POD_STRUCT打开后的代码,

 ; BIG_POD_STRUCT defined
 push        200h  
 push        0  
 lea         eax,[pod]  
 push        eax  
 call        @ILT+440(_memset)

这里编译器调用了memset,而由于memset是__cdecl函数,所以参数压栈从右到左。size=200H,value=0,dest=&pod,分别被压栈后调用memset。200H正好是pod_struct的大小。

我测试了一下编译器会将赋值替换为memset实现的阈值,在我的机器上是40个字节,VC就会使用memset对pod_struct进行清零。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值