c++ 如果可能的话,std::vector复制赋值操作符是否会避免释放和重新分配内存?

wtlkbnrh  于 2023-08-09  发布在  其他
关注(0)|答案(2)|浏览(119)

我有一个将一个向量复制到另一个向量的函数(还有很多其他的东西)。下面是代码的简化版本。

void Fun(std::vector<double> &in, std::vector<double> &out) {
    out = in;
}

字符串
我关心的是最大化效率,因为函数将运行多次。因此,我希望尽可能避免重新分配内存。向量“in”可能已经具有为其保留的大量存储器。所以,如果我做了一个手动实现,我相信我可以完成这一点。举例来说:

void Fun(std::vector<double> &in, std::vector<double> &out) {
    out.resize(in.size());//note - if the capacity of out is greater than in.size(), this will involve no memory release or allocation
    for (unsigned int i = 0;i<in.size();i++) {
        out[i] = in[i];
    }
}


如果in.size()小于out的现有容量,那么后面的实现将不会释放或分配任何新的堆内存。
问题是-我的原始实现会做同样的事情吗?也就是说,如果我简单地做了“out = in”,std::vector会自动以一种内存有效的方式来做这件事吗?“?
特别是,我担心的是,也许“出=进;“行可能会执行一些操作,例如释放当前分配给out的所有堆内存,然后重新分配它。与此等效的东西:

void Fun(std::vector<double> &in, std::vector<double> &out) {
    out.clear();
    out.shrink_to_fit();//releasing memory from heap
    out.resize(in.size());//reallocating memory from heap
    for (unsigned int i = 0;i<in.size();i++) {
        out[i] = in[i];
    }
}

9fkzdhlc

9fkzdhlc1#

std::vector::operator=在C库中的实现是很久以前就存在的。几乎可以肯定的是,它的作者知道他们在做什么,结合现代C编译器,它将生成最佳代码,以最大限度地减少工作量和动态分配。operator=的作者非常聪明。他们比我聪明。他们比你聪明。
很有可能,所示的优化此操作的尝试最多会达到平衡。在最坏的情况下,当out小于in.size()时,它最终会在resize()中进行一系列毫无意义的零初始化,除非编译器足够聪明,可以优化它。
如果有特定的知识,在一个特定的用例中,股票out=in;导致可测量的次优性能,那么某种微优化可能是谨慎的;但是,仅仅是“关心效率最大化”,就应该有具体的东西来评价。

km0tfn4u

km0tfn4u2#

向量“in”可能已经具有为其保留的大量存储器。
[...]我的原始实现会做同样的事情吗?
我不知道有任何实现仅仅因为要复制的vector有很多.capacity()就保留了多余的内存。此外,第二个代码片段不会保留超过存储in中所有元素所需的内存,并且它首先默认构造额外的元素,然后为它们分配新值。对于double s,这可能没什么关系,但对于非平凡类型,这可能会产生明显的差异。原始的out = in;永远不会比第二个代码片段中的代码慢。
如果你想保留in所保留的内存,你可以手动使用.reserve()

void Fun(const std::vector<double>& in, std::vector<double>& out) {
    out.clear();
    out.reserve(in.capacity());
    out = in;
}

字符串
关于你的问题的补充:
特别是,我担心out = in;行可能会做一些事情,比如释放当前分配给out的所有堆内存,然后重新分配它。与此等效的东西:

void Fun(std::vector<double> &in, std::vector<double> &out) {
    out.clear();
    out.shrink_to_fit();//releasing memory from heap
    out.resize(in.size());//reallocating memory from heap
    for (unsigned int i = 0;i<in.size();i++) {
       out[i] = in[i];
    }
}


不,这不太可能。从cppreferece开始:
[...]否则,*this拥有的内存可能会在可能的情况下重新使用。在任何情况下,最初属于*this的元素都可以被销毁或被逐元素复制赋值替换。
一个正常的实现会尽可能地重用已经分配的内存。该实现可能会对不同的类型使用不同的方法,以使副本分配尽可能有效。对于普通的可复制类型(如double),它可能是out.resize(in.size()),然后是std::copy。对于更复杂的类型,它可以out.reserve(in.size());,复制赋值out中的现有元素,然后复制构造其余的元素(如果是out.size() < in.size())-或者它可以out.clear(); out.reserve(in.size());,然后复制构造所有元素。这取决于容器中的类型。
如果outin有更多的容量,它将(几乎肯定)不会shrink_to_fit(理论上可能会导致不必要的重新分配),而是保留多余的分配供以后使用,如this example所示,所有主要的实现即使在复制分配了一个小得多的vector之后也会保留容量。

相关问题