c++中的矢量存储

Vector storage in C++

本文关键字:存储 c++      更新时间:2023-10-16

我希望存储一个大的d维点向量(d固定且小:<10)。

如果我将Point定义为vector<int>,我认为vector<Point>将在每个位置存储一个指向点的指针。

但是如果将Point定义为固定大小的对象,如下所示:std::tuple<int,int,...,int>std::array<int, d>,程序是否将所有点存储在连续内存中,还是保留额外的间接层?

如果答案是数组避免了额外的间接,那么在扫描vector<Point>时,这会对性能(缓存利用局部性)产生很大的影响吗?

如果您将Point定义为具有连续数据存储(例如struct Point { int a; int b; int c; }或使用std::array),那么std::vector<Point>将在连续内存位置存储Point s,因此您的内存布局将为:

p0.a, p0.b, p0.c, p1.a, p1.b, p1.c, ..., p(N-1).a, p(N-1).b, p(N-1).c
另一方面,如果将Point定义为vector<int>,则vector<Point>具有vector<vector<int>>的布局,即不是连续的,因为vector指针存储到动态分配的内存中。所以你有单个 Point s的连续性,但不是整个结构。

第一个解决方案比第二个更有效(因为现代cpu喜欢访问连续的内存位置)。

vector将在连续内存中存储您的类型包含的任何内容。所以,是的,如果那是arraytuple,或者更好的是,自定义类型,它将避免间接。

性能方面,一如既往,你必须衡量它。不要投机。至少就扫描而言。

然而,当您首先创建这些点时,肯定会有巨大的性能增益,因为您将避免为每个存储点的vector分配不必要的内存。在c++中,内存分配通常非常昂贵。

对于上述d (<10)的值,将Point定义为vector<int>几乎会使std::vector<Point>的全部内存使用量增加一倍,并且几乎不会带来任何优势。

由于维度是固定的,我建议您使用使用维度作为模板参数的模板。像这样:

template <typename R, std::size_t N> class ndpoint 
{
public:
  using elem_t=
    typename std::enable_if<std::is_arithmetic<R>::value, R>::type;
  static constexpr std::size_t DIM=N;
  ndpoint() = default;
  // e.g. for copying from a tuple
  template <typename... coordt> ndpoint(coordt... x) : elems_ {static_cast<R>(x)...} {
  }
  ndpoint(const ndpoint& other) : elems_() {
    *this=other;
  }
  template <typename PointType> ndpoint(const PointType& other) : elems_() {
    *this = other;
  }
  ndpoint& operator=(const ndpoint& other) {
    for(size_t i=0; i<N; i++) {
      this->elems_[i]=other.elems_[i];
    }
    return *this;
  }
  // this will allow you to assign from any source which defines the
  // [](size_t i) operator
  template <typename PointT> ndpoint& operator=(const PointT& other) {
    for(size_t i=0; i<N; i++) {
      this->elems_[i]=static_cast<R>(other[i]);
    }
  }
  const R& operator[](std::size_t i) const { return this->elems_[i]; }
  R& operator[](std::size_t i) { return this->elems_[i]; }
private:
  R elems_[N];
};

然后使用std::vector<ndpoint<...>>作为最佳性能点的集合。

100%确定数据结构的唯一方法是完全实现自己的内存处理。

但是,有许多库实现了矩阵和矩阵操作,您可以查看这些库。有些已经记录了关于连续内存,重塑等的信息(例如OpenCV Mat)。

注意,一般来说,你不能相信点的数组是连续的。这是由于对齐,分配块头等。例如考虑

struct Point {
   char x,y,z;
};
Point array_of_points[3];

现在,如果你尝试"重塑",即根据点在容器中相邻的事实在点元素之间迭代,那么它很可能会失败:

(char *)(&array_of_points[0].z) != (char *)(&array_of_points[1].x)