neon---题外篇

目录

摘要

一、访问内存的优化

1. 函数内存的引用

2. 顺序的访问

二、stl库的使用

1. reserver

2. emplace_back

3. move

总结


摘要

代码性能的优化,包括连续内存的访问和stl中move等使用的介绍。


一、访问内存的优化

1. 函数内存的引用

add_poor对形参dst的每次修改, add_better计算end后再赋值。

//array的求和
void add_poor(const int *src, int n, int *dest)
{
    for (int i = 0; i < n; ++i)
    {
        *dest += src[i];
    }
}

void add_better(const int *src, int n, int *dest)
{
    int sum = *dest;
    for (int i = 0; i < n; ++i)
    {
        sum += src[i];
    }
    *dest = sum;
}

//the delta time
auto start_time = std::chrono::system_clock::now();
//do something
auto end_time = std::chrono::system_clock::now();
std::chrono::duration<double> diff = end_time -start_time;
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(diff);
std::cout<<"deata ms is: "<< duration.count()<<std::endl;

2. 顺序的访问

多维数组是以行主序存储在内存中的,那么在循环中访问完一行后再访问下一行的方式更高效, 尽量避免不连续的访问。

// 按列访问数组元素
long get_poor(const int *src, int rows, int cols)
{
    long long sum = 0;
    for (int i = 0; i < cols; ++i)
    {
        for (int j = 0; j < rows; ++j)
        {
            sum += *(src + j * cols + i);
        }
    }
    return sum;
}

// 按行访问数组元素
long get_better(const int *src, int rows, int cols)
{
    long long sum = 0;
    for (int i = 0; i < rows; ++i)
    {
        for (int j = 0; j < cols; ++j)
        {
            sum += *(src + i * cols + j);
        }
    }
    return sum;
}

 

二、stl库的使用

1. reserver

预分配n个元素的存储空间,std::vector添加的元素已知,那么在添加元素前使用reserve函数预分配std::vector需要的内存,避免内部数据发生数据的迁移带来不必要的元素拷贝开销、申请新内存、释放内存,第一个元素的地址&data[0]都一直不变。即在这添加这些元素时,std::vector内部未发生数据迁移,在添加后面的其他元素时,也不会导致std::vector内部数据迁移。

void testReserve()
{
    std::vector<int> myVec;
    //myVec.reserve(100);         // 新元素还没有构造,此时不能用[]访问元素

    for (int i = 0; i < 100; i++ )
    {
        myVec.push_back(i);   //新元素这时才构造
    }

    myVec.resize(102);          // 用元素的默认构造函数构造了两个新的元素
    myVec[100] = 1;             //直接操作新元素
    myVec[101] = 2;
}

vector<int> myVec, 然后500次调用 myVec.push_back(****) ; vector<int> myVec(500), 然后500次调用 myVec.push_back(****); 做法2只需要进行1到2次内存分配,而做法1不知道要进行多少次内存分配了。

2. emplace_back

push_back:  ori200 copy 200 move 0  dest 200; emplce_back:  ori200 copy 0   move 0  dest 0

push_back函数的三种开销:临时对象的构造和拷贝开销 析构开销

emplace_back函数只有必要的构造元素开销,不会引入临时对象. 此时只调用了一次构造函数,连移动构造函数都省掉了,这是因为emplace_back把参数10完美转发给A的构造函数,直接构造了一个元素,而这个元素是直接存放在vector容器中的

int g_count_ori = 0;
int g_count_copy = 0;
int g_count_move = 0;
int g_count_des = 0;

class TestObject
{
public:
    explicit TestObject(int a, int b, int c): a_(a), b_(b), c_(c)
    {
        g_count_ori++;
    }

    TestObject(const TestObject & other)
    {
        g_count_copy++;
    }

    TestObject(const TestObject && other)
    {
        g_count_move++;
    }

    ~TestObject()
    {
        g_count_des ++;
    }
    
    int a_, b_, c_;
};


void poor(std::vector<TestObject> &data)
{
    data.reserve(N);
    for (int i = 0; i < N; i++)
    {
        data.push_back(TestObject(i, i+1, i+2));
    }
}

