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…

二、C#基础语法( 变量与数据类型)

C# 是一种强类型的编程语言,这意味着在声明变量时必须指定其数据类型。数据类型是用于定义变量可以持有的数据类型的系统。C# 支持多种内置数据类型,同时也支持用户自定义的数据类型。 变量 变量是用来存储数据的容器。在 C# 中,每个变量都有一个特定的数据类型,这决定了…

算法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…

MySQL中的like模糊查询

like与通配符搭配使用进行模糊查询 通配符 % 表示任意多个字符 _ 表示任意单个字符 #用户名中包含a的 select * from user where name like %a% #用户名中第二个字符为a的 select * from user where name like _a% #用户名中第二个字符为_的(需要使用转义字符) select * from u…

强烈推荐,提高开发效率的IDEA插件

自动生成JVM诊断工具arthas的命令 arthas idea (2.36) 基于IntelliJ IDEA开发的Alibaba Arthas 命令生成插件&#xff0c;支持Alibaba Arthas 官方常用的命令。 官方的命令太复杂&#xff0c;此插件为了简化使用者的难度&#xff0c;提供了一些常用命令的支持 该插件支持Aliba…

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 …

Nodejs 使用 fs-extra 模块进行目录和文件操作

在Node.js中&#xff0c;fs-extra模块是fs模块的扩展&#xff0c;提供了一些额外的功能&#xff0c;使文件和目录的操作更加便捷。那么我们如何使用 fs-extra 模块进行目录和文件操作呢&#xff1f;以下是一些常见的用法示例&#xff1a; 1. ensureDir/ensureDirSync - 创建目…

WuBit:聚合BRC20资产交易,续写铭文市场新浪潮

2023年的数字货币领域见证了重大的创新&#xff0c;尤其是比特币Ordinals协议的推出&#xff0c;它为铭文赛道带来了新的生命力。这一协议的核心在于将比特币分割为更小的单位——聪&#xff0c;并在每个聪上嵌入独特信息&#xff0c;从而创建了一种新颖的非同质化资产。BRC20等…

亚马逊云科技 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是一种静…

GO语言基础笔记(四):并发编程基础

目录 Goroutines 通道&#xff08;Channel&#xff09; 代码示例 Goroutines 定义与特点&#xff1a; Goroutines是Go语言中实现并发的基本单位。它比传统的线程更轻量级&#xff0c;拥有更小的内存占用和更快的启动时间。在Go程序中&#xff0c;您可以轻松地启动成千上万的G…

Redis基本介绍和使用场景

1、什么是Redis Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景&#xff0c;比如 String(字符串)、…

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

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

vue跑马灯

vue跑马灯 组件propscolOption 使用elementui的布局组件实现列宽度 <template><div class"lamp-container"><div class"lamp-header"><el-row :gutter"10"><el-col v-for"col in column" :key"col.pr…

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&…