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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: eclipse-jee-neon-3是一款基于Java的集成开发环境(IDE),它是Eclipse IDE的一个版本。Eclipse IDE是一个广泛使用的开发工具,可以用于开发各种类型的应用程序。 "Eclipse-jee"表示这个版本主要针对Java Enterprise Edition(Java EE)的开发,该平台提供了一组技术和API,用于开发企业级Java应用程序。与传统的Java SE(Java Standard Edition)相比,Java EE提供了更多的功能和工具,以满足企业级应用程序的需求。 "Neon"是Eclipse IDE的开发代号,代表这个版本的发布时间。Neon是Eclipse IDE的第四个主要版本,它于2016年发布。 "Eclipse-jee-neon-3"是基于Neon版本的Java EE开发工具的第三个修订版。这意味着该版本修复了之前版本存在的一些错误和问题,并且还可能提供了一些新的功能和改进。 使用eclipse-jee-neon-3,开发者可以创建、编辑和调试Java EE应用程序。它提供了许多有用的功能,如自动代码补全、语法高亮显示、调试器、版本控制集成等。此外,它还支持许多其他技术和框架,如Servlet、JSP、EJB、JPA等。 总之,eclipse-jee-neon-3是一个功能强大的Java EE开发工具,可以帮助开发者轻松地构建和调试企业级Java应用程序。 ### 回答2: eclipse-jee-neon-3是一个流行的集成开发环境(IDE),用于开发JavaEE(Java企业版)应用程序。它是Eclipse Foundation开发的,并通过开放源代码方式进行发布。 这个版本的Eclipse-JEE-Neon-3是Eclipse Neon系列的第三个服务版本。Neon是Eclipse的第四个同时发布的版本,专注于提供Java开发工具的最新和最先进的功能和性能。 Eclipse-JEE-Neon-3提供了广泛的功能,以帮助开发者更轻松地构建和调试企业级应用程序。它支持许多JavaEE技术,如Servlets、JavaServer Pages(JSP)、JavaBeans、Enterprise JavaBeans(EJB)、Java Persistence API(JPA)等。它还集成了许多其他的开发工具,如Git、Maven、JUnit等,使开发者能够更加高效地进行版本控制和测试。 Eclipse-JEE-Neon-3还具有插件式的架构,允许开发者根据自己的需求进行定制和扩展。开发者可以安装各种插件来增加不同的功能和工具,以满足他们特定的开发需求。 此外,Eclipse-JEE-Neon-3还具有友好的用户界面,提供了易于使用和导航的功能。开发者可以通过简单的拖放操作来创建和管理项目,轻松地导航代码和资源,方便地查找和修复错误。 总而言之,Eclipse-JEE-Neon-3是一个强大而全面的开发工具,专门用于构建Java企业级应用程序。它提供了丰富的功能和工具,使开发者能够高效地进行开发和调试,并能够满足不同的需求和要求。 ### 回答3: Eclipse-jee-neon-3是一个用于开发Java Enterprise Edition(JEE)应用程序的集成开发环境(IDE)。它是Eclipse IDE的一个版本,专门针对JEE开发进行优化和扩展。 对于Java开发人员来说,Eclipse-jee-neon-3是一个非常有用的工具,它提供了一系列强大的功能和特性。其包括代码编辑器、调试器、编译器、版本控制、自动完成、语法高亮等等。它还支持各种JEE相关的技术和框架,如Servlet、JSP、EJB、JSF等。 通过Eclipse-jee-neon-3,开发人员可以方便地创建JEE项目,并且能够自动为项目生成基本的代码结构。它还提供了一系列工具和插件,用于简化开发过程,如界面设计器、数据库连接器、Web服务器集成等。 除了常规的代码开发功能外,Eclipse-jee-neon-3还提供了一些用于优化代码质量和性能的工具。例如,它可以帮助检测和修复代码的错误,提供代码建议和重构提示。此外,它还可以进行代码分析和性能测试,并提供相应的报告和建议。 Eclipse-jee-neon-3还支持与其他开发工具和框架的集成。例如,它可以与Maven、Ant、Git等工具进行无缝协作,方便进行项目构建和版本管理。同时,它还可以与常见的应用服务器集成,如Tomcat、WebSphere、JBoss等,方便部署和调试应用程序。 总之,Eclipse-jee-neon-3是一个强大而且功能丰富的开发工具,非常适合开发Java Enterprise Edition应用程序的开发人员使用。通过它,开发人员可以更快速、高效地开发和调试应用程序,提高开发效率和质量。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值