You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中二维vector与数组的性能优化探究及疑问

C++大型连续二维数组结构化访问性能测试与疑问

测试背景与环境

我正在优化C++中大型连续二维数组的结构化访问性能,但网上关于最优实现的信息杂乱不一。为此我用以下环境做了四种方案的性能测试:

  • 编译器:clang 14.0.3
  • 编译参数:g++ -std=c++20 -O3 vectortest.cpp -o vectortest

测试代码

#include <vector>
#include <chrono>
#include <iostream>

using namespace std::chrono;

#define ARRAYSIZE 25000

template <typename T>
class vector2d {
    public:
        size_t size_x, size_y;
        std::vector<T> mvec;

    public:
        vector2d(
            size_t a_size_x,
            size_t a_size_y
        ) :
            size_x(a_size_x),
            size_y(a_size_y)
        {
            mvec.resize(size_x * size_y);
        }

    public:
        T & operator()(size_t i, size_t j) {
            return mvec[i * size_x + j];
        }
};

template <typename T>
class array2d {
    public:
        size_t size_x, size_y;
        T * mvec;

    public:
        array2d(
            size_t a_size_x,
            size_t a_size_y
        ) :
            size_x(a_size_x),
            size_y(a_size_y)
        {
            mvec = new T[size_x * size_y];
        }

        ~array2d() {
            delete[] mvec;
        }

    public:
        T & operator()(size_t i, size_t j) {
            return mvec[i * size_x + j];
        }
};

double vectorvector() {
    std::vector< std::vector<double> > x(ARRAYSIZE, std::vector<double>(ARRAYSIZE));

    for (size_t i = 0; i < ARRAYSIZE; i++) {
        for (size_t j = 0; j < ARRAYSIZE; j++) {
            x[i][j] = static_cast<double>(i) * static_cast<double>(j);
        }
    }

    return x[ARRAYSIZE/2][ARRAYSIZE/2];
}

double vector_contiguous() {
    std::vector<double> x(ARRAYSIZE*ARRAYSIZE);

    for (size_t i = 0; i < ARRAYSIZE; i++) {
        for (size_t j = 0; j < ARRAYSIZE; j++) {
            x[i * ARRAYSIZE + j] = static_cast<double>(i) * static_cast<double>(j);
        }
    }

    return x[(ARRAYSIZE/2)*ARRAYSIZE + (ARRAYSIZE/2)];
}

double vector2d_class() {
    vector2d<double> x(ARRAYSIZE, ARRAYSIZE);

    for (size_t i = 0; i < ARRAYSIZE; i++) {
        for (size_t j = 0; j < ARRAYSIZE; j++) {
            x(i,j) = static_cast<double>(i) * static_cast<double>(j);
        }
    }

    return x(ARRAYSIZE/2,ARRAYSIZE/2);
}

double array2d_class() {
    array2d<double> x(ARRAYSIZE, ARRAYSIZE);

    for (size_t i = 0; i < ARRAYSIZE; i++) {
        for (size_t j = 0; j < ARRAYSIZE; j++) {
            x(i,j) = static_cast<double>(i) * static_cast<double>(j);
        }
    }

    return x(ARRAYSIZE/2,ARRAYSIZE/2);
}

int main() {

    std::chrono::time_point<std::chrono::high_resolution_clock> start, stop;
    microseconds duration;

    start = high_resolution_clock::now();
    std::cout << vectorvector() << std::endl;
    stop = high_resolution_clock::now();
    duration = duration_cast<microseconds>(stop - start);
 
    std::cout << "vectorvector: " << duration.count() << " microseconds" << std::endl;

    start = high_resolution_clock::now();
    std::cout << vector_contiguous() << std::endl;
    stop = high_resolution_clock::now();
    duration = duration_cast<microseconds>(stop - start);

    std::cout << "vector_contiguous: " << duration.count() << " microseconds" << std::endl;

    start = high_resolution_clock::now();
    std::cout << vector2d_class() << std::endl;
    stop = high_resolution_clock::now();
    duration = duration_cast<microseconds>(stop - start);

    std::cout << "vector2d_class: " << duration.count() << " microseconds" << std::endl;

    start = high_resolution_clock::now();
    std::cout << array2d_class() << std::endl;
    stop = high_resolution_clock::now();
    duration = duration_cast<microseconds>(stop - start);

    std::cout << "array2d_class: " << duration.count() << " microseconds" << std::endl;

}

测试结果

vectorvector: 3291998 microseconds
vector_contiguous: 2238134 microseconds
vector2d_class: 2221989 microseconds
array2d_class: 1544225 microseconds

疑问解答

1. 这些测试结果是否符合预期?

完全符合预期:

  • vectorvector性能最差:它是嵌套vector结构,每个子vector的内存独立分配,物理上不连续。遍历的时候会频繁触发缓存失效——CPU加载完一行数据后,下一行的内存地址可能不在当前缓存页中,需要从内存重新加载,这会极大降低效率,比连续内存方案慢45%是合理的。
  • array2d比vector实现更快:虽然vector内部也是连续内存,但编译器对裸指针的优化通常更激进。vector的operator[]或data()访问需要经过一层成员函数,即便-O3会inline,但裸指针的直接访问减少了潜在的中间操作;另外,new[]分配的内存和vector默认allocator分配的内存可能在对齐策略或分配路径上有差异,导致裸指针版本的内存访问效率更高。

2. 实验存在明显缺陷吗?

有几个值得注意的缺陷:

  • 计时包含IO操作:代码中std::cout放在了计时区间内,IO操作的延迟会严重干扰性能测量,应该先计算并保存结果,计时结束后再输出。
  • 单次测试误差大:仅跑一次测试无法排除系统调度、内存碎片化等外界因素的干扰,应该多次运行取平均值。
  • 测试场景单一:只测试了写操作,未覆盖读操作、随机访问等场景,不同场景下的性能差异可能不同。
  • 数组尺寸影响:25000×25000的数组占用约5GB内存,远超CPU缓存容量,此时内存带宽成为瓶颈,内存连续性的影响被放大。可以尝试更小的数组(能放入L3缓存),观察性能差距是否缩小。

3. 有哪些值得尝试的替代方案?

  • C++20 std::mdspan:标准库提供的多维视图,可直接包装一维vector或裸数组,提供结构化的多维访问接口,无额外内存开销,性能接近裸指针。
  • 自定义对齐内存分配:使用aligned_alloc或posix_memalign分配对齐内存(比如64字节对齐适配AVX指令),vector可通过自定义allocator实现对齐,提升缓存命中率和SIMD优化效率。
  • 第三方数值库:比如Eigen、Boost.MultiArray,这类库针对多维数组做了深度优化,支持自动SIMD并行、缓存友好的遍历策略,性能远超手动实现。
  • std::span包装:用std::span<std::span<double>>包装连续内存,提供类似二维数组的访问方式,适合C++20及以上版本。

4. STL为何未提供多维vector类型?

主要原因有三点:

  • STL组件化设计理念:STL遵循单一职责原则,vector的定位是动态一维连续数组。多维数组可通过一维vector+自定义访问逻辑实现,无需专门的标准库组件,保持STL的简洁性。
  • 需求多样性:多维数组的访问模式(行优先/列优先)、维度数量、内存布局等需求差异大,标准库很难提供一个满足所有场景的通用实现,不如让开发者根据需求自行封装或使用第三方库。
  • 标准补充已到位:C++20引入的std::mdspan已经填补了多维访问的空白,它可以作为一维vector的视图,提供结构化的多维操作,无需单独设计多维vector。

内容的提问来源于stack exchange,提问作者aycarus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 20:04:51