美文网首页
C++性能优化之一:合理使用内存

C++性能优化之一:合理使用内存

作者: 思考着自己的未来 | 来源:发表于2019-10-04 12:27 被阅读0次

    要想在编码过程中,写出高效的代码,是需要自己长期的总结和不断学习的。工作以来,我自己也总结了一些小技巧,可以让你的程序运行的更快、内存空间使用更合理,同时我还会不断地补充该blog,争取建立出一个属于自己的c++ effective系列。

    不多说,直接进入正题,以下都是我再编程过程中,总结出来c++高效编码规则,每个topic对应一个规则。

    局部变量合理使用

    让我们先看一段代码:

    for (int i = 0; i < 1000; ++i)
    {
        string str = "do some thing:" + int2str(i);
        func(str);
    }
    

    这段代码,在循环中使用局部变量拼装函数func的入参,在每次循环过程中,str对象都会执行一次构造函数和析构函数,那么,在这个for循环中,单单是str的组装就耗费了1000次的内存申请和释放,局部变量占用内存小的话,影响不会很大,如果动辄几十、几百kb,那就会造成系统内存使用的波动,那么是不是有更高效的方法?

    其实只需要把str变量放到for循环外部声明即可,如下面代码:

    string str;
    for (int i = 0; i < 1000; ++i)
    {
        str = "do some thing:" + int2str(i);
        func(str);
    }
    

    这段代码会大大降低内存的申请和释放次数,因为首次循环后,str会申请15个字节的内存空间来容纳现有数据,第二次循环时,在赋值运算符函数中,由于str当前空间已经足够容纳第二次循环的数据,因此我们可以考虑对原有str内存进行复用,所以只存在一次数据拷贝,不存在新的内存申请和释放;到第十次循环时,需要16个字节才能容纳现有数据,因此需要释放str原有内存,申请新的内存。以此类推,我们可以算出1000次循环过程中,只有三次内存申请和释放,大大降低了内存的申请和释放次数。

    小结:在循环体中,局部变量如果占用内存空间较大,会造成内存使用不合理,可以考虑放到循环体外声明。

    左值引用的合理使用

    左值引用提升程序性能的应用场景。
    首先是函数入参,看下面两个函数的声明,func1会存在一次str副本的拷贝构造的过程,且退出函数体,还需要释放str,而func2直接将str的地址传入函数体内部,不存在拷贝构造,如果str内存很大,那么节约一次拷贝的收益还是很可观的。

    void func1(const string str);  //存在冗余拷贝构造和析构
    void func2(const string& str); //直接传递str变量的地址
    

    其次是循环体中,获取数组元素时,如果我们不需要修改原始值,那么应该是使用常引用直接指向数组元素的地址,避免局部变量的冗余的拷贝构造和析构

    for (int i = 0; i < arrstrs.size(); ++i)
    {
        string str = arrstrs[i];        //存在冗余拷贝构造和析构
        const string& str = arrstrs[i]; //直接使用arrstrs[i]变量的地址
        ...
    }
    

    动态数组容量提前设定

    分层架构的代码中,经常出现需要对不同层次数据规格进行转换,即把其他层次的数据转化为所在层的数据格式,以下是项目中经常看见的一段代码,主要目的是把第二层的数据转化到第一层坐标数据中,代码如下:

    //变量格式声明
    typedef struct _FirstLayer_PosData_t
    {
        double x;
        double y;
    }FirstLayer_PosData_t;
    typedef struct _SecondLayer_PosData_t
    {
        double x;
        double y;
        int    tag;
    }SecondLayer_PosData_t;
    vector<FirstLayer_PosData_t> arrfir;
    vector<SecondLayer_PosData_t> arrsec;
    //层数据转化代码
    for (int i = 0; i < arrsec.size(); ++i)
    {
        FirstLayer_PosData_t stFirstLayerPos;
        stFirstLayerPos.x = arrsec[i].x;
        stFirstLayerPos.y = arrsec[i].y;
        arrfir.push_back(stFirstLayerPos);
    }
    

    这段代码可以这样改进,其实我们要拷贝的元素个数是已知的,因此我们可以直接将arrfirst数组大小设置为arrsecond的大小即可,这就避免了在循环体中动态的去扩容(每次扩容的成本是先申请新的内存空间,将旧内存空间数据拷贝到新内存空间,然后释放旧内存空间),改进代码如下:

    arrfir.setsize(arrsec.size());
    for (int i = 0; i < arrfir.size(); ++i)
    {
        FirstLayer_PosData_t stFirstLayerPos;
        stFirstLayerPos.x = arrsec[i].x;
        stFirstLayerPos.y = arrsec[i].y;
        arrfir[i] = stFirstLayerPos;
    }
    

    仔细观察下,其实还有优化空间,局部变量是可以避免的,直接使用引用代替第一层数组的每个元素即可,最终优化代码如下:

    arrfir.setsize(arrsecond.size());
    for (int i = 0; i < arrfirst.size(); ++i)
    {
        FirstLayer_PosData_t& stFirstLayerPos = arrfir[i];
        stFirstLayerPos.x = arrsec[i].x;
        stFirstLayerPos.y = arrsec[i].y;
    }
    

    move语义的合理使用

    TODO

    相关文章

      网友评论

          本文标题:C++性能优化之一:合理使用内存

          本文链接:https://www.haomeiwen.com/subject/sjdoyctx.html