C++ std::string使用效率优化

        字符串操作是任何一个C++开发程序无法绕过的点,很多时候针对字符串的操作需要进行优化,从而达到更优的使用效率和内存利用率。一般会采用标准的std::string替代C字符串,一方面是std::string为一个成熟的类对象,其成员操作基本能满足绝大多数的使用场景,另一个方面是因为string在标准库中已经实现了自动的扩容操作(capacity),不用使用者再为内存空间的分配而做过多的操作,从而减少对内存管理带来的性能开销。

        针对string的使用,不同的使用方法会带来很大的性能差距。所以在日常的开发中,针对string使用方法的效率优化也是一个基础的问题。

1、传递方式优化

        由于string是一个类,在函数调用传递和获取string对象时,如果是查询string对象的某个元素或者一些信息,或者操作修改该string时,采用引用传递替代值传递会减少临时对象的分配,并且在同时操作多个较大的string时,也会减少瞬时的内存占用突增问题。

测试代码如下:

void valTransferStr(string str)
{return;
}void refTransferStr(string& str)
{return;
}void testTranferStr()
{string str("test");auto current_time1 = std::chrono::system_clock::now();auto timestamp1 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time1.time_since_epoch()).count();cout << "val begin timestamp: " << timestamp1 << endl;for (int i = 0; i < 10000000; ++i){valTransferStr(str);}auto current_time2 = std::chrono::system_clock::now();auto timestamp2 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time2.time_since_epoch()).count();cout << "val end timestamp: " << timestamp2 << endl;cout << "val transfer string cost time: " << timestamp2 - timestamp1 << endl;auto current_time3 = std::chrono::system_clock::now();auto timestamp3 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time3.time_since_epoch()).count();cout << "ref begin timestamp: " << timestamp3 << endl;for (int i = 0; i < 10000000; ++i){refTransferStr(str);}auto current_time4 = std::chrono::system_clock::now();auto timestamp4 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time4.time_since_epoch()).count();cout << "ref end timestamp: " << timestamp4 << endl;cout << "ref transfer string cost time: " << timestamp4 - timestamp3 << endl;
}

可以看到,单纯进行1000W次值传递耗时是引用耗时的52倍左右,所以采用能在使用引用传递的情况尽量采用引用传递的方式。

2、扩容分配优化

    针对string的扩容策略在另一篇文章中已经详细介绍过《std::string在 Windows MSVC和Linux Gcc 中capacity容量扩容策略的分析和对比》,这里就不再介绍。如果需要操作的string需要频繁的进行拼接操作,那么需要在代码工程定义该string的地方,预先调用reserver()进行容量的分配,避免频繁的capacity扩容,提高运行效率。

预分配方式耗时低于反复扩容方式。

测试代码如下:

void insertStr1()
{string str;for (int i = 0; i < 10000000; ++i){str += "1";}
}void insertStr2()
{string str;str.reserve(10000000);for (int i = 0; i < 10000000; ++i){str += "1";}
}void testInsertCostTime()
{auto current_time1 = std::chrono::system_clock::now();auto timestamp1 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time1.time_since_epoch()).count();cout << "origin insert begin timestamp: " << timestamp1 << endl;insertStr1();auto current_time2 = std::chrono::system_clock::now();auto timestamp2 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time2.time_since_epoch()).count();cout << "origin insert end timestamp: " << timestamp2 << endl;cout << "origin insert string cost time: " << timestamp2 - timestamp1 << endl;auto current_time3 = std::chrono::system_clock::now();auto timestamp3 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time3.time_since_epoch()).count();cout << "reserver insert begin timestamp: " << timestamp3 << endl;insertStr2();auto current_time4 = std::chrono::system_clock::now();auto timestamp4 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time4.time_since_epoch()).count();cout << "reserver insert end timestamp: " << timestamp4 << endl;cout << "reserver insert string cost time: " << timestamp4 - timestamp3 << endl;
}

3、字符拼接方式的调整

针对字符的拼接在另一篇文章中已经详细介绍过《std::string多个插入字符方式以及效率对比》。

