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如果您将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
存储指针 指向动态分配的内存。所以你有 single Point
s 的连续性,但不是整个结构。
第一种解决方案比第二种更有效(因为现代 CPU 喜欢访问连续的内存位置)。
【讨论】:
一个是否比另一个更有效取决于用例。如果您需要插入一些数据,那么只移动一些指针可能比复制所有数据要快得多。 一般来说,测量是一件好事。但是,具有缓存友好的结构往往对性能很有好处。基于节点的结构往往不利于性能,因为它们对缓存不友好。请注意,分页的成本很高。相反,现代 CPU 喜欢访问 连续 内存位置(这让预取器很高兴)。【参考方案2】:对于d
(Point 定义为vector<int>
将使std::vector<Point>
的全部内存使用量几乎翻倍,并且几乎不会带来任何优势。
【讨论】:
【参考方案3】:vector
会将您的类型包含的任何内容存储在连续内存中。所以是的,如果那是 array
或 tuple
,或者可能更好的自定义类型,它将避免间接。
在性能方面,一如既往,您必须对其进行衡量。不要猜测。至少就扫描而言。
但是,当您首先创建这些点时,肯定会有巨大的性能提升,因为您将避免为每个存储点的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<ndpoint<...>>
来收集积分以获得最佳性能。
【讨论】:
这与 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++ 中的向量存储的主要内容,如果未能解决你的问题,请参考以下文章