总结一次C++ 程序优化历程
作者:tlanyan 发布时间:2023-11-02 22:38:30
近期用到了一位师兄写的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主频都要高,所以耗时短)。从数据看出,效果还是很明显的。
来源:https://tlanyan.me/cpp-program-optimization/
猜你喜欢
- Spring Boot+Vue 前后端分离项目架构项目流程:1. SpringBoot 后端项目1、新建一个 SpringBoot 工程,并
- 最近有朋友问屏幕锁定的问题,自己也在学习,网上找了下也没太详细的例子,看的资料书上也没有有关屏幕锁定程序的介绍,下个小决心,自己照着官方文档
- 本文实例讲述了Android播放assets文件里视频文件相关问题。分享给大家供大家参考,具体如下:今天做了一个功能,就是播放项目工程里面的
- 最近开发项目中,有个在屏幕上任意拖动的悬浮窗功能,其实就是利用 WindowManager的api来完成这个需求,具体的实现的功能如下:1.
- 前言日志处理是每个项目当中一个非常重要的内容。没有了日志,也就失去了对系统的可控性。没有日志,系统出现任何问题,都会没有踪迹可寻,这对一个信
- 0. Grinder – Grinder是一个开源的JVM负载测试框架,它通过很多负载注射器来为分布式测试提
- 本文实例为大家分享了java实现饮料自助售货机的具体代码,供大家参考,具体内容如下①用户类import java.util.Scanner;
- 属性问题引入前面我们已经在父工程中的dependencyManagement标签中对项目中所使用的jar包版本进行了统一的管理,但是如果在标
- 简单概述在日常生活中,我们经常会突发一些奇思妙想,或是一个画面,或是几个符号。这时候无法使用拍照或者打字功能实现,想拿笔记下又身边找不到笔。
- 实例如下:private bool creatExcel(string xlsfile) { &nb
- " 梧桐更兼细雨,到黄昏、点点滴滴。"C语言朱武大战数据结构专栏C语言植物大战数据结构快速排序图文示例C语言植物大战数据
- 前言什么是mybatis二级缓存?二级缓存是多个sqlsession共享的,其作用域是mapper的同一个namespace。即,在不同的s
- 序列化与反序列化是.net程序设计中常见的应用,本文即以实例展示了.net实现序列化与反序列化的方法。具体如下:一般来说,.net中的序列化
- 前言在Android开发中,我们有时需要实现类似IOS的对话框。今天我就来总结下,如何通过自定义的开发来实现类似的功能。自定义Dialog我
- 本文主要为大家分享了MyEclipse中安装使用Maven的方法,供大家参考,具体内容如下Step1:下载mavenhttp://maven
- 前言最近使用QT中的QTextEdit控件,作为实时数据显示的UI,在一次写入超过多少k的时候循环写入则会卡顿,网上也没有什么好的解决方案,
- 本文实例展示C#实现过滤html标签,汉字间空格,制表符,并保留a标签的方法。分享给大家供大家参考之用。具体方法如下:可以在公共类如Comm
- 请停止代码注释“干净的代码应该像写好的散文一样” - Robert C. Martin不良代码的通病就是有很多注释。这是凌乱的源代码最明显的
- Android onClick 与 setOnClickListener区别为Android Widgets添加点击事件处理函数又
- MyEclipse配置IDEA配置Tomcat环境IDEA:2020.2Tomcat:apache-tomcat-9.0.38创建Web项目