4、遍历方式优化

        直接贴上迭代器遍历以及标准的for遍历的结果

void iterLoop(string &str)
{auto current_time1 = std::chrono::system_clock::now();auto timestamp1 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time1.time_since_epoch()).count();cout << "iter loop begin timestamp: " << timestamp1 << endl;for (auto it = str.begin(); it != str.end(); ++it){if (*it == 'a'){}}auto current_time2 = std::chrono::system_clock::now();auto timestamp2 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time2.time_since_epoch()).count();cout << "iter loop end timestamp: " << timestamp2 << endl;cout << "iter loop string cost time: " << timestamp2 - timestamp1 << endl;
}void indexLoop(string& str)
{auto current_time3 = std::chrono::system_clock::now();auto timestamp3 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time3.time_since_epoch()).count();cout << "index loop begin timestamp: " << timestamp3 << endl;auto len = str.size();for (int i = 0; i < str.size(); ++i){if (str[i] == 'a'){}}auto current_time4 = std::chrono::system_clock::now();auto timestamp4 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time4.time_since_epoch()).count();cout << "index loop end timestamp: " << timestamp4 << endl;cout << "index loop string cost time: " << timestamp4 - timestamp3 << endl;
}void testLoopStr()
{string str;for (int i = 0; i < 10000000; ++i){str.push_back('1');}iterLoop(str);indexLoop(str);
}

        

发现迭代器遍历执行时间大于标准for遍历,但是迭代器操作不会产生临时对象,这一点优于下标方式,所以要看实际使用场景

5、复合赋值操作避免创建临时对象

        在进行字符串拼接时,字符串的连接运算符+ 开销会很大,它会调用内存管理器构建一个临时的字符串对象来保存连接后的字符串,再将该字符串赋值给目的字符串。可以采用复合赋值操作符”xxx+=yyy”来替代”xxx= xxx + yyy”的方式

测试代码如下:

void testAddStr1(string &str)
{string res;auto len = str.size();auto current_time1 = std::chrono::system_clock::now();auto timestamp1 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time1.time_since_epoch()).count();cout << "=+ begin timestamp: " << timestamp1 << endl;for (int i = 0; i < len; ++i){res = res + str[i];}auto current_time2 = std::chrono::system_clock::now();auto timestamp2 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time2.time_since_epoch()).count();cout << "=+ end timestamp: " << timestamp2 << endl;cout << "=+ string cost time: " << timestamp2 - timestamp1 << endl;
}void testAddStr2(string& str)
{string res;auto len = str.size();auto current_time3 = std::chrono::system_clock::now();auto timestamp3 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time3.time_since_epoch()).count();cout << "+= begin timestamp: " << timestamp3 << endl;for (int i = 0; i < len; ++i){res += str[i];}auto current_time4 = std::chrono::system_clock::now();auto timestamp4 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time4.time_since_epoch()).count();cout << "+= end timestamp: " << timestamp4 << endl;cout << "+= string cost time: " << timestamp4 - timestamp3 << endl;
}void testAddStr()
{string str;for (int i = 0; i < 10000; ++i){str.push_back('1');}testAddStr1(str);testAddStr2(str);
}

6、减少频繁调用

举一个例子来说明当前优化点,比如size()成员函数的调用。字符串的遍历为一个常见操作,如果一个字符串较大,在for遍历时,如果每次都用当前的index与size()进行比较,则需要每次都调用size()获取字符串大小,调用次数为size次,如果用一个临时对象先记录size大小,在for遍历时就不需要进行每次的调用,减少时间开销。

测试代码如下:

