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

为何reserve+emplace_back的std::vector比原生数组性能更慢?

关于reserve+emplace_back的std::vector仍比原生数组慢的原因分析

问题背景

已知使用resize或push_back的std::vector会因额外的对象创建/拷贝比原生数组慢,通常建议用reserve+emplace_back提升性能,但实际基准测试发现,即便用了reserve+emplace_back,std::vector仍比原生数组慢很多,请问原因何在?是否存在不公平对比的因素?

测试代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <vector>
#include <chrono>
#include <string>
#include <algorithm>
#include <climits>
using tp = std::chrono::steady_clock::time_point;
class Time {
public:
    static void show(tp t1, tp t2) { //time passed since t1
        std::cout << std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count() << '\t';
        printf("nanoseconds \n");
    }
    tp add() {
        tp p = std::chrono::steady_clock::now();
        return p;
    }
};

int main()
{
    Time time;
    const int VSIZE = 1000000;
    auto t1 = time.add();
    double vsizearr[VSIZE];
    for (auto i = 0; i < VSIZE; i++) {
        vsizearr[i]=i;
        asm volatile("" : : : "memory"); //doesn't allow compiler to erase the loop
    }
    auto t2 = time.add();
    std::vector<double> second;
    second.reserve(VSIZE);
    for (auto i = 0; i < VSIZE; i++) {
        second.emplace_back(i);
        asm volatile("" : : : "memory"); //doesn't allow compiler to erase the loop
    }
    auto t3 = time.add();
    time.show(t1, t2);
    time.show(t2, t3);
    return 0;
}

基准测试结果

Windows 10环境(C++17, LLVM-clang, -O1)

306300  nanoseconds
1824700 nanoseconds

Ubuntu 20.04环境(C17, g/clang, -O1)

root@vlad-VirtualBox:/home/vlad/Documents clang++ -O1 -std=c++17 -o test test.cpp
root@vlad-VirtualBox:/home/vlad/Documents ./test
284340 nanoseconds 
3115997 nanoseconds 
root@vlad-VirtualBox:/home/vlad/Documents g++ -O1 -std=c++17 -o test test.cpp
root@vlad-VirtualBox:/home/vlad/Documents ./test
284340 nanoseconds 
3145702 nanoseconds 

注:-O3优化下结果与-O1大致相同。

原因分析

1. 内存分配的本质差异

  • 原生数组double vsizearr[VSIZE]是栈上分配,仅通过调整栈指针完成,几乎无额外开销;而std::vector即便用reserve,也是在堆上分配内存,堆分配需要经过内存管理器的复杂逻辑(如查找空闲块、更新内存链表等),这本身就会带来显著开销。

2. emplace_back的额外状态维护

  • emplace_back并非单纯的内存赋值:每次调用除了构造基础类型double(本质是赋值),还需要更新vector的size成员变量,这是额外的写内存操作;而原生数组直接操作内存地址,无任何状态维护步骤。

3. 编译器优化的偏向性

  • 编译器对栈数组的优化更直接:栈内存地址编译期即可确定,能生成更高效的直接内存访问指令;而vector的内部数据指针是运行期确定的堆地址,编译器优化时需考虑指针别名等问题,生成的指令效率可能不如栈数组。

4. 基准测试的不公平点

  • 测试代码中,vector的reserve堆分配开销被计入了t2到t3的统计时间内;而原生数组的栈分配开销几乎可以忽略,未对测试结果产生影响。若单独排除reserve的开销,两者的性能差距会有所缩小。

5. 缓存效率差异

  • 栈内存属于CPU缓存的热点区域,访问频率高,CPU预取机制更高效;堆内存地址相对随机,缓存命中率可能更低,导致内存访问延迟更高。

总结

核心原因是栈分配与堆分配的本质开销差异,再加上vector内部状态维护的额外操作、编译器优化偏向性以及缓存效率差异共同导致了性能差距。同时测试代码中未剥离reserve的堆分配开销,进一步放大了两者的性能差。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 15:40:28