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

news2025/2/4 7:51:15

        字符串操作是任何一个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.coloradmin.cn/o/1337425.html

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

相关文章

std::string在 Windows MSVC和Linux Gcc 中capacity容量扩容策略的分析和对比

1、capacity()作用 在std::string中&#xff0c;capacity()为当前string占用内存字符的长度&#xff0c;表示当前string的容量&#xff0c;可以理解为一个预分配制度&#xff0c;如果当前的string不断进行扩展操作&#xff0c;则不需要每次都进行内存上的分配&#xff0c;提高程…

CentOS7搭建Elasticsearch与Kibana服务

1.部署单点es 1.1.创建网络 因为我们还需要部署kibana容器&#xff0c;因此需要让es和kibana容器互联。这里先创建一个网络&#xff1a; docker network create es-net 1.2拉取elasticsearch镜像 docker pull elasticsearch:7.11.1 1.3.运行 运行docker命令&#xff0c;部…

NLP论文阅读记录 - 以大语言模型为参考学习总结

文章目录 前言0、论文摘要一、Introduction1.1目标问题1.2相关的尝试1.3本文贡献 二.相关工作2.1文本生成模型的训练方法2.2 基于LLM的自动评估2.3 LLM 蒸馏和基于 LLM 的数据增强 三.本文方法3.1 Summarize as Large Language Models3.1.1 前提3.1.2 大型语言模型作为参考具有…

面向对象设计与分析40讲(15)简单工厂方法模式

文章目录 定义示例优缺点定义 简单工厂模式是一种创建型模式,用于根据客户端的需求创建对象实例,所谓的需求反映到编程语言里就是传入的参数。 简单工厂模式包括三个主要部分: 工厂类(Simple Factory):这是整个模式的核心。它负责根据客户端的请求来创建并返回相应的对…

罗门哈斯同级抛光树脂:单晶硅电池超纯水生产与应用

在光伏行业中&#xff0c;单晶硅电池是最高效和最可靠的太阳能电池之一。然而&#xff0c;生产单晶硅电池需要使用超纯水&#xff0c;这是许多光伏制造商所面临的一个挑战。那么&#xff0c;超纯水是如何生产的呢&#xff1f; 为什么需要超纯水&#xff1f; 超纯水是一种高纯…

vue3 setup + ts 项目模块找不到问题解决:Cannot find module ...

Vscode contrl shift p 打开vscode配置面板&#xff0c;查找typescript&#xff1b;是使用workspace version,完事儿

EternalBlue【永恒之蓝】漏洞详解(复现、演示、远程、后门、入侵、防御)内容丰富-深入剖析漏洞原理-漏洞成因-以及报错解决方法-值得收藏!

漏洞背景&#xff1a; 1.何为永恒之蓝&#xff1f; 永恒之蓝&#xff08;Eternal Blue&#xff09;爆发于2017年4月14日晚&#xff0c;是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限&#xff0c;以此来控制被入侵的计算机。甚至于2017年5月12日&#xff0c; 不法分子…

JavaScript基础练习题解析与实践

如果大家感感兴趣也可以去看&#xff1a; &#x1f389;博客主页&#xff1a;阿猫的故乡 &#x1f389;系列专栏&#xff1a;JavaScript专题栏 &#x1f389;ajax专栏&#xff1a;ajax知识点 &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 学习…

HarmonyOS4.0系统性深入开发04UIAbility组件详解(下)

UIAbility组件间交互&#xff08;设备内&#xff09; UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时&#xff0c;会涉及到启动特定的UIAbility&#xff0c;该UIAbility可以是应用内的其他UIAbility&#xff0c;也可以是其他应用的UIAbility&#xff08;例如启…

LSTM的记忆能力实验 [HBU]

目录 模型构建 LSTM层 模型训练 多组训练 模型评价 模型在不同长度的数据集上的准确率变化图 模型汇总 总结 长短期记忆网络&#xff08;Long Short-Term Memory Network&#xff0c;LSTM&#xff09;是一种可以有效缓解长程依赖问题的循环神经网络&#xff0e;LSTM 的…

Text-to-SQL小白入门(十)RLHF在Text2SQL领域的探索实践

本文内容主要基于以下开源项目探索实践&#xff0c; Awesome-Text2SQL:GitHub - eosphoros-ai/Awesome-Text2SQL: Curated tutorials and resources for Large Language Models, Text2SQL, Text2DSL、Text2API、Text2Vis and more.DB-GPT-Hub&#xff1a;GitHub - eosphoros-ai…

【QML-按钮】

QML编程指南 VX&#xff1a;hao541022348 ■ 按钮■ AbstractButton■ Button■ CheckBox■ DelayButton■ RadioButton■ RoundButton&#x1f31f;■ Switch&#x1f31f;■ ToolButton&#x1f31f; ■ 按钮 ■ AbstractButton 所有的按钮控件的父类都是 AbstractButton 。…

Electron自定义通知Notification

Notification是什么&#xff1f; 对于渲染进程&#xff0c;Electron 允许开发者使用通知中API&#xff0c;来运行系统的原生通知进行显示。 如何实现系统Notification&#xff1f; const { Notification } require(electron);const isAllowed Notification.isSupported();…

市场复盘总结 20231226

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整 昨日回顾&#xff1a; SELECT CODE,成交额排名,净流入排名,代码,名称,DDE大单金额,涨幅,主力净额,DDE大单净量,CONVERT(DATETIME, 最后封…

概率论1:下象棋问题(3.5)

每日小语 时刻望着他人的眼色行事&#xff0c;是腾飞不了的。自己怎么想就积极地去做&#xff0c;这是需要胆量的。——广中平佑 题目 甲、乙二人下象棋&#xff0c; 每局甲胜的概率为a,乙胜的概率为b. 为简化问题&#xff0c;设没有和局的情况&#xff0c;这意味着a b1. 设想…

Flappy Bird游戏python完整源码

通过pygame实现当年风靡一时的flappy bird小游戏。 当前只设定了同样长度的管道&#xff0c;图片和声音文件自行导入。 效果如下&#xff1a; # -*- coding:utf-8 -*- """ 通过pygame实现曾风靡一时的flappybird游戏。 小鸟x坐标不变&#xff0c;画布左移实现…

分享5款对工作有帮助的小工具

​ 在职场中&#xff0c;简单而高效的工具是我们事业成功的一部分。这五款工具可能会成为你职业生涯中的得力助手。 1.图片标注——iPhotoDraw ​ iPhotoDraw是一款图片标注软件&#xff0c;可以在图片上添加文字、符号、尺寸、放大细节等注释对象&#xff0c;增加图片的信息…

〖Python网络爬虫实战㊹〗- JavaScript Hook 的用法

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;订阅本专栏前必读关于专栏〖Python网络爬虫实战〗转为付费专栏的订阅说明作者&#xff1…

SQL server 数据库练习题及答案(练习2)

使用你的名字创建一个数据库 创建表&#xff1a; 数据库中有三张表&#xff0c;分别为student,course,SC&#xff08;即学生表&#xff0c;课程表&#xff0c;选课表&#xff09; 问题&#xff1a; --1.分别查询学生表和学生修课表中的全部数据。--2.查询成绩在70到80分之间…

文件操作(c)

1.文件分类&#xff1a;分为程序文件和数据文件 1.程序文件&#xff1a;源程序文件&#xff08;后缀.c&#xff09;,目标文件&#xff08;windows环境后缀.obj&#xff09;,可执行程序&#xff08;windows环境后缀.exe&#xff09; 2.数据文件&#xff1a;程序运行时读写的数据…