C性能优化系列专题1优化string的使用(上)
0、前言
std::string 是 c++ 中经常使用的数据结构,然而并不是每个人都能高效地使用它。本文将以一个例子带你一步步去优化 std::string 的使用。1、std::string 的特点
字符串是动态分配的。任何会使字符串变长的操作,如在字符串后面再添加一个字符或字符串,都可能会使字符串的长度超出它内部的缓冲区大小。当发生这种情况时,操作会从内存管理器中获取一块新的缓冲区,并将字符串复制到新的缓冲区中。类似 std::vector.
字符串就是值。在赋值语句和表达式中,字符串的行为与值是一样的。如果你使用 s1 = s2 + s3 + s4 这条语句连接字符串,那么 s2 + s3 的结果会被保存在一个新分配的临时字符串中,连接 s4 后的结果会被保存在另一个临时字符串中。这个值将会取代 s1 之前的值。接着,为第一个临时字符串和 s1 之前的值动态分配的内存将会被释放。这会导致多次调用内存管理器。2、使用 std::string 的例子#include #include using namespace std::chrono; class TimerBase { public: // 初始化定时器 TimerBase() : m_start(system_clock::time_point::min()) {} // 清除计时器 void Clear() { m_start = system_clock::time_point::min(); } // 如果计时器正在计时,则返回true bool IsStarted() const { return (m_start.time_since_epoch() != system_clock::duration(0)); } // 启动计时器 void Start() { m_start = system_clock::now(); } // 得到自计时开始后的毫秒值 unsigned long PrintElapsedMs() { if (IsStarted()) { system_clock::duration diff; diff = system_clock::now() - m_start; std::cout << (unsigned)(duration_cast(diff).count()); } return 0; } private: system_clock::time_point m_start; }; std::string remove_ctrl(std::string s) { std::string result; // result.reserve(s.length()); for (int i=0; i= 0x20) { result = result + s[i]; // result += s[i]; } } return result; } int main() { TimerBase tb; tb.Start(); std::string test_str = ""; for(size_t i = 0; i < 100000; i++) { test_str += "a"; } remove_ctrl(test_str); tb.PrintElapsedMs(); return 0; }
上述代码中,实现了一个计时的类 TimerBase,然后自定义了一个函数 remove_ctrl,测试发现,该函数调用 100000 次,共耗时 486 ms.2.1、第一次优化:使用复合赋值操作避免临时字符串result += s[i]
测试发现:耗时 3 ms ,性能提升 100 倍之多。2.2、第二次优化:通过预留存储空间减少内存的重新分配std::string remove_ctrl(std::string s) { std::string result; result.reserve(s.length()); for (int i=0; i= 0x20) { // result = result + s[i]; result += s[i]; } } return result; }
测试发现:耗时 1 ms 2.3、第三次优化:消除对参数字符串的复制std::string remove_ctrl(std::string const& s) { std::string result; result.reserve(s.length()); for (int i=0; i= 0x20) { // result = result + s[i]; result += s[i]; } } return result; }
测试发现:耗时 2.5 ms 。what?性能还下降了,到底是为什么呢?虽然省了一次内存分配,但是,程序解引用指针带来了额外开销。2.4、第四次优化:使用迭代器消除指针解引std::string remove_ctrl(std::string const& s) { std::string result; result.reserve(s.length()); for (auto it = s.begin(), end = s.end(); it != end; ++it) { if (*it > 0x20) { result += *it; } } return result; }
测试发现:耗时 2 ms ,使用迭代器,节省了解引用操作,的确带来了性能提升。2.5、第五次优化:用字符数组代替字符串char* remove_ctrl(char* dst, char const* src, size_t size) { for (size_t i = 0; i < size; i++) { if(src[i] > 0x20) { *dst++ = src[i]; } *dst = 0; } return dst; }
测试发现:耗时 0.5 ms ,效率惊人!获得这种改善效果的原因之一是移除了若干函数调用以及改善了缓存局部性。3、总结
第二章使用了 5 种小的优化手段,让我们明白了哪种优化手段最有效。在下一篇文章中,我们将继续优化,主要手段有:使用更好的算法、使用更好的编译器、使用更好的字符串库、使用更好的内存分配器。
别老说路虎质量差,这台路虎销量一直就没下来过在豪华SUV市场中,大家一定会想起路虎这个品牌,而今天给大家介绍的这款路虎揽胜运动版,就是集豪华感与运动感于一身的中大型SUV,这款车外观大气,并且深受消费者的喜爱,下面就让我们一
哪都不错的一款中型轿车,就是卖不好,只因对手太强说到30万40万元之间的轿车能买到谁?BBA的C级3系和A4L没问题,沃尔沃S90也可以,优惠完凯迪拉克CT6也应该没差不多。说了这么多车,谁又能想到DS9PHEV这款车呢?的确,
10万块钱最靠谱的合资车都在这里,居家过日子的最佳选择今天有人私信我,让我给他推荐一款10万左右的高品质合资汽车,我盘算了一下,这几款都是不错的选择,只要正常保养,想开坏还是非常难的!本田飞度官方指导价7。3810。28万元优惠后价格
有一种选择叫别人家的职业发展通道近年来,越来越多的企业关注员工职业发展,希望通过为企业内部各类人才建立良好的职业发展通道,更好的激励员工,促进其发挥主动性和创造性,从而实现提升人力资源管理的目标,帮助企业获得未来
玩转跨界联名,飞利浦JT60真无线耳机体验报告跨界,已经成为现代艺术中的一种时尚。前不久,知名影音品牌飞利浦就和丹麦奢侈品银饰品牌GeorgJense联名推出了一系列高端产品,其中有一款别具特色的真无线耳机迅速吸引了笔者的眼球
迈巴赫GLS480来穿车衣,气场全开才在车展上看到,今天全新迈巴赫GLS480,新车就到店了,小伙伴一声惊呼,气质真是太震场了,同是顶级豪车,迈巴赫丝毫没有壕气冲天,天生就贵气十足,这款全新上市,听说需要加价百万,怪
iPhone12和小米MIX4哪个好,理性分析,看具体需求这问题问得好,这问题问得我措手不及,iPhone12和小米MIX4哪个好,我认为从不同方面去看待吧。如果你是习惯IOS系统,那前者好呗,反之后者好。当然,除了明显的系统不同,两者还
TWS耳机同质化下如何挑选?还原需求,百元便能享受沉浸式立体声自从苹果手机取消3。5mm耳机孔,无线蓝牙(TWS)耳机便占领了绝大部分的市场份额,如今在公共场所还戴着有线的耳机,难免会显得有些另类。有一说一,AirPods确实是一款不错的TW
数据库事务MVCC架构数据库事务MVCC架构MultiVersionConcurrencyControl多版本并发控制,MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问在编程
python3连接数据库python3连接数据库版本python3。8安装连接包windows环境环境windows10httpsblog。csdn。netweixin43570254articledet
掌机玩家来看看,你的手柄充电伴侣,看谷粒随心充是如何然你随心前言来一个灵魂拷问你每天需要充电的数码产品有多少?3的请评论留言1,否则回2。而耀哥,则有手机手表蓝牙耳机Switch及其手柄等等,给各种装备进行充电回血几乎成为了每天的睡前作业。