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

Python deque与C++ list随机位置增删性能对比及C++优化咨询

优化C++双链表随机位置增删性能的问题

需求背景

需要实现一个队列,支持按指定位置插入对象、删除队列头部或中间对象;每次增删操作后需更新后续对象的位置,采用队列索引跟踪位置。这类场景需要频繁在数据结构的第n个元素位置执行增删操作,双链表的增删操作本身是O(1)复杂度,但定位到目标位置的效率是关键。

测试代码

Python实现(script.py)

from collections import deque
import random

class Event():
    def __init__(self):
        self.a = random.randint(0, 9)
        self.b = random.randint(0, 9)

structure = deque()

for i in range(0, 5000000, 1):
    event = Event()
    structure.insert(random.randint(0, 1000), event)

for i in range(0, 1000000, 1):
    del structure[random.randint(0, 1000)]

C++实现(script.cpp)

#include <iostream>
#include <list>
#include <random>

class Event {
        public:
                Event() {
                        std::random_device rd;
                        std::mt19937 gen(rd());
                        std::uniform_int_distribution<> dis(0, 9);
                        a = dis(gen);
                        b = dis(gen);
                }
                int a;
                int b;
};

int main() {
        std::list<Event> structure;

        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(0, 1000);

        for (int i = 0; i < 5000000; ++i) {
                Event event;

                auto it = structure.begin();
                std::advance(it, dis(gen));
                structure.insert(it, event);
        }

        for (int i = 0; i < 1000000; ++i) {
                auto it = structure.begin();
                std::advance(it, dis(gen));

                structure.erase(it);
        }
        return 0;
}

测试结果

  • g++ -O3编译C++代码后运行耗时:28.28秒
  • Python 3.11版本运行耗时:9.43秒

C++版本性能不如预期,经微基准测试,主要开销来自std::advance方法。

当前C++实现的不足

  • 数据结构选择不当:std::list是双向链表,std::advance需要从begin()开始逐个移动迭代器到目标索引,时间复杂度为O(k)(k为目标索引值)。而Python的deque底层是分段连续存储结构,支持O(min(k, n-k))的索引访问,在本次测试的0-1000索引范围内几乎是O(1)级别的效率,这是核心性能差距。
  • 随机数生成冗余:Event构造函数中每次都重新初始化std::random_device和std::mt19937,带来不必要的初始化开销。
  • 缺乏边界检查:当生成的随机索引超过当前std::list的元素数量时,std::advance会将迭代器移动到end(),此时执行erase(end())属于未定义行为,存在崩溃风险。

优化方案

1. 替换为std::deque数据结构

C++的std::deque与Python的deque底层实现逻辑类似,采用分段连续存储,索引访问效率远高于std::list。对于本次测试中0-1000的随机索引,std::deque可以直接通过begin() + idx定位到目标位置,时间复杂度接近O(1)。

修改后的核心代码示例:

#include <iostream>
#include <deque>
#include <random>

class Event {
public:
    static std::mt19937 gen;
    static std::uniform_int_distribution<> dis;
    
    Event() {
        a = dis(gen);
        b = dis(gen);
    }
    int a;
    int b;
};

// 初始化静态随机数生成器
std::mt19937 Event::gen(std::random_device{}());
std::uniform_int_distribution<> Event::dis(0, 9);

int main() {
    std::deque<Event> structure;

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> idx_dis(0, 1000);

    const int insert_count = 5000000;
    for (int i = 0; i < insert_count; ++i) {
        size_t idx = idx_dis(gen);
        // 确保索引不超过当前容器大小,超出则插入到末尾
        if (idx > structure.size()) {
            idx = structure.size();
        }
        structure.insert(structure.begin() + idx, Event());
    }

    const int erase_count = 1000000;
    for (int i = 0; i < erase_count; ++i) {
        if (structure.empty()) break;
        size_t idx = idx_dis(gen);
        // 确保索引合法,超出则取最后一个元素
        if (idx >= structure.size()) {
            idx = structure.size() - 1;
        }
        structure.erase(structure.begin() + idx);
    }
    return 0;
}

2. 复用随机数生成器

将Event类中的随机数生成器改为静态成员,避免每次构造对象时重复初始化,减少额外开销。

3. 增加边界合法性检查

在插入和删除操作前,校验随机索引是否在当前容器的有效范围内,避免未定义行为。

4. 可选:自定义带索引优化的双链表(极致场景)

如果业务场景必须使用双链表,可以实现带跳表(Skip List)或节点大小统计的自定义结构,将索引定位的时间复杂度降低到O(log n),但实现复杂度较高,仅适合对性能有极致要求的场景。

优化效果预期

改用std::deque后,索引定位的开销会大幅降低,结合C++本身的底层执行效率,性能会超过Python版本,填补之前的性能差距。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 06:20:44