不,它们的效率并不低下。或许“和什么比较?”会是一个更有用的回答。当人们抱怨标准库容器的性能时,通常会是以下三个现实问题之一:
- 复制开销
- 查表很慢
- 我写的(浸入式)链表比 std::list 要快得多
在优化之前,请先考虑是否真有性能问题。在我收到的大多数案例中,性能问题只是理论上的或者只存在于想象中:首先仔细思量,除非必要,就不要优化。
让我们一个接一个地来分析这些问题。通常,vector<X> 要慢于某些人专门写的 My_container<X>,因为 My_container<X> 的实现是“一个保存指向 X 的指针的容器”。标准容器保存值的拷贝,当你将一个值放入容器时,该值是被复制进去的。对小型的值来说,这是无可挑剔的,但对大型对象来说,这又是非常的不合适的:
vector<int> vi;
vector<Image> vim;
// ...
int i = 7;
Image im("portrait.jpg"); // 使用文件来初始化 Image
// ...
vi.push_back(i); // 将 i(的一个拷贝)放入 vi
vim.push_back(im); // 将 im(的一个拷贝)放入 vim
假若 portrait.jpg 有好几兆那么大,而且 Image 是值语义(value semantics。例如,复制赋值和复制构造会创建新的拷贝),那么 vim.push_back(im) 的开销无疑是非常大的。但——俗话说得好——不要做赔本的买卖。取而代之,你应该使用容器来保存句柄或者指针。例如,如果 Image 是引用语义(reference semantics),那么上面的代码招致的仅仅是调用复制构造函数的开销,而且这个开销和大多数图像处理操作相比是微不足道的。如果某些类,比如 Image,因为一些合适的理由,必须采用复制语义(copy semantics),那么使用容器来保存其指针通常是个合理的解决方案:
vector<int> vi;
vector<Image> vim;
// ...
Image im("portrait.jpg"); // 使用文件来初始化 Image
// ...
vi.push_back(7); // 将 i(的一个拷贝)放入 vi
vim.push_back(&im); // 将 &im(的一个拷贝)放入 vim
自然而然,如果你使用指针,就必须考虑资源管理的问题,不过,保存指针的容器本身就可以是一个有效且低开销的资源处理器(通常,你需要这么一种容器:它带有用于删除“属于它的”对象的析构函数)。
第二个常见的现实问题是使用 map 来处理数量庞大的 (string,X) pair。map 适用于处理相对小型的容器(例如好几百或好几千个元素——访问 10000 个元素的 map 中的一个元素需要大约 9 次比较)。这些相对小型的容器的“小于”比较应该是低开销的,并且不能构建出优秀的哈希函数。如果你要处理大量字符串,而且也有一个优秀的哈希函数,那么 你应该使用哈希表。标准委员会的技术报告(Tecnical Report)中定义的 unordered_map 目前已经广泛可用,而且远远胜于大多数人的“私藏佳酿”。
有时,你可以使用 (const char*,X) pair 来代替 (string,X) pair,从而提高程序效率。但切记 < 并不能比较 C 风格的字符串。而且,如果 X 很庞大,你还是有可能遇到复制问题(可选用一种常用办法来解决)。
浸入式链表当然可以很快。然而,首先你应该考虑一下你是否需要使用链表:vector 更加紧凑,因此它比链表更小,而且在很多情况下也比链表更快——甚至于进行插入/删除操作时亦是如此。例如,如果你的 list 只不过拥有为数不多的整型元素,那么使用 vector 无疑会明显快于 list(无论任何链表)。而且,浸入式链表不能直接保存内建类型(int 没有 link 成员变量)。所以,假设你真的需要使用链表,而且你可以为每种元素类型提供 link 成员变量,才可以使用浸入式链表。每当进行插入元素的操作,标准库 list 默认会进行一次内存分配,然后将该元素复制到新分配好的空间里(而每当进行删除元素的操作,list 都会进行一次内存回收)。对于使用默认分配器的 std::list 来说,这样做可能会带来很明显的性能损失。对于复制开销不大的小型元素,可以考虑使用经过优化的分配器。只有当你需要使用链表并且不能错失哪怕是一盎司的 性能提升时,才应该使用自制的浸入式链表。
人们有时会担心 std::vector 的增长开销。我过去也担心这个,并且使用 reserve() 来优化其增长。在仔细思量代码,并且一次又一次地在实际程序中遇到难以计算 reserve() 所带来的性能提升这个麻烦之后,我停止了使用 reserve(),除非是为了避免迭代器失效(我的代码中很少有这种情况)而不得不使用它。重申:优化前请仔细思量。
原文地址:http://www.research.att.com/~bs/bs_faq2.html#slow-containers
分享到:
相关推荐
总之,Java中的容器提供了丰富的数据管理和操作能力,正确理解和运用这些容器,能够极大地提升程序的开发效率和运行性能。在实际应用中,开发者应根据具体需求和场景,合理选择并利用Java集合框架提供的各种容器类型...
在Java编程中,容器是用于...总之,理解并掌握这些Java容器的特性和效率差异,能够帮助我们在实际开发中做出更合适的选择,提高程序的性能和可维护性。在具体应用时,还需要结合业务需求和性能测试,才能做出最佳决策。
《GB 150-2011 压力容器标准》是中国关于压力容器设计、制造、检验和验收的重要规范,旨在确保压力容器的安全性和可靠性。这一标准由四个主要部分构成,每部分都涵盖了不同的专业领域,为相关行业的工程师和技术人员...
通过标准的统一,可以使得简单压力容器在全球范围内得到认可,为厂商提供更广阔的市场。 总而言之,简单压力容器的标准知识是工业生产中不可忽视的重要组成部分。通过严格的标准制定和执行,不仅可以确保压力容器的...
GB150-1998《钢制压力容器》是一项国家标准,其全称为GB150-1998《钢制压力容器》,该标准主要规定了钢制压力容器的设计、制造、检验和验收等方面的技术要求。在压力容器的设计和制造领域,该标准是基础性技术文件,...
JB4732钢制压力容器的分析标准和设计标准。
C++ STL标准库容器成员一览表 C++ STL(Standard Template Library)是C++标准库的一部分,提供了许多有用的容器、算法和迭代器,使得C++程序员能够更方便地编写高效、可维护的代码。STL容器是STL的核心部分,提供...
压力容器标准
容器标准化可以确保物流过程中的货物易于管理、追踪,区块链技术可以为物流过程提供一个不可篡改的数据记录平台,从而为智能物流系统提供稳定可靠的技术支持。结合这两项技术,可以构建一个从制造商到最终消费者的全...
OPener_ServerOpener_Server 是一个轻 Http 容器标准。 具体来说:以 Http Server 作为底层架构,以异步非阻塞模式为主要思想,通过 Http POST 模式构建一个可注入代码的容器,新注入的代码码依靠脚本语言内置的 ...
根据设计压力,压力容器被划分为低压、中压、高压和超高压四个等级。此外,根据其在工艺过程中的作用,容器可以分为反应器(R)、换热器(E)、分离器(S)和储罐(C)。这些分类有助于确定容器在实际应用中的功能和...
有色金属压力容器标准大全,针对国内标准,绝对超值。
GB150-2011《钢制压力容器》标准是压力容器设计、制造、检验的重要依据,它不仅涵盖了材料选择、结构设计、焊接工艺、检验与试验等方面的技术要求,还强调了安全附件与保护措施的重要性,为提高压力容器的安全性和...
在C++编程中,STL(Standard Template Library,标准模板库)是不可或缺的一部分,它提供了大量的容器、迭代器、算法和函数对象等工具,极大地提高了程序员的效率。本项目聚焦于C++标准容器的性能测试,特别是对于...
首先,让我们了解什么是容器。容器是一种轻量级的虚拟机,可以 isolating 应用程序。容器 runtime 或编排平台也可以将其视为一种 packaging 格式。Spark 是一个基于内存的计算引擎,可以在容器中运行。为了优化 ...
### 压力容器的分类 #### 一、引言 ...正确选择合适的容器类型不仅能够提高生产效率,还能确保操作人员的安全,减少潜在的风险。随着工业技术的发展,未来还可能出现更多新型的压力容器及其分类标准。
综上所述,JB-T 4735-1997标准为钢制焊接常压容器的设计、制造及检验提供了全面的技术指导,对于确保这类容器的安全运行具有重要意义。企业在生产和使用此类容器时应严格遵守该标准的各项规定,以确保产品的质量和...
VCAD是专为压力容器设计定制的一款CAD插件,它集成了多种专业功能,旨在简化复杂的容器设计流程,提高设计效率。通过集成在主流CAD软件中,如AutoCAD或SolidWorks,VCAD能够帮助工程师快速绘制容器的二维草图和三维...
尽管这部分文档存在文字识别的误差,但内容依然很丰富,为读者提供了STL标准库容器的性能特点与适用场景的全面分析。在实际应用中,开发者应根据特定需求选择合适的数据结构,以达到优化性能的目的。