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);