C++ 中的向量存储

Posted

技术标签:

【中文标题】C++ 中的向量存储【英文标题】:Vector storage in C++ 【发布时间】:2017-03-11 05:09:39 【问题描述】:

我希望存储一个大的 d 维点向量(d 固定和小:

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

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

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

【问题讨论】:

标准向量类应该在很大程度上与数组兼容,这意味着它分配的数据存储在连续的内存块中,就像数组一样。如果您有一个std::vector<Point>,那么所有Point 对象都将连续存储。如果Point 类本身有指针(直接或间接(比如当它有一个向量时)),那么并非所有数据都会连续存储。 是的,我会走元组路线或更好,只需使用普通的 C 风格结构(我仍然觉得元组使用起来很烦人,即 std::get() 并不是全部直观)。 【参考方案1】:

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

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 存储指针 指向动态分配的内存。所以你有 single Points 的连续性,但不是整个结构。

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

【讨论】:

一个是否比另一个更有效取决于用例。如果您需要插入一些数据,那么只移动一些指针可能比复制所有数据要快得多。 一般来说,测量是一件好事。但是,具有缓存友好的结构往往对性能很有好处。基于节点的结构往往不利于性能,因为它们对缓存不友好。请注意,分页的成本很高。相反,现代 CPU 喜欢访问 连续 内存位置(这让预取器很高兴)。【参考方案2】:

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

【讨论】:

【参考方案3】:

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

在性能方面,一如既往,您必须对其进行衡量。不要猜测。至少就扫描而言。

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

【讨论】:

【参考方案4】:

由于尺寸是固定的,我建议您使用将尺寸用作模板参数的模板。像这样的:

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&lt;ndpoint&lt;...&gt;&gt; 来收集积分以获得最佳性能。

【讨论】:

这与 std::vector<:array>> 相比有何不同/更好? "这比 std::vector<:array>> 好在哪里?"控制您可以对 ndpoint 执行的操作(例如,添加 norm 方法或 distanceTo 等)。在性能方面,它是一样的。只是不要使用元组或向量作为 nd 点的存储。【参考方案5】:

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

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

请注意,通常您不能相信 array 点是连续的。这是由于对齐,分配块头等。例如考虑

struct Point 
   char x,y,z;
;

Point array_of_points[3];

现在,如果您尝试“重塑”,即根据容器中的点相邻这一事实在 Point 元素之间进行迭代,那么它最有可能失败:

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

【讨论】:

以上是关于C++ 中的向量存储的主要内容,如果未能解决你的问题,请参考以下文章

无法访问存储在多维向量中的对象(C++)

如何将来自不同图像的多个描述符存储在 C++ 和 OpenCV 中的向量向量中?

如何找到存储在 C++ 向量中的对象的类方法?

如何将来自先前类的值存储到 C++ 中的向量中?

如何将文件存储到包含 C++ 中的类对象的向量中?

存储在向量中的 C++ std::function 不起作用