总结一次C++ 程序优化历程

近期用到了一位师兄写的C++程序,总体功能良好。使用不同的数据测试,发现了一个明显的缺点:大数据量下,预处理过程耗时很长。中科院的某计算集群,普通队列中的程序运行时间不能超过6个小时。而手上这套程序,大数据量下预处理就花了不止六个小时,结果当然是还没开始就被结束了。

和天河二号的工作人员联系,确认没有执行时间限制。于是开通了天河二号的账号,把程序扔上去跑。执行大数据量时,程序莫名被kill。询问技术支持,得知是内存耗尽,建议每个节点的进程数少一点。如此折腾了两次,大数据量的例子没跑通,大部分时间都费在预处理上,然后程序崩了,又要调整参数重新再来。

耗时长,最多是多花点机时,问题不大。但是没跑通的情况下每次要等五六个小时,然后才知道能否运行,测试然后反馈的过程太低效。忍无可忍,就开始进行优化吧!

第一步,找出耗时的点。原来的程序输出日志用的cout,没有附带时间,不能通过日志发现耗时的点。为了找出性能关键点,第一步是改进log,在输出中加上时间。写了一个Log类,替换掉cout,程序的输出中就带上时间了:

#include "../include/Log.hpp"

#include
#include
#include
#include 

using namespace std;

namespace tlanyan {

 string Log::datetimeFormat = "%F %T";

 Log::Log()
 {
 }

 void Log::info(const char* message) {
  cout << getCurrentTime() << " [info] " << message << endl;
 }

 void Log::debug(const char* message) {
#if DEBUG
  cout << getCurrentTime() << " [debug] " << message;
#endif;
 }

 const char* Log::getCurrentTime()
 {
  //locale::global(locale("zh_CN.utf8"));
  time_t t = time(NULL);
  char mbstr[512];
  if (strftime(mbstr, sizeof(mbstr), Log::datetimeFormat.c_str(), localtime(&t))) {
   return mbstr;
  }

  cerr << "获取或格式化时间错误!" << endl;

  exit(1);
 }

 Log::~Log()
 {
 }
}

// 调用示例:
Log::info("program begins...");

通过查看Log,定位到了耗时长的过程。

  • 第一步,目测程序源代码,找出问题所在。该段代码比较好理解,主要是进行数据初始化和打标签。程序中规中矩,都是操控内存中的数组,没有磁盘、网络、进程通信等耗时调用。审查代码中发现第一个问题:内存重分配。程序声明了vector,没有指定大小,后续代码中使用push_back对数组的每一项进行赋值。内存分配和数据拷贝的代价是很大的,这应该是一个性能点。修改代码,声明时指定数组大小。编译并运行程序,结果表明省下了30%的耗时。
  • 第二步,统计代码的工作量。耗时过程的初始化数据量,大概是整个数据量的10%,就算其中内嵌了两层循环,也不应该耗时如此多。为了查看是否有额外工作量,加入了计数器。运行结果显示,该段函数的计算量不大,耗时长应该有其他的原因。
  • 第三步,根据经验判断是缓存失效导致。第一反应是用valgrind查看缓存命中,但valgrind模拟运行的效率太差,几个小时后kill掉放弃了。目测程序源码,发现很多数据都是从全局内存读取,没有充分利用缓存。修改代码,使用局部变量缓存全局数据,接下来代码中的数据使用缓存数据。经过测试,效果非常明显,提升了50%的效率。
  • 第四步,查找其他性能热点。经过几次小的调优测试,发现一些全局内存访问不可避免(随机访问,无法利用缓存),按照目前的方式难以继续优化。要大幅降低耗时需要重写算法,目前无法保证对算法和程序意图十分了解,遂暂时作罢。

优化前后的结果对比:中等数据规模下,耗时从8'43"降到3'25";大数据量下,耗时从4h38'44"降到1h49'21"(注:使用自己的机器测试,CPU主频3.46GHz,比中科院和天河二号集群的CPU主频都要高,所以耗时短)。从数据看出,效果还是很明显的。

以上就是C++ 程序优化历程总结的详细内容,更多关于C++ 程序优化的资料请关注我们其它相关文章!

(0)

