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);
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

阿赵UE学习笔记——4、新建关卡

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   之前介绍了虚幻引擎的常用窗口功能&#xff0c;这次开始创建游戏内的世界了。首先先从创建关卡开始。 一、创建新关卡 在使用UE引擎制作游戏&#xff0c;首先要有一个场景作为基础&#xff0c;这个场景在UE里面成为关卡。…

若依(Spring boot)框架中如何在不同的控制器之间共享与使用数据

在若依框架或Spring boot框架中&#xff0c;控制器共享和使用数据是为了确保数据一致性、传递信息、提高效率和降低系统复杂性。这可以通过全局变量、依赖注入或数据库/缓存等方式实现。共享和使用数据对框架的正常运行非常关键&#xff0c;有助于促进控制器之间的协同工作&…

消息走漏提前做空腾讯爆赚30倍?逐帧分析还原真相

数量技术宅团队在CSDN学院推出了量化投资系列课程 欢迎有兴趣系统学习量化投资的同学&#xff0c;点击下方链接报名&#xff1a; 量化投资速成营&#xff08;入门课程&#xff09; Python股票量化投资 Python期货量化投资 Python数字货币量化投资 C语言CTP期货交易系统开…

C语言结构体内存对齐

文章目录 一、结构体内存对齐问题二、查看结构体成员起始位置三、设置内存对齐方式 一、结构体内存对齐问题 如下的info_s结构体类型&#xff0c;包含一个int型成员age, 一个char型成员gender, 一个int型成员id。 单从数据成员的大小进行分析&#xff0c;整个结构体的大小应为…

【JavaWeb学习笔记】18 - 文件上传下载

项目代码 https://github.com/yinhai1114/JavaWeb_LearningCode/tree/main/fileupdown 目录 文件上传 一、基本介绍 二、文件上传的基本原理 ​编辑 三、文件上传应用实例 四、文件上传的注意细节 1.解决中文乱码问题 2.分割文件夹 3.防止重名 4.百度WebUploader 5.空…

Windows无法安装edge 无法连接Internet

如果出现以上问题&#xff0c;或者Edge浏览器无法更新&#xff0c;提示防火墙错误之类的都可以解决问题。 下载以下证书文件并导入即可解决问题。 MicrosoftRootCertificateAuthority2011.cer

注意力机制(数学公式)

人类视觉注意力机制极大地提高了视觉信息处理的效率与准确性 计算机注意力机制是为了让卷积神经网络注意到他更加需要注意的地方 &#xff0c;而不是什么都关注 。 分为三种注意力机制&#xff0c;空间注意力机制&#xff0c;通道注意力机制&#xff0c;以及两者的结合。 …

关于MULTI#STORM活动利用远程访问木马瞄准印度和美国的动态情报

一、基本内容 于2023年6月22日&#xff0c;一款代号为MULTI#STORM的新网络钓鱼活动将目标瞄准了印度和美国&#xff0c;利用JavaScript文件在受感染的系统上传播远程访问木马。 二、相关发声情况 Securonix的研究人员Den luzvyk、Tim Peck和Oleg Kolesnikov发表声明称&#x…

【AI服饰】孔雀背景服装_AIGC服饰订制设计咨询产业

服饰系列 AIGC&#xff08;Artificial Intelligence Generated Content&#xff09;服饰图是指通过人工智能生成的服装设计图案。随着人工智能技术的不断进步&#xff0c;AIGC服饰图在未来有着广阔的发展空间。 首先&#xff0c;AIGC服饰图可以提供更多的设计可能性。传统的服…

PYTHON基础:K最邻近算法

K最邻近算法笔记 K最邻近算法既可以用在分类中&#xff0c;也可以用在回归中。在分类的方法&#xff0c;比如说在x-y的坐标轴上又两个成堆的数据集&#xff0c;也就是有两类&#xff0c;如果这个时候有个点在图上&#xff0c;它是属于谁&#xff1f; 原则就是哪一类离它比较近…

