不能疏漏的性能问题(memset)之ltrace调试

有一次吃饭,跟同事聊某个项目的代码实现,同事讲里面有太多的malloc与memset(bzero),对性能的影响比较大。

而在平常我们在程序中,申请内存后要初始化,防止使用未经初始化的内存导致不可预知的结果,否则在系统长时间运行后,可能新申请的这段内存里面的数据可能是杂乱的。对大段内存进行 memset操作的话,其实是很耗费性能的。

** memset置零的性能比较**

是不是平常经常使用 memset或者bzero的你完全没有感受到这个问题,如果你使用的内存在小于1024字节左右,那么你几乎感受不到它的性能问题; 但如果是操作的内存大小在 1M字节以上,那性能影响就很明显了。

#include <stdio.h>
#include <sys/time.h>
#include <string.h>

#define TIMEDIFF(s, e) (((e.tv_sec)-(s.tv_sec))*1000000 + (e.tv_usec) - (s.tv_usec))

int main(int argc, char **argv){

    struct timeval ts, te;
    char num_1K[1024], num_1M[1024*1024], num_4M[1024*1024*4];
    int i ;

    gettimeofday(&ts, NULL);
    memset(num_1K, 0, sizeof(num_1K));
    gettimeofday(&te, NULL);
    printf("memset 1k: %d\n", TIMEDIFF(ts, te));

    gettimeofday(&ts, NULL);
    memset(num_1M, 0, sizeof(num_1M));
    gettimeofday(&te, NULL);
    printf("memset 1M: %d\n", TIMEDIFF(ts, te));

    gettimeofday(&ts, NULL);
    memset(num_4M, 0, sizeof(num_4M));
    gettimeofday(&te, NULL);
    printf("memset 4M: %d\n", TIMEDIFF(ts, te));

    gettimeofday(&ts, NULL);
    for(i=0; i<sizeof(num_1K); ++i)
       num_1K[i]=0;
    gettimeofday(&te, NULL);
    printf("for 1k: %d\n", TIMEDIFF(ts, te));

    gettimeofday(&ts, NULL);
    for(i=0; i<sizeof(num_1M); ++i)
       num_1M[i]=0;
    gettimeofday(&te, NULL);
    printf("for 1M: %d\n", TIMEDIFF(ts, te));

    gettimeofday(&ts, NULL);
    for(i=0; i<sizeof(num_4M); ++i)
       num_4M[i]=0;
    gettimeofday(&te, NULL);
    printf("for 4M: %d\n", TIMEDIFF(ts, te));

    return 0;
}

编译输出,以下的运行结果的数值单位是微秒(gettimeofday的默认单位)。

在这里插入图片描述

运行的结果基本上是,在数组较小的情况下,bzero的效率比memset高;当数组超过一定大小之后,bzero的效率开始比memset低;数组越 大,memset的性能优势越明显。而在数组较小的情况下,memset的性能甚至不如直接for循环对数组中的每一个字节置零的方法

如果你在代码里循环调用memset,或者每次流程处理都要调用memset,而且每次都要memset 1M以上,那么你就要小心了,你可以考虑用其它替代方法哈。如果memset 1k以下,次数也不多,则可以用。

ltrace调试memset

ltrace命令是用来跟踪进程调用库函数的情况,ltrace其实也是基于ptrace。我们知道,ptrace能够主要是用来跟踪系统调用,那么它是如何跟踪库函数呢?可以查看一下ltrace -h

在这里插入图片描述
默认centos 7是没有安装ltrace ,centos7安装如下:

yum -y install ltrace

来看看个例子,在栈内存中申请。

#include <stdio.h>

int main(int argc, char **argv){

   char buffer[1024 * 1024] = {0};

   return 0;
}

但就是这么一行简单的代码,却隐藏了一个陷阱:初始化的时候调用了memset。我们用ltrace工具来看看:
在这里插入图片描述

是不是疑惑,全部都初始化为0了,当然慢了,如果我只初始化第一个元素,是不是会没有这个问题呢?在看看只初始化一个元素。

#include <stdio.h>

int main(int argc, char **argv){

   char buffer[1024 * 1024] = {0,};

   return 0;
}

在这里插入图片描述

从上面的ltrace输出可以看出,不管你是全部设置为0,还是只设置第一个,都会调用memset。不管char buffer[1024 * 8] = {}里面是什么内容,都会调用memset,将所有内存设置为0。
.
还有一个问题就是,数组的大小小于char buffer[1024 * 8] = {0}时不会调用memset。

#include <stdio.h>

int main(int argc, char **argv){

   char buffer[1024*8] = {0};

   return 0;
}


在这里插入图片描述

各种赋值的情况我们已经验证过了,当然也要验证一下不赋值的情况,下面是验证情况:

#include <stdio.h>

int main(int argc, char **argv){

   char buffer[1024*1024];

   return 0;
}

在这里插入图片描述
从ltrace结果可以清楚的看出:即使使用了内存,这次也并没有调用memset了。

建议大家在编写代码的时候不要用char buffer[1024 * 1024] = XXX;这种方式,而要使用char buffer[1024 * 1024];除非确定一定要memset。

总结

如果 memset的内存大小在1024字节以下,仅仅是在申请内存的时候使用,那就没有什么性能和效率上的问题。
对于那些我们申请的结构体,一般C语言的做法就是申请完就使用 memset进行操作。但是对于 C++来说,可以定义默认构造函数来完成初始化,这样既可以任意指定结构体中变量的初始化值,也可以减少 memset的使用。

参考:http://blog.csdn.net/yunhua_lee/article/details/6381866

在这里插入图片描述
欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下:

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值