重新初始化向量的最快方法是什么?

时间:2019-03-20 17:07:06

标签: c++ performance c++11

将大向量的所有值重置为其默认值的最快方法是什么?

struct foo
{
  int id;
  float score;
};

std::vector<foo> large_vector(10000000);

最简单的方法是创建一个新的向量,但是我想重新分配内存要比重新初始化一个现有的向量花费更多的时间?

在重置之前,我必须遍历向量以收集非零分数(可能是数千或数百万)。我应该在此循环中逐个重置结构吗?

编辑:

向量大小是固定的,对于每个结构成员(所有浮点数和整数),“默认值”表示0。

3 个答案:

答案 0 :(得分:5)

  

重新初始化向量的最快方法是什么?

不要。

只需调用clear()来记录矢量没有有效条目的事实。这具有既(可能)是最佳的又保证正确的优点,并且还具有完美的表现力。除非配置文件显示实际需要,否则IMO不应考虑任何建议的替代方法。

您的元素类型微不足道,因此对于良好的质量实现,复杂度的线性上限实际上应该是恒定的-无需依次销毁每个元素。

没有释放内存,或者稍后需要重新分配。

push_back之后写入向量时,只需使用emplace_backclear(),而不是使用operator[]

要使其与首次使用保持一致,请不要使用带有10000000个值构成的元素初始化向量,而应使用reserve(10000000)进行预先分配而不进行初始化。

例如

int main() {
  vector<foo> v;
  v.reserve(10000000);

  while(keep_running) {
    use(v);
    v.clear();
  }
}

// precondition: v is empty, so
// don't access v[i] until you've done
//   v.push_back({id,score})
// at least i+1 times
void use(vector<foo> &v) {
}

由于您需要就地将元素归零,因此第二快的通用解决方案可能是将上述循环更改为

  while(keep_running) {
    v.resize(10000000);
    use(v);
    v.clear();
  }

或删除clear()并使用fill()覆盖所有元素。

如果非零元素稀疏(例如,基于有意义的索引进行更新),则在主循环遍历向量时,将它们动态归零可能会更快。

同样,您确实需要配置文件,以找出哪个更适合您的用例。

答案 1 :(得分:2)

  

将大向量的所有值重置为其默认值的最快方法是什么?

取决于“默认值”中矢量的含义。

如果要删除所有元素,最有效的方法是std::vector::clear

如果您希望将所有元素保留在向量中但设置其状态,则可以使用std::fill

std::fill(large_vector.begin(), large_vector.end(), default_value);

如果元素类型不重要,并且“默认值”为零,则std::memset可能是最佳的:

static_assert(std::is_trivially_copyable_v<decltype(large_vector[0])>);
std::memset(large_vector.data(), 0, large_vector.size() * sizeof(large_vector[0]));

要验证std::memset值得承担的麻烦,您应该测量(或检查组件)。优化器可能会为您完成工作。

在所有位均未设置的意义上为零。 C ++不保证这是零浮点数的表示。如果您的非最小用例使用了指针,它也不能保证它是一个空指针。

答案 2 :(得分:2)

为了确定最快的方法,您将需要运行一些基准测试。

有多种方法可以“重新初始化”向量:

  1. 调用clear(),对于平凡类型,这应大致等同于仅进行vector.size = 0。向量的容量不变,没有元素被释放。析构函数将在存在的元素上调用。当您使用push_backemplace_backresize向量时,旧值将被覆盖。
  2. 致电assign(),例如large_vector.assign( large_vector.size(), Foo() );。这将遍历整个向量,将每个元素重置为其默认值。希望编译器将设法将此优化到内存集或类似的版本。
  3. 由于类型很琐碎,如果您只想将每个元素重置为0,则应该能够执行memset,例如:memset( large_vector.data(), 0, sizeof(Foo)*large_vector.size() );
  4. 致电std::fill,例如std::fill( large_vector.begin(), large_vector.end(), Foo() );,这应该类似于assignmemset