void better(std::vector<TestObject> &data)
{
    data.reserve(N);
    for (int i = 0; i < N; i++)
    {
        //因为emplace_back把参数10完美转发给A的构造函数,直接构造了一个元素
        //data.emplace_back((i, i+1, i+2)); //error
        //如果一个类没有拷贝或移动构造,则不能用于STL容器中,如果没有相应参数类型的构造实现,
        //emplace_back编译不过,找不到它需要的对应构造函数
        data.emplace_back(i, i+1, i+2);
        //data.emplace_back(std::move(TestObject(i, i+1, i+2)));
    }
}

3. move

移动而非拷贝数据以避免不必要的拷贝开销,从而改善程序性能


class MemoryBlock
{
public:

   // Simple constructor that initializes the resource.
   explicit MemoryBlock(size_t length)
      : _length(length), _data(new int[length])
   {
      std::cout << "ori construct. length = "<< _length << std::endl;
   }

   // Destructor.
   ~MemoryBlock()
   {
      std::cout << "deconstruc. length = "<< _length;
      if (_data != nullptr) {
         delete[] _data;
      }
   }

   // Copy constructor.
   MemoryBlock(const MemoryBlock& other)
      : _length(other._length), _data(new int[other._length])
   {
      std::cout << "copy constructor length = "<< other._length  << std::endl;
      std::copy(other._data, other._data + _length, _data);
   }

   // Copy assignment operator.
   MemoryBlock& operator=(const MemoryBlock& other)
   {
      std::cout << "Copy assignment operator length = "
                << other._length  << std::endl;

      if (this != &other) {
         delete[] _data;
         _length = other._length;
         _data = new int[_length];
         std::copy(other._data, other._data + _length, _data);
      }
      return *this;
   }

   size_t Length() const
   {
      return _length;
   }

   // Move constructor.
    MemoryBlock(MemoryBlock&& other)
       : _data(nullptr), _length(0)
    {
       std::cout << "Move constructor. length = "<< other._length  << std::endl;

       // Copy the data pointer and its length from the source object.
       _data = other._data;
       _length = other._length;

       // Release the data pointer from the source object
       other._data = nullptr;
       other._length = 0;
    }

    // Move assignment operator.
    MemoryBlock& operator=(MemoryBlock&& other)
    {
       std::cout << "move operator length = "
                 << other._length << "." << std::endl;

       if (this != &other) {
          delete[] _data;
          // Copy the data pointer and its length from the source object.
          _data = other._data;
          _length = other._length;

          // Release the data pointer from the source object so that
          // the destructor does not free the memory multiple times.
          other._data = nullptr;
          other._length = 0;
       }
       return *this;
    }

private:
   size_t _length;  // The length of the resource.
   int* _data;      // The resource.
};

void TestSTLObject()
{
    std::string str = "Hello";
    std::vector<std::string> v;

    // uses the push_back(const T&) overload
    v.push_back(str);
    std::cout << "After copy, str is " << str << std::endl;

    // uses the rvalue reference push_back(T&&) overload,
    // which means no strings will be copied; instead, the contents
    // of str will be moved into the vector.  This is less
    // expensive, but also means str might now be empty.
    v.push_back(std::move(str));
    std::cout << "After move, str is " << str <<std::endl;

    /*
    After copy, str is "Hello"
    After move, str is ""
    */
}

void TestMyObjectWithoutUseMove()
{
   std::vector<MemoryBlock> v;
   MemoryBlock mb1(25);
   v.push_back(mb1);

   /*
    ori construct. length = 25
    copy constructor length = 25
    deconstruc. length = 25 Deleting resource.
    deconstruc. length = 25 Deleting resource.
    */
}

void TestMyObjectWithUseMove()
{
   std::vector<MemoryBlock> v;
   MemoryBlock mb1(25);
   v.push_back(std::move(mb1));
   /*
    ori construct. length = 25
    Move constructor. length = 25
    deconstruc. length = 0
    deconstruc. length = 25
   */

}

 

A: 左值的声明符号为&,右值的声明符号为&&。

B: 深层拷贝对程序的影响比较大. 把临时的对象直接移动给被赋值的左值对象,效率是显著的, 就产生了移动语义,右值引用是用来支持转移语义的。

C: 移动语义可以将资源(堆,系统对象等) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁. 移动语义通过移动构造函数和移动赋值操作符实现,其与拷贝构造函数类似,区别如下:

参数的符号必须为右值引用符号,即为&&;  参数的成员转移后需要修改(如改为nullptr),避免临时对象的析构函数将资源释放掉


总结

内存的连续的访问和std::emplace_back

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值