提问人:luca 提问时间:5/7/2021 更新时间:11/17/2023 访问量:2242
有没有一种有效的方法来切片 C++ 向量,给定一个包含要切片的索引的向量
Is there an efficient way to slice a C++ vector given a vector containing the indexes to be sliced
问:
我正在努力实现用MATLAB编写的C++代码。
在 MATLAB 中,您可以将一个数组与另一个数组(如 A(B)))进行切片,这会在 B 中元素的值指定的索引处生成一个新的 A 元素数组。
我想使用向量在C++中做类似的事情。这些向量的大小为 10000-40000 个 double 类型的元素。
我希望能够使用另一个包含要切片的索引的 int 类型的向量对这些向量进行切片。
例如,我有一个向量 v = <1.0、3.0、5.0、2.0、8.0>和一个向量 w = <0、3、2>。我想使用 w 对 v 进行切片,使切片的结果是一个新向量(因为旧向量必须保持不变)x = <1.0、2.0、5.0>。
我想出了一个函数来做到这一点:
template<typename T>
std::vector<T> slice(std::vector<T>& v, std::vector<int>& id) {
std::vector<T> tmp;
tmp.reserve(id.size());
for (auto& i : id) {
tmp.emplace_back(v[i]);
}
return tmp;
}
我想知道是否有更有效的方法来完成这样的任务。速度是这里的关键,因为这个切片函数将位于一个大约有 300000 次迭代的 for 循环中。我听说 boost 库可能包含一些有效的解决方案,但我还没有使用它的经验。
我使用 chrono 库来测量调用此切片函数所需的时间,其中要切片的向量长度为 37520,包含索引的向量大小为 1550。对于此函数的单次调用,经过的时间 = 0.0004284s。但是,在 ~300000 次 for 循环迭代中,总运行时间为 134 秒。
任何建议都会非常感激!
答:
emplace_back
有一些开销,因为它涉及内部的一些内部会计。请尝试以下操作:std::vector
template<typename T>
std::vector<T> slice(const std::vector<T>& v, const std::vector<int>& id) {
std::vector<T> tmp;
tmp.resize (id.size ());
size_t n = 0;
for (auto i : id) {
tmp [n++] = v [i];
}
return tmp;
}
此外,我在您的内部循环中删除了不必要的取消引用。
编辑:我又想了想,受到@jack回答的启发,我认为内部循环(这是最重要的)可以进一步优化。这个想法是将循环使用的所有内容都放在局部变量中,这为编译器提供了优化代码的最佳机会。所以试试这个,看看你得到什么时间。确保测试发布/优化版本:
template<typename T>
std::vector<T> slice(const std::vector<T>& v, const std::vector<int>& id) {
size_t id_size = id.size ();
std::vector<T> tmp (id_size);
T *tmp_data = tmp.data ();
const int *id_data = id.data ();
const T* v_data = v.data ();
for (size_t i = 0; i < id_size; ++i) {
tmp_data [i] = v_data [id_data [i]];
}
return tmp;
}
评论
std::vector<T> tmp(id.size());
性能似乎有点慢;您是否使用编译器优化进行构建(例如。 或者,如果使用 IDE,则切换到发布模式)。仅此一项就将计算时间缩短了约 10 倍。g++ main.cpp -O3
如果您已经在使用优化,通过使用基本的 for 循环迭代,我的机器上的计算时间加快了大约 2-3 倍,这个想法是,编译器不必解析类型引用的内容,并且由于基本的 for 循环一直存在于 C++ 中,编译器可能有很多技巧来加快它。(for int i = 0; i < id.size(); i++)
auto
template<typename T>
std::vector<T> slice(const std::vector<T>& v, const std::vector<int>& id){
// @Jan Schultke's suggestion
std::vector<T> tmp(id.size ());
size_t n = 0;
for (int i = 0; i < id.size(); i++) {
tmp [n++] = v [i];
}
return tmp;
}
评论
auto
auto
我想使用向量在C++中做类似的事情。
C++ 对 Matlab 可切片向量的回答不是;它。std::vector
std::valarray
https://en.cppreference.com/w/cpp/numeric/valarray
C++ 支持借助 实例化其他对象。std::valarray
std::valarray
std::slice
一旦你有了你的valarray切片对象,C++显式地支持将标量运算应用于它的每个元素,不仅使用基本的算术运算,而且还通过将函数传递给每个元素来应用函数。std::valarray
std::valarray::apply
评论
for (auto i : id)
应该比 运行得更快,因为它涉及的最内层循环的每次迭代少一个取消引用。此外,将函数参数声明为 const ref 可能有助于编译器优化代码。for (auto& i : id)