void testCallStr1(string &str)
{string res;auto current_time1 = std::chrono::system_clock::now();auto timestamp1 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time1.time_since_epoch()).count();cout << "origin call begin timestamp: " << timestamp1 << endl;for (int i = 0; i < str.size(); ++i){}auto current_time2 = std::chrono::system_clock::now();auto timestamp2 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time2.time_since_epoch()).count();cout << "origin call end timestamp: " << timestamp2 << endl;cout << "origin call cost time: " << timestamp2 - timestamp1 << endl;
}void testCallStr2(string& str)
{string res;auto len = str.size();auto current_time3 = std::chrono::system_clock::now();auto timestamp3 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time3.time_since_epoch()).count();cout << "opt call begin timestamp: " << timestamp3 << endl;for (int i = 0; i < len; ++i){}auto current_time4 = std::chrono::system_clock::now();auto timestamp4 = std::chrono::duration_cast<std::chrono::milliseconds>(current_time4.time_since_epoch()).count();cout << "opt call end timestamp: " << timestamp4 << endl;cout << "opt call cost time: " << timestamp4 - timestamp3 << endl;
}void testStr()
{string str;for (int i = 0; i < 10000000; ++i){str.push_back('1');}testCallStr1(str);testCallStr2(str);
}

可以看到优化后的效率有所提高

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/579481.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是“私域流量”?

公域流量&#xff0c;字面意思就是人人都可以用的流量&#xff0c;但要付出一定成本&#xff0c;而且不一定可持续。 具体到实际对象&#xff0c;则是线下流量以及BAT等几个互联网流量大户&#xff0c;尤其是后者&#xff0c;比如微信、QQ、天猫、淘宝、百度、抖音、快手、微博…

vue-awesome-swiper轮播组件

安装版本&#xff1a;"swiper": "^6.0.0", 安装版本&#xff1a;"vue-awesome-swiper": "^4.1.1", <div class"swiper_conter"><swiper class"swiper" :options"swiperOption" ref"mySw…

算法leetcode|94. 二叉树的中序遍历(多语言实现)

文章目录 94. 二叉树的中序遍历&#xff1a;样例 1&#xff1a;样例 2&#xff1a;样例 3&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 94. 二叉树的中序遍历&#xff1a; …

Spring高手之路-Spring AOP

目录 什么是AOP Spring AOP有如下概念 补充&#xff1a; AOP是如何实现的 Spring AOP 是通过代理模式实现的。 Spring AOP默认使用标准的JDK动态代理进行AOP代理。 什么是AOP AOP(Aspect-Oriented Programming)&#xff0c;即面向切面编程&#xff0c;用人话说就是把公共的…

开发人工智能 需要什么工具

人工智能&#xff08;Artificial Intelligence, AI&#xff09;是指利用计算机模拟、扩展和延伸人类智能的理论、方法、技术和应用系统的一门学科。人工智能研究的目标是使计算机能够像人类一样具有智能&#xff0c;能够感知和理解环境、学习和推理、决策和规划&#xff0c;具备…

jar 运行清单文件MANIFEST.MF生成定义Main-Class Premain-Class IDEA maven-assembly-plugin

可运行jar文件中的启动清单文件 META-INF/MANIFEST.MF 内容自定义生成 清单文件中的 Main-Class: Premain-Class: Can-Retransform-Classes: 在maven-assembly-plugin插件中的生成配置如下, 注意命名 <archive> <manifest> <mainClass>c…

Dockerfile构建镜像

Dockerfile构建镜像 Dockerfile 是一个文本格式的配置文件&#xff0c; 用户可以使用 Dockerfile 来快速创建自定义的镜像&#xff0c;另外&#xff0c;使 用Dockerfile去构建镜像好比使用pom去构建maven项目一样&#xff0c;有异曲同工之妙 基本结构 Dockerfile 由一行行…

Intel® SGX Instruction References(五)

文章目录 前言一、Intel SGX Instruction Syntax and Operation1.1 ENCLS Register Usage Summary1.2 ENCLU Register Usage Summary1.3 ENCLV Register Usage Summary1.4 Information and Error Codes1.5 Internal CREGs1.6 Concurrent Operation Restrictions 二、Intel SGX …

亚马逊云科技 re:Invent 2023 产品体验:亚马逊云科技产品应用实践 王炸产品 Amazon Q,你的 AI 助手

意料之中 2023年9月25日&#xff0c;亚马逊宣布与 Anthropic 正式展开战略合作&#xff0c;结合双方在更安全的生成式 AI 领域的先进技术和专业知识&#xff0c;加速 Anthropic 未来基础模型的开发&#xff0c;并将其广泛提供给亚马逊云科技的客户使用。 亚马逊云科技开发者社…

