`
colin115
  • 浏览: 41148 次
社区版块
存档分类
最新评论

C++的性能优化实践

    博客分类:
  • C++
阅读更多

优化准则:

1. 二八法则:在任何一组东西中,最重要的只占其中一小部分,约20%,其余80%的尽管是多数,却是次要的;在优化实践中,我们将精力集中在优化那20%最耗时的代码上,整体性能将有显著的提升; 这个很好理解。函数A虽然代码量大,但在一次正常执行流程中,只调用了一次。而另一个函数B代码量比A小很多,但被调用了1000次。显然,我们更应关注B的优化。
2. 编完代码,再优化;编码的时候总是考虑最佳性能未必总是好的;在强调最佳性能的编码方式的同时,可能就损失了代码的可读性和开发效率;  

工具:

1 Gprof

工欲善其事,必先利其器。对于Linux平台下C++的优化,我们使用gprof工具。 gprof是GNU profile工具,可以运行于linux、AIX、Sun等操作系统进行C、C++、Pascal、Fortran程序的性能分析,用于程序的性能优化以及程序瓶颈问题的查找和解决。通过分析应用程序运行时产生的“flat profile”,可以得到每个函数的调用次数,消耗的CPU时间(只统计CPU时间,对IO瓶颈无能为力),也可以得到函数的“调用关系图”,包括函数调用的层次关系,每个函数调用花费了多少时间。

2. gprof使用步骤

1)    用gcc、g++、xlC编译程序时,使用-pg参数,如:g++ -pg -o test.exe test.cpp 编译器会自动在目标代码中插入用于性能测试的代码片断,这些代码在程序运行时采集并记录函数的调用关系和调用次数,并记录函数自身执行时间和被调用函数的执行时间。
2)    执行编译后的可执行程序,如:./test.exe。该步骤运行程序的时间会稍慢于正常编译的可执行程序的运行时间。程序运行结束后,会在程序所在路径下生成一个缺省文件名为gmon.out的文件,这个文件就是记录程序运行的性能、调用关系、调用次数等信息的数据文件。
3)    使用gprof命令来分析记录程序运行信息的gmon.out文件,如:gprof test.exe gmon.out则可以在显示器上看到函数调用相关的统计、分析信息。 上述信息也可以采用gprof test.exe gmon.out> gprofresult.txt重定向到文本文件以便于后续分析。

以上只是gpro的使用步骤简介,关于gprof使用实例详见附录1;  

实践

我们的程序遇到了性能瓶颈,在采用架构改造,改用内存数据库之前,我们考虑从代码级入手,先尝试代码级的优化; 通过使用gprof分析,我们发现以下2个最为突出的问题:

1.初始化大对象耗时

分析报告:307   6.5% VOBJ1::VOBJ1@240038 VOBJ1
在整个执行流程中被调用307次,其对象初始化耗时占到6.5%。

这个对象很大,包含的属性多,属于基础数据结构;   
    在程序进入构造函数函数体之前,类的父类对象和所有子成员变量对象已经被生成和构造。如果在构造函数体内位其执行赋值操作,显示属于浪费。如果在构造函数时已经知道如何为类的子成员变量初始化,那么应该将这些初始化信息通过构造函数的初始化列表赋予子成员变量,而不是在构造函数函数体中进行这些初始化。因为进入构造函数函数体之前,这些子成员变量已经初始化过一次了。
  

     在C++程序中,创建/销毁对象是影响性能的一个非常突出的操作。首先,如果是从全局堆中生成对象,则需要首先进行动态内存分配操作。众所周知,动态分配/回收在C/C++程序中一直都是非常耗时的。因为牵涉到寻找匹配大小的内存块,找到后可能还需要截断处理,然后还需要修改维护全局堆内存使用情况信息的链表等。      

      解决方法:我们将大部分的初始化操作都移到初始化列表中,性能消耗降到1.8%。  

2.Map使用不当

分析报告:89   6.8% Recordset::GetField
Recordset的getField被调用了89次,性能消耗占到6.8%;
       Recordset是我们在在数据库层面的包装,对应取出数据的记录集;(用过ADO的朋友很熟悉);

     由于我们使用的是底层c++数据库接口,通过对数据库原始api进行一层包装,从而屏蔽开发人员对底层api的直接操作。 这样的包装,带来的好处就是不用直接与底层数据库交互,在代码编写方面方便不少,代码可读性也很好;带来的问题就是性能的损失;

分析:(2点原因)
1)在GetField函数中,使用了map[“a”]来查询数据,如果找不到“a”,则map会自动插入key ”a”,并设value为0;而m.find(“a”)不会自动插入上述pair,执行效率更高; 原有逻辑:

string Recordset::GetField(const string &strName)
{
    int nIndex;
    if (hasIndex==false)
    {
        nIndex = m_nPos;
    }
    else
    {
        nIndex = m_vSort[m_nPos].m_iorder;
    }
    if (m_fields[strName]==0)
    {
        LOG_ERR("Recordset::GetField:"<<strName<<" Not Find!!");
    }
    return m_records[nIndex].GetValue(m_fields[strName] - 1) ;
}

改造后的逻辑:

string Recordset::GetField(const string &strName)
{
    unordered_map<string, int>::iterator iter = m_fields.find(strName);
    if (iter == m_fields.end())
    {
        LOG_ERR("[Recordset::GetField] "<< strName <<" Not Find!!");
    }
    int nIndex = hasIndex ? m_vSort[m_nPos].m_iorder : m_nPos;
    return m_records[nIndex].GetValue(iter->second - 1) ;
}

调整后的Recordset::GetField的执行时间约是之前的1/2;且易读性更高;

2)在Recordset中,对于每个字段的存储,使用的是map<string, int> m_fields;  g++中的stl标准库中默认使用的红黑树作为map的底层数据结构;

通过附录中的文档2,我们发现其实有更快的结构, 在效率上,unorder map优于hash map, hash map 优于 红黑树; 如果不要求map有序,unordered_map 是更好的选择;

解决方法:将map结构换成unordered_map,性能消耗降到1.4%;  

总结

我们修改不到30行代码,整体性能提升10%左右,效果明显; 打蛇打七寸,性能优化的关键在于找准待优化的点,之后的事,也就水到渠成;
by the way,对于Linux平台使用C++工作的朋友,推荐一本好书:《程序员的自我修养》。这本书介绍了运行库相关的各种技术。 对装载、链接和库进行了深入浅出的剖析。看过真是大呼过瘾;

附录:

附1:prof工具介绍及实践
附2: map hash_map unordered_map 性能测试

Posted by: 大CC | 03JUN,2013

博客:blog.me115.com 

0
2
分享到:
评论

相关推荐

    C++高效编程:内存与性能优化(pdf版)

    《C++高效编程:内存与性能优化》是一本专注于C++编程语言内存管理和性能调优的书籍。本书深入探讨了如何在C++编程中高效地使用内存资源,以及如何进行性能优化,以编写出既高效又优雅的代码。 书籍作者Rene ...

    C++性能优化英文版

    ### C++性能优化知识点解析 #### 一、前言与背景 C++作为一种广泛应用于高性能计算领域的编程语言,其性能优化对于提升软件系统的整体效率至关重要。《Optimized C++》一书由Kurt Guntheroth编写,旨在为读者提供一...

    CPP-Summit 2019 - 李永顺 - 嵌入式系统C++性能优化.pdf

    根据提供的文档信息,我们可以总结出以下关于嵌入式系统中C++性能优化的关键知识点: ### 一、嵌入式系统C++性能优化的重要性 在嵌入式系统开发中,C++是一种广泛使用的编程语言,因为它提供了高效能和对底层硬件...

    用Valgrind和GDB进行C-C++项目的性能优化与调试.md

    如何使用 Valgrind 和 GDB 进行 C/C++ 项目的性能优化与调试。首先,Valgrind 是一套强大的动态分析工具集,可检测内存泄漏、非法内存访问和数据竞争等问题;其中,Memcheck、Massif 和 Cachegrind 是常用工具,用于...

    C++高效编程:内存与性能优化(PDG)

    在C++编程中,内存管理和性能优化是提升程序效率的关键环节。C++高效编程的核心在于理解和熟练运用语言特性,以实现内存的有效管理并减少不必要的计算开销。以下是一些关于C++内存与性能优化的重要知识点: 1. **...

    C++高效编程:内存与性能优化

    在C++编程中,内存管理和性能优化是两个关键领域,对于开发高质量、高效能的应用程序至关重要。本主题将深入探讨这两个方面,旨在帮助开发者更好地理解如何编写更优化的C++代码。 内存管理是C++的核心特性之一,...

    C++内存与性能优化

    理解C++11及以后版本的并发库,如`std::thread`、`std::async`和`std::mutex`,并掌握同步原语的使用,是现代C++性能优化的重要部分。 以上只是《C++内存与性能优化》中可能涉及的一部分内容,实际书籍可能会涵盖更...

    《C++程序设计实践教程》

    同时,理解并遵循C++的最佳实践,如内存管理、错误处理和性能优化,是成为一名专业C++程序员的必备素养。 总的来说,《C++程序设计实践教程》是一本全面介绍C++语言特性和编程实践的资源,对于初学者和有一定经验的...

    提高C++性能的编程技术

    理解何时应使用内联,何时不应使用,是性能优化的关键。 5. **模板元编程**:模板元编程允许在编译时执行计算,减少运行时开销。但需谨慎使用,因为它可能会增加编译时间和代码复杂性。 6. **C++11/14/17新特性**...

    [提高C++性能的编程技术].左飞.扫描版.pdf

    C++性能优化的关键在于理解语言的底层机制,包括内存管理、对象生命周期、模板元编程以及对硬件的直接访问。以下是一些核心的知识点: 1. **内存管理**:C++允许程序员直接操作内存,这既是它的优势也是挑战。了解...

    c++应用程序性能优化第1和第2版本

    《C++应用程序性能优化》一书会详细讲解这些知识点,并提供实际案例分析和实践建议,帮助读者深入理解C++性能优化的各个方面,提升编程技能。通过阅读这两版书籍,开发者不仅可以学习到理论知识,还能获得实战经验,...

    C++优化方法C++有效使用内存

    在C++编程中,优化是提高程序性能的关键环节。有效的内存管理是优化的重要组成部分,它不仅关乎程序运行速度,还关系到资源的高效利用。本文将深入探讨C++中的优化方法,特别是关于如何有效地使用内存。 一、内存...

    深入探索C++编译器优化技术

    适合人群:面向对性能有较高要求或需要深入理解编译器优化机制的C++程序员及编程爱好者。 使用场景及目标:适用于希望提高自身编写的C++应用程序性能的开发者,通过掌握优化技巧从而达到改善运行效率的目的。 阅读...

    C++ 最佳实践 cppbestpractices

    文档可能会涉及如何利用现代C++的特性来提升程序性能,比如避免不必要的内存分配,利用好编译器优化等。 脚本化:文档也可能鼓励开发者编写脚本来自动化重复性的任务,比如代码的格式化、性能测试等,这能显著提高...

    优化C++程序编译效率的实例.txt

    综上所述,《优化C++程序编译效率的实例》不仅提供了具体的代码示例,还深入讲解了C++性能优化的多个方面,包括内存管理、IO操作、函数调用、链接器选项以及特定平台下的优化技巧。这些知识对于希望提高C++程序性能...

    C++程序设计原理与实践 高清 扫描版 pdf

    性能优化是提高程序运行效率的关键。可以通过减少不必要的内存分配、利用缓存、避免重复计算等方式来优化代码。同时,合理地使用多线程技术可以进一步提升程序的并发性能。 #### 3. 调试与测试 调试是找出程序中...

    C++应用程序性能优化之内存池

    ### C++应用程序性能优化之内存池 #### 一、引言与重要性 在C++程序设计中,性能优化是一项至关重要的任务,特别是在资源管理和计算密集型应用中。内存管理,尤其是内存池技术,是提升C++应用程序效率的关键策略之...

    optimizing C++ C++优化-英文版

    在IT领域,尤其是在软件开发与优化的范畴内,C++作为一种高效、灵活...以上仅为《Optimizing C++ C++优化-英文版》中部分关键知识点的概述,深入学习和实践这些内容,将有助于开发者在实际项目中实现更高效的C++程序。

Global site tag (gtag.js) - Google Analytics