RK3568平台开发系列讲解(Linux系统篇)GPIO调试手段

🚀返回专栏总目录 文章目录 一、/sys/kernel/debug/gpio目录二、/sys/kernel/debug/pinctrl 目录沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 GPIO调试手段。 一、/sys/kernel/debug/gpio目录 debugfs 是 Linux 内核提供的一个调试文件系统,可以用于…

Navicat删除连接报错:service in use cannot be deleted的解决方法

我在删除连接时遇到了这个报错&#xff0c;内容如图。下面我介绍两种解决方法&#xff0c;非常简单。 第一种 右键点击想要删除的连接&#xff0c;先选择“关闭连接”。再选择“删除连接” 第二种 如果第一种方法无效&#xff0c;直接关闭Navicat软件&#xff0c;重新打开。然后…

javaweb--实验十 --期末复盘

实验十 JDBC连接MySQL 本次实验没有代码补全&#xff0c;以下都是完整过程&#xff0c;详细关注连接过程的问题 一、实验目的&#xff1a; 1、掌握JDBC连接数据库的一般操作。 2、理解JavaBean的基本作用。 3、理解分层设计的思想。 二、 实验内容&#xff1a; 实现学生信…

广州华锐互动VRAR:VR煤矿安全警示教育系统提供真实生动的场景体验

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐渗透到各个领域&#xff0c;为人们的生活带来了诸多便利。在煤矿行业&#xff0c;VR技术的应用也日益受到关注。广州华锐互动开发的VR煤矿安全警示教育系统&#xff0c;旨在通过虚拟现实技术&…

WordPress主题大前端DUX v8.3源码下载

DUX主题8.3版本更新内容&#xff1a; 新增&#xff1a;Cloudflare Turnstile 免费验证功能 新增&#xff1a;子菜单页面模版&#xff0c;支持多级页面 新增&#xff1a;手机端文章内表格自动出现横向滚动条&#xff0c;可集体或单独设置滚动宽度 新增&#xff1a;标签云页面模版…

drf知识-06

视图集 #1 ModelViewSet&#xff1a; 视图类&#xff1a;GenericAPIView 映射&#xff1a;list create retrieve update destroy #2 ViewSetMixin类&#xff1a; 只要继承它&#xff0c;路由写法变了 分析&#xff1a;ViewSetMixin 不是视图类&#xff0c;支…

FTP不同方式使用与搭建与端口号常识了解

目录 一、FTP介绍 二、winServer2012搭建ftp服务器 在虚拟机搭建具体步骤 2.1、新建组&#xff1a; 2.2、新建用户名 2.3、把用户名与组绑定 2.4、安装ftp 2.5、配置ftp服务器 2.6、给文件夹调整权限 2.7、测试 a、服务器本机测试 b、外部机器测试 C、借助工具Mobal…

低代码开发:数字化转型的引擎

引言 在当今数字化时代&#xff0c;组织面临着不断变化的市场需求和技术挑战。数字化转型已成为维持竞争力的关键&#xff0c;而低代码开发正在崭露头角&#xff0c;成为加速创新和数字化转型的有力工具。本文将深入探讨低代码开发的核心概念、优势和应用&#xff0c;以揭示它…

数据库基础面试第四弹

1. Redis的数据结构有哪些 1. 字符串&#xff08;String&#xff09;&#xff1a; 字符串是Redis最基本的数据结构。它可以存储任意类型的数据&#xff0c;包括文本、整数或二进制数据。字符串类型的值最大可以达到512MB。 1 2 3 4 SET name "John" GET name 将字…

云原生Kubernetes:K8S集群版本升级(v1.22.14 - v1.23.14)

目录 一、理论 1.K8S集群升级 2.环境 3.升级集群&#xff08;v1.23.14&#xff09; 4.验证集群&#xff08;v1.23.14&#xff09; 二、实验 1. 环境 2.升级集群&#xff08;v1.23.14&#xff09; 2.验证集群&#xff08;v1.23.14&#xff09; 一、理论 1.K8S集群升级 …