TypeScript学习(基础篇)

前言 在现代的Web开发生态系统中&#xff0c;JavaScript已经成为一种必备的技术。然而&#xff0c;随着应用的增大&#xff0c;JavaScript的一些限制开始显现&#xff0c;例如缺乏静态类型检查和编译时错误检查。这正是TypeScript发挥作用的地方&#xff0c;TypeScript是一种静…

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

12.26ARM作业

三个按键中断&#xff0c;控制对应灯亮灭 main.c #include "key_it.h"void delay(int ms){int i,j;for(i0;i<ms;i){for(j0;j<2000;j);}}int main(){all_led_init();key1_it_config();key2_it_config();key3_it_config();while(1){printf("do main...\n&…

Linux c++开发-14-IO复用

什么是文件 程序员使用I/O最终都逃不过文件这个概念。 在Linux世界中文件是一个很简单的概念&#xff0c;作为程序员我们只需要将其理解为一个N byte的序列就可以了&#xff1a; b1, b2, b3, b4, … bN 实际上所有的I/O设备都被抽象为了文件这个概念&#xff0c;一切皆文件…

常用命令-设置

目录 系统配置查看系统架构屏幕演示工具合并终端命令Windows cmd命令提示符重启网卡禁止系统更新CMD运行powshell获取文件安装目录微软VC运行库合集强制刷新IP默认程序打开文件SSH免密登录关闭IE增强配置警告滑动关机最近操作记录解决谷歌翻译禁止系统休眠文件传输文件批量改名…

基于Java (spring-boot)的宠物管理系统

一、项目介绍 1、用户端功能&#xff1a; 首页&#xff1a;展示公告列表&#xff0c;宠物科普&#xff0c;介绍流浪宠物&#xff0c;热门活动。 宠物领养&#xff1a;用户搜索想要领养宠物&#xff0c;申请领养&#xff0c;查看自己领养的宠物。 宠物救助&#xff1a;用户能…

C# .Net学习笔记—— 加密和解密算法

一、四种加密方式 1、MD5不可逆加密 2、Des对称可逆加密 3、RSA非对称可逆加密 4、数字证书SSL 二、详解 1、MD5加密 public class MD5Encrypt{public static string Encrypt(string source, int length 32){if (string.IsNullOrEmpty(source)) return string.Empty;HashA…

002、使用 Cargo 创建新项目,打印 Hello World

1. Cargo 简介 Cargo 是 Rust 的构建系统和包管理工具&#xff0c;比如构建代码、下载依赖的库、构建这些库等等。在安装 Rust 时&#xff0c;Cargo也会一起安装。 2. 创建新项目的具体步骤 步骤1&#xff1a; 我们在桌面新建一个文件夹&#xff0c;用于存放后面练习用的代码文…

免费好用的10款AI配音软件,总有一款适合你

随着人工智能技术的快速进步&#xff0c;越来越多的语音合成软件开始在我们的日常生活中应用。这些软件能够将文字转化为自然流畅的语音&#xff0c;并且能够模拟各种声音和语调&#xff0c;给人们带来了巨大的便利和创造性。在这其中&#xff0c;配音软件尤其受到广泛欢迎&…

微信小程序备案流程整理

一、备案流程 [找备案入口]–[填主体信息]–[填小程序信息]–[初审]–[短信核验]–[通管局审核] 1&#xff0c;在小程序后台找到备案入口 &#xff08;1&#xff09;新的未上架小程序&#xff0c;可以在小程序首页点击【去备案】进入。 &#xff08;2&#xff09;已上架小程…

工具系列:TimeGPT_(3)处理假期和特殊日期

日历变量和特殊日期是预测应用中最常见的外生变量类型之一。它们为时间序列的当前状态提供了额外的上下文信息&#xff0c;特别是对于基于窗口的模型&#xff08;如TimeGPT-1&#xff09;而言。这些变量通常包括添加每个观测的月份、周数、日期或小时数的信息。例如&#xff0c…