直到最近程序性能优化时,才需要彻底明白其中的逻辑和情况,学习贵在坚持, 需要持之以恒,不仅仅为工作,更为自己后续的职业发展打下鉴定的基础,加油,小伙伴们,也是说给自己。
说说背景和情况,目前客户反馈数据提取比较慢,排查各种情况,之后通过日志等,定位到map的查询接口,但是其中查询接口仅仅做查询操作,比较耗时,且会多次查询,进而导致,数据量比较大。
简单复习和学习一下STL的map知识点:
map是STL的一种关联容器,存储key和value值,其内部为一个红黑树实现(这点上,可以看见数据结构的重要性),其内部所有数据是有序的,默认顺序为采用小于号做为排序,如果需要自定义排序排序算法(重装之前less算法即可),自身实现即可,这点可能在具体项目是有需求存在,所以需要掌握。 一般项目中使用主要如下:模拟一个完整的项目中的使用。
#include<map>
using namespace std:
struct CData
{
int m_nID;
std::string m_strName;
}
typedef std::map<int, CData*> MAP_DATA; /// 建议这样书写,养成良好的编码风格
MAP_DATA m_mapData;
CData* pData = new CData;
m_mapData.insert(std::make_pair(pData->m_nID, pData)); ///平时自己喜欢这个插入方式;(键值一样,则后者插入失败)
注意如下插入方式:
m_mapData[1] = pData; /// 这种方式很直观,对c风格很熟悉,但是隐含性能问题,因为插入时,先查找主键1,如果没有则创建一个新的对象CData,键值为1,插完之后,在将pData复制给之前生成的对象。 次时,特别是类对象,会比较大的开销;
所以推荐如上insert方式插入。
类似的 CData* pData = m_mapData[1] 也一样,只有map中存在键值时才正确,否则会自动插入实例,为初始化值。
map的查找应该是主要应用的接口,如下:
CData* GetDataByKey(int nID)
{
MAP_DATA::iterator iter = m_mapData.find(nID);
if((iter != m_mapData.end()))
{
return iter->second;
}
return NULL;
}
言归正传, 接着性能分析继续,排查之后,性能瓶颈主要在于map的查找,然后查找相关资料, 看看有没有替代的,有几篇文档将很不错, 如http://blog.csdn.net/blues1021/article/details/45054159
最后通过利用unorder_map替换现有的map, 性能提升不少, 但是还是不理想,最后继续跟源码,发现在查找时,先在底层复制一份临时string 之后在进程查找操作,这样就产生性能损耗, 将key改为LPCTSTRC即可。