相关推荐

  • 总结c++性能优化策略

    1 关于继承:不可否认良好的抽象设计可以让程序更清晰,代码更看起来更好,但是她也是有损失的,在继承体系中子类的创建会调用父类的构造函数,销毁时会调用父类的析构函数,这种消耗会随着继承的深度直线上升,所以不要过度的抽象和继承. 2 对象的复合:对象的复合和继承很相似,当一个对象包含其他对象构造时也会引起额外的构造.关于这点可能会有很多人不解,认为这是不可避免的,举个例子,你的一个对象中用到数组和字符串,你是选择string和vector还是char* 和c系的数组呢,如果没有用到c++stl库提供

  • 详解C++中StringBuilder类的实现及其性能优化

    介绍 经常出现客户端打电话抱怨说:你们的程序慢如蜗牛.你开始检查可能的疑点:文件IO,数据库访问速度,甚至查看web服务. 但是这些可能的疑点都很正常,一点问题都没有. 你使用最顺手的性能分析工具分析,发现瓶颈在于一个小函数,这个函数的作用是将一个长的字符串链表写到一文件中. 你对这个函数做了如下优化:将所有的小字符串连接成一个长的字符串,执行一次文件写入操作,避免成千上万次的小字符串写文件操作. 这个优化只做对了一半. 你先测试大字符串写文件的速度,发现快如闪电.然后你再测试所有字符串拼接的速

  • C++开发的Redis数据导入工具优化

    背景 使用C++开发了一个Redis数据导入工具 从oracle中将所有表数据导入到redis中: 不是单纯的数据导入,每条oracle中的原有记录,需要经过业务逻辑处理, 并添加索引(redis集合): 工具完成后,性能是个瓶颈: 优化效果 使用了2个样本数据测试: 样本数据a表8763 条记录: b表940279 条记录: 优化前,a表耗时11.417s: 优化后,a表耗时1.883s: 用到的工具 gprof, pstrace,time 使用time工具查看每次执行的耗时,分别包含用户时间

  • C/C++ 编译器优化介绍

    0. gcc -o gcc -o 的优化仍然是机械的,想当然的.只有做到深入理解计算机系统,加深对编程语言的理解,才能写出最优化的代码. Linux下gcc 优化级别的介绍  · gcc -o0 ⇒ 不提供任何优化:  · gcc -o1 ⇒ 最基本的优化,主要对代码的分支.表达式.常量等进行优化,编译器会在较短的时间下将代码变得更加短小,这样体积就会变得更小,会减少内存的占用率,在操作系统进行内存调度时就会更快.          · 但是事情没有绝对的优点,当一个庞大的程序被拆碎细分的话,内

  • C++快速排序的分析与优化详解

    相信学过数据结构与算法的朋友对于快速排序应该并不陌生,本文就以实例讲述了C++快速排序的分析与优化,对于C++算法的设计有很好的借鉴价值.具体分析如下: 一.快速排序的介绍 快速排序是一种排序算法,对包含n个数的输入数组,最坏的情况运行时间为Θ(n2)[Θ 读作theta].虽然这个最坏情况的运行时间比较差,但快速排序通常是用于排序的最佳的实用选择.这是因为其平均情况下的性能相当好:期望的运行时间为 Θ(nlgn),且Θ(nlgn)记号中隐含的常数因子很小.另外,它还能够进行就地排序,在虚拟内存

  • 详解C++ 临时量与临时对象及程序的相关优化

    一.临时量与临时对象 临时量: 内置类型生成的临时量是常量(临时量,寄存器带出来). 自定义类型生成的临时量是变量 ,在内存中. 隐式生成生成的临时量是常量 ,显式生成生成的临时量是变量 . 临时对象: 临时对象是系统临时分配的对象,在没主动声明所需对象而又使用其功能时产生的 显示对象:出现类型名 隐式对象:不出现类型名 注意: 临时对象的生存周期只在本条语句,临时对象一旦被引用,它的生存周期就和引用相同. 对象如何生成? 先分配内存 在调用构造函数初始化对象的成员变量  产生对象对象析构了 对

  • 基于C++ Lambda表达式的程序优化

    什么是Lambda? C++ 11加入了一个非常重要的特性--Lambda表达式.营里(戴维营)的兄弟都对Objective-C很熟悉,许多人多block情有独钟,将各种回调函数.代理通通都用它来实现.甚至有人选择用FBKVOController.BlocksKit等开源框架将KVO.控件事件处理都改为通过block解决.原因就是简单.方便.直观,函数的定义和使用出现在同一个地方.这里的Lambda表达式实际上和block非常类似,当然如果你用它和Swift语言的闭包比较,那就是一回事了. 这是

  • c++并查集优化(基于size和rank)

    基于size的优化是指:当我们在指定由谁连接谁的时候,size数组维护的是当前集合中元素的个数,让数据少的指向数据多的集合中 基于rank的优化是指:当我们在指定由谁连接谁的时候,rank数组维护的是当前集合中树的高度,让高度低的集合指向高度高的集合 运行时间是差不多的: 基于size的代码: UnionFind3.h #ifndef UNION_FIND3_H_ #define UNION_FIND3_H_ #include<iostream> #include<cassert>

  • VS2010 C++ 配置优化方案

    个人感觉VC6.0太土了,而且有很多bug存在,且微软早就不对其更新.所以,在选择C++编程的时候.使用IDE,VC6.0一段时间以后,我毅然决然的放弃了,觉得还是使用VS2010比较有前途. 但是当使用VS2010的时候,发现了不少问题,用了不少办法终于将问题挨个解决,所谓"工欲善其事,必先利其器".想要快速的开发C++软件,绝对离不开一款好用的IDE.如下为使用中需要的问题,已经解决方案. 1.任意创建一个新的对话框工程,整个解决方案文件夹大小达到了100M,垃圾太多了. 解决方案

  • 总结一次C++ 程序优化历程

    近期用到了一位师兄写的C++程序,总体功能良好.使用不同的数据测试,发现了一个明显的缺点:大数据量下,预处理过程耗时很长.中科院的某计算集群,普通队列中的程序运行时间不能超过6个小时.而手上这套程序,大数据量下预处理就花了不止六个小时,结果当然是还没开始就被结束了. 和天河二号的工作人员联系,确认没有执行时间限制.于是开通了天河二号的账号,把程序扔上去跑.执行大数据量时,程序莫名被kill.询问技术支持,得知是内存耗尽,建议每个节点的进程数少一点.如此折腾了两次,大数据量的例子没跑通,大部分时间

  • Oracle数据库及应用程序优化开发者网络Oracle

    正在看的ORACLE教程是:Oracle数据库及应用程序优化开发者网络Oracle.介绍:细处着手,巧处用功.高手和菜鸟之间的差别就是:高手什么都知道,菜鸟知道一些.电脑小技巧收集最新奇招高招,让你轻松踏上高手之路. 摘 要:本文对ORACLE数据库及ORACLE应用程序的优化,进行了全面的分析与研究,并提出了自己的一些建议. 关 键 词:ORACLE,优化,数据库,SQL 1.引言 随着信息化时代的到来,人们开始广泛地使用数据库技术对大量而复杂的信息进行科学高效的管理.在数据库领域中的各种应用

  • Asp.net程序优化js、css实现合并与压缩的方法

    本文实例讲述了Asp.net程序优化js.css实现合并与压缩的方法.分享给大家供大家参考.具体实现方法如下: 访问时将js和css压缩并且缓存在客户端, 采用的是Yahoo.Yui.Compressor组件来完成的,用户可以点击此处本站下载. 创建一个IHttpHandler来处理文件 复制代码 代码如下: public class CombineFiles : IHttpHandler {         private const string CacheKeyFormat = "_Cac

  • 关于小程序优化的一些建议(小结)

    setData setData 是小程序开发中使用最频繁的接口,也是最容易引发性能问题的接口.在介绍常见的错误用法前,先简单介绍一下 setData 背后的工作原理. 工作原理 小程序的视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境.在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道.当前,视图层和逻辑层的数据传输,实际上通过两边提供的evaluateJavascript 所实现.即

  • asp.net程序优化 尽量减少数据库连接操作

    项目以我自己的设计编码完成,并整合测试.初始化数据时,问题出现了.刚开始体现在客户端接受数据很慢.测试环境环境下,数据库服务器部署在国外,网站部署在公司内部,而且我一直认为我的程序在数据库数据处理这里已经做了足够的优化,包括索引和主键已经做到了合理使用.综上所述,起初的速度问题一直没有引起我的关注. 然而最后问题的关键恰恰出在数据库连接查询方面,频繁查询导致数据初始化速度很慢.刚开始我采取的方法是即用即查:需要数据的时候就从数据库查,有比较多的单表查询返回单个字段的情况.假如我有大概3000条左

  • asp.net 程序优化精选第1/2页

    1.数据库访问性能优化 数据库的连接和关闭        访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源.ASP.NET中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响.系统将用户的数据库连接放在连接池中,需要时取出,关闭时收回连接,等待下一次的连接请求.连接池的大小是有限的,如果在连接池达到最大限度后仍要求创建连接,必然大大影响性能.因此,在建立数据库连接后只有在真正需要操作时才打开连接

  • ASP.NET 程序优化 小结

    一.SqlDataRead和Dataset的选择 Sqldataread优点:读取数据非常快.如果对返回的数据不需做大量处理的情况下,建议使用SqlDataReader,其性能要比datset好很多.缺点:直到数据读完才可close掉于数据库的连接 (SqlDataReader 读数据是快速向前的.SqlDataReader 类提供了一种读取从 SQL Server 数据库检索的只进数据流的方法.它使用 SQL Server 的本机网络数据传输格式从数据库连接直接读取数据.DataReader需

  • 关于C#程序优化的五十种方法

    一.用属性代替可访问的字段 1..NET数据绑定只支持数据绑定,使用属性可以获得数据绑定的好处: 2.在属性的get和set访问器重可使用lock添加多线程的支持. 二.readonly(运行时常量)和const(编译时常量) 1.const只可用于基元类型.枚举.字符串,而readonly则可以是任何的类型: 2.const在编译时将替换成具体的常量,这样如果在引用中同时使用了const和readonly两种值,则对readonly的再次改变将会改变设计的初衷,这是需要重新编译所更改的程序集,

  • JSP/Servlet应用程序优化八法

    你的J2EE应用是不是运行的很慢?它们能不能承受住不断上升的访问量?本文讲述了开发高性能.高弹性的JSP页面和Servlet的性能优化技术.其意思是建立尽可能快的并能适应数量增长的用户及其请求.在本文中,我将带领你学习已经实践和得到证实的性能调整技术,它将大大地提高你的servlet和jsp页面的性能,进而提升J2EE的性能.这些技术的部分用于开发阶段,例如,设计和编码阶段.另一部分技术则与配置相关. 技术1:在HttpServletinit()方法中缓存数据 服务器会在创建servlet实例之

随机推荐