C++ C++;固定大小向量的有效增长向量
在我的程序中,我有一个C++ C++;固定大小向量的有效增长向量,c++,C++,在我的程序中,我有一个std::vector vec,其中n_通道是编译时已知的常量整数。在程序中,vecvec会随着时间的推移而增长 现在我想取消编译时必须知道n_通道的限制,因此我将定义更改为std::vector vecn_通道仍然是一个固定值,在构建vecvec之前已知该值(所有vecvec的元素具有相同的长度) 然而,现在我的程序突然慢了2.5倍 我假设这是因为vecvec的内存突然被碎片化了,因为它不“知道”vecvec的每个元素都有相同的大小 有什么办法可以让我既吃蛋糕又吃蛋糕吗?
std::vector vec
,其中n_通道
是编译时已知的常量整数。在程序中,vecvec
会随着时间的推移而增长
现在我想取消编译时必须知道n_通道
的限制,因此我将定义更改为std::vector vec
n_通道
仍然是一个固定值,在构建vecvec
之前已知该值(所有vecvec
的元素具有相同的长度)
然而,现在我的程序突然慢了2.5倍
我假设这是因为vecvec
的内存突然被碎片化了,因为它不“知道”vecvec的每个元素都有相同的大小
有什么办法可以让我既吃蛋糕又吃蛋糕吗?你想既吃蛋糕又吃蛋糕吗?现在就实现您自己的可调整行大小的2D数组类!!!
您可以编写自己的2D数组类。通过在内存中使行连续,您可以获得使用std::vector
的所有好处,但不需要固定的编译器时间大小!为了简化实现,可以将其包装为std::vector
template<class T>
struct Row {
T* _start;
size_t _size;
// These are const because if we need the elements to be const
// We just make T const
T* begin() const noexcept { return _start; }
T* end() const noexcept { return _start + _size; }
size_t size() const noexcept { return _size; }
T& operator[](size_t index) const noexcept {
return _start[index];
}
// Implicitly convertible to Row<T const>
operator Row<T const>() const noexcept {
return {_start, _size};
}
};
为了实现完整的功能,我们还应该创建两个“helper”类。其中一个表示数组中的一行,另一个表示该行的迭代器。当我们遍历2D数组时,我们将遍历数组中的行
行类
这很直截了当。它只包含一个开始和结束指针。数组是连续存储的,因此我们实际上不存储行
s,但使用它们仍然很方便,因此我们有一个要迭代的类型
由于行
类仅表示矩阵中的一行视图,行类不应分配或删除任何内存。此外,我将行
类的所有成员函数设置为常量,以便可以对直接从行迭代器
返回的行
执行操作
template<class T>
struct Row {
T* _start;
size_t _size;
// These are const because if we need the elements to be const
// We just make T const
T* begin() const noexcept { return _start; }
T* end() const noexcept { return _start + _size; }
size_t size() const noexcept { return _size; }
T& operator[](size_t index) const noexcept {
return _start[index];
}
// Implicitly convertible to Row<T const>
operator Row<T const>() const noexcept {
return {_start, _size};
}
};
基准结果
基准测试结果在中,vector2D
与使用数组向量一样快
测试
测试分为两部分:
- 用值填充二维数组
- 将所有值相加
std::vector
、std::vector
或我们自己的vector2D
一起使用
template<class List>
auto calculateSum2D(List const& list) {
using elem_t = std::decay_t<decltype(list[0][0])>;
elem_t initial = 0;
for(auto const& row : list) {
for(auto& elem : row) {
initial += elem;
}
}
return initial;
}
template<class List>
void fill(List& list, int rows, int cols) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
list[i][j] = i * j;
}
}
}
基准v2:无重复分配
事实证明,在初始基准中,大部分成本来自重复分配(在所有情况下,对象在基准的每次迭代中都被重新分配)。为了解决这个问题,我将声明移出了循环,这样它只会出现一次。我还调整了行和列的数量,这样就有了更多的行和更少的列,从而得到一个更现实的场景,在这个场景中,并不是所有的东西都适合缓存
再一次,vector2D
和vector
总结
根据基准测试结果,vector2D
应该可以让您的性能恢复到原来的水平。因为您的代码可能包含分配和使用的混合,所以您得到的结果介于两个基准测试之间(vector of vector慢2.5倍)。因为vector2D
是连续的,并且避免了困扰向量向量方法的重复堆分配,所以它应该与数组向量一样快 您是否预先预留了足够的空间?顺便问一下,如果vecvec
的每个元素都具有相同的大小,那么std::vector
可能是错误的数据结构。考虑使用一个平面<代码> STD::向量< /代码>不释放迭代器调试释放构建吗?你介绍过吗?什么编译器/平台?很难说为什么你的程序运行较慢,因为你没有提供任何代码。我的猜测是,这与开销有关。正如@formerlyknownas_463035818所建议的,您可以通过使用驱动硬盘的功能来消除大部分开销。我对它进行了基准测试,我的实现速度是使用向量向量的4.5倍。上面提供了基准测试的详细信息,这是一个带有链接的长链接,这样您就可以自己运行基准测试了!我会为你的阵型做2D。周六晚上6点?啊,该死。那就必须这样了。这是一个很好的解释。。。。花了将近30分钟完成阅读……)
template<class List>
auto calculateSum2D(List const& list) {
using elem_t = std::decay_t<decltype(list[0][0])>;
elem_t initial = 0;
for(auto const& row : list) {
for(auto& elem : row) {
initial += elem;
}
}
return initial;
}
template<class List>
void fill(List& list, int rows, int cols) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < cols; j++) {
list[i][j] = i * j;
}
}
}
// Benchmark using a vector of vectors
static void sumVector(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
std::vector<std::vector<double>> vect(rows, std::vector<double>(cols));
fill(vect, rows, cols);
auto sum = calculateSum2D(vect);
benchmark::DoNotOptimize(sum);
}
}
// Register the function as a benchmark
BENCHMARK(sumVector);
// Benchmark using a vector of arrays
static void sumArray(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
std::vector<std::array<double, cols>> vect(rows, std::array<double, cols>());
fill(vect, rows, cols);
auto sum = calculateSum2D(vect);
benchmark::DoNotOptimize(sum);
}
}
// Register the function as a benchmark
BENCHMARK(sumArray);
// Benchmark using vector2D implementation
static void sumvector2D(benchmark::State& state) {
// Code inside this loop is measured repeatedly
for (auto _ : state) {
vector2D<double> vect(rows, cols);
fill(vect, rows, cols);
auto sum = calculateSum2D(vect);
benchmark::DoNotOptimize(sum);
}
}
// Register the function as a benchmark
BENCHMARK(sumvector2D);