更有效地访问 std::vector

Posted

技术标签:

【中文标题】更有效地访问 std::vector【英文标题】:Accessing std::vector more efficiently 【发布时间】:2016-02-20 09:23:32 【问题描述】:

在我的项目中,我定义了一个类Matrix,我使用的数据结构是std::vector<std::vector<T> > matrix;

现在我的问题是:如何更有效地访问它?如果我没记错的话,按索引访问(例如矩阵[i][j])是一种效率杀手,而建议的方式是通过迭代器。是这样吗?

假设是这样,那么我还有另一个问题:有时我需要知道我正在操作的行或列的索引。例如,考虑与另一个矩阵逐个元素相加:我怎么能这么说

C[i][j] = A[i][j] + B[i][j]

不使用两个索引ij?然后我还有其他操作,比如乘以 Vector(我正在定义的另一个类),但我想如果我正确理解访问元素的最有效方法,那么我可以复制相同的模式。

【问题讨论】:

matrix[i][j] 还不错。会杀了你的是你有 N+1 std::vectors with no assurances of spatial locality。如果您想要快速,请使用一维向量并使用 row * numColumns + column 对其进行索引。请注意此处如何定义矩阵类:isocpp.org/wiki/faq/operator-overloading#matrix-subscript-op。它解决了您将遇到的许多问题。不过,我建议使用 std:: vector 代替动态数组。 按索引访问本身不是问题,效率杀手是访问非连续内存,不管你怎么做或如何避免它。 我明白了,那么我可能会尝试使用包含 rows*columns 元素的一维 std::vector 如果你手头有一个链接,我不介意,@harold。一直在寻找改进代码的方法。 @user4581301 这么多链接,很难选择.. 如果您搜索“矩阵乘法缓存阻塞”,您应该会找到各种有趣的文章/论文/等。基准也是。 【参考方案1】:

如何更有效地访问它?如果我没记错的话,按索引访问(例如矩阵[i][j])是一种效率杀手,而建议的方法是通过迭代器。是这样吗?

别猜了,读一读:

复杂性

常数。

http://en.cppreference.com/w/cpp/container/vector/operator_at

正如 harold 指出的那样,这并不意味着它是高效的,而是知道这个运算符(总是?)实现类似于...

// data member
T * underlying_array;
// in operator[]
return *(underlying_array + position); // underlying_array[position]

...这将是相对好的,性能明智。不过请注意,我还听说过 STL 实现会向 operator[] 添加边界检查,这会添加一个分支,因此可能会对性能产生负面影响。

也就是说,您当然可以将代码更改为使用迭代器而不是 operator[],例如:

std::vector<std::vector<T>> A,B,C;
// Check for correct sizes first!
auto a_row = std::begin(A);
auto c_row = std::begin(C);
auto b_row = std::begin(B);
for (; a_row != std::end(A); ++a_row, ++b_row, ++c_row) 
  // Check correct sizes!
  auto a_col = std::begin(*a_row);
  auto b_col = std::begin(*b_row);
  auto c_col = std::begin(*c_row);
  for (; a_col != std::end(*a_row); ++a_col, ++b_col, ++c_col) 
    *c_col = *a_col + *b_col;
  

可能更有效率...但是否值得...

真正的性能杀手将是您的数据并非全部连续存储。 std::vector 将其元素存储在连续的内存中,但连续存储 std::vectors(就像您的“外部”向量所做的那样)对您没有帮助,因为“内部”向量可能将它们的数据存储在不同的位置。因此,要将矩阵的所有数据保存在连续内存中,您应该使用单个 std::vector 并自己进行正确的列 - 行索引(您的矩阵可以提供 operator[] 返回对辅助类实例的引用,该实例在turn 实现了它的operator[] 来访问矩阵,使得像matrix[1][2] 这样的调用成为可能)。

【讨论】:

"complexity: constant" - 这并没有提高效率。事实证明确实如此,但那句话并不能证明这一点。 好的,那么我可能会切换到使用一维向量来访问连续内存。或者您有其他建议方式吗? @harold 事实上,我假设 OP 将它与随机访问混淆了,比如一个列表。会澄清的。【参考方案2】:

来自 std::vector 的文档

元素是连续存储的,也就是说元素可以 不仅可以通过迭代器访问,还可以使用常规的偏移量 指向元素的指针。

所以,应该类似于从数组中访问,应该是高效的。

此外,向量中元素的随机访问是一个常数,与向量的大小无关。O(1)。您也可以像获取向量的底层数组并访问它一样使用它,但这应该与使用随机访问相同..

int main()

   std::vector<std::vector<int>> matrix;

   matrix.push_back(1,2,3);
   matrix.push_back(1,2,3);
   matrix.push_back(1,2,3);

   int op = matrix.data()[0][0] + matrix.data()[0][1];

   std::cout << "OP should be 3 : Real op" << op << std::endl;


【讨论】:

一个向量是连续的。两个相邻的几率很低。 10的几率低得惊人。因此,在 vector> 中,每次您在行(或列,如果这是您滚动的方式)之间切换时,可能会影响性能。从vec[0][0]vec[0][1]vec[0][2] 访问是小菜一碟,但是vec[0][2]vec[1][0] 可能会花费你,因为你刚刚切换了vectors。【参考方案3】:

在我的项目中,我定义了一个类Matrix 和数据结构 我使用的是std::vector&lt;std::vector&lt;T&gt; &gt; matrix;

现在我的问题是:我怎样才能更有效地访问它?

测量其当前速度,修改代码后再次测量。这将告诉您是否有任何可衡量的改进。

您可以尝试通过将数据存储为大小为 width x height 的简单 std::vector&lt;T&gt; 来提高性能,并在给定 x 和 y 时相应地计算偏移量。

例如(只是为了理解这个概念,没有错误检查或任何东西):

// ...

private:
    int width;
    std::vector<T> v;

// ...

public:

    Matrix(int width, int height) : width(width), v(width * height)
    
    

    T get(int x, int y) const
    
        return v[(y * width) + x];
    

// ...

但很有可能是您的机器速度太快,而您的数据太小,看不出有什么不同。

【讨论】:

其实我用的是几千个元素的矩阵,所以可能会有差异。反正我会试试的

以上是关于更有效地访问 std::vector的主要内容,如果未能解决你的问题,请参考以下文章

以有效的方式将 C 字符串转换为 std::vector<byte>

将数据从 std::vector 传递到 std::valarray 的最有效方法

std::vector 与 std::list 与 std::slist 的相对性能?

我是不是保证在移动向量后指向 std::vector 元素的指针有效?

将 std::vector<std::tuple<>> 转换为 torch::Tensor 的最有效方法是啥?

在winsock中重用向量作为数组的更有效方法?