当前位置: 首页 > news >正文

[c++]你最喜爱的stringstream和snprintf性能深入剖析

最近写一个程序中两个差不多的模块,一个使用了snprintf输出中间数据,另一个偷懒使用stringstream。结果你猜怎么着?居然压帧了!!到底是谁拖了性能的后退?

来自阿里云的性能分析实验

我上网一搜,有人做了性能分析实验。他的实验demo大概有4个步骤:

  1. 循环体内部构造stringstream对象,填充数据
  2. 循环体外部构造stringstream对象,循环体内每次使用清空对象再使用
  3. 循环体内部创建buffer,使用snprintf填充数据
  4. 循环体外部创建buffer,循环体内先清空buffer再使用snprintf填充数据

在十万次调用结束后,上述4种方式所耗时情况为: 方法 2 > 方法 3 > 方法 4 > 方法 1 方法2>方法3>方法4>方法1 方法2>方法3>方法4>方法1

可见,不要干这种不必要的在循环体内部反复构造析构的事情。

原因

那么,为啥呢?这俩到底做了啥呢?

C99 snprintf

观察它的源码,会发现和其他printf一样,他是一个可变参函数,这就意味着它会经历一系列的递归展开:

/* Maximum chars of output to write in MAXLEN.  */
extern int snprintf (char *__restrict __s, size_t __maxlen,const char *__restrict __format, ...)__THROWNL __attribute__ ((__format__ (__printf__, 3, 4)));

当展开到最底层的时候,这个函数首先根据所需的字符串长度预先分配内存,底层差不多这样:

char* buf = (char*)malloc(buf_size);

然后,对分配的内存执行格式化操作:

int result = vsnprintf(buf, buf_size, format, args);

可以看到有意思的是,他的参数展开是依赖于vsnprintf这个函数的:

extern int vsnprintf (char *__restrict __s, size_t __maxlen,const char *__restrict __format, _G_va_list __arg)__THROWNL __attribute__ ((__format__ (__printf__, 3, 0)));

为了不让自己的头变得很大,我在这里做一个非常短小精悍的vsnprintf精华版实现:

int vsnprintf(char *__restrict __s, size_t __maxlen,const char *__restrict __format, _G_va_list __arg) {  int result;  va_list copy;  va_copy(copy, args);  result = vsnprintf_l(__restrict __s, __maxlen, __restrict __format, copy);  va_end(copy);  return result;  
}

这里其实他的实现因编译器而异,我在这使用了vsnprintf_l,是一个线程安全的版本。

接下来,打住!请确保你已经了解了可变参数列表和相关函数的基础知识!如果不太了解,过两天我再写一个博客(肥水不流外人田.jpg)

接下来,我们看一下这个函数干了什么:

  1. va_copy(copy, args);创建了一个可变参数列表的副本。为什么要创建副本呢?本质上是为了防止修改参数列表而对原来的参数列表造成难以debug的痛苦影响。
  2. vsnprintf_l(__restrict __s, __maxlen, __restrict __format, copy);这个函数接收了下列参数并格式化了buf
    1. 指向我们指定的、要写入的buf的指针
    2. 我们指定的buf的大小
    3. 包含结果字符串格式的格式化字符串(回文表达,耶!)
    4. 参数列表,要写入字符串中的实际值
  3. 我们的vsnprintf_l函数返回了一个整数值,表示成功写入到缓冲区的字符数(不包括结尾的空字符)。这个值会被vsnprintf函数返回给调用者。
  4. 为了不发生内存泄漏,在最后一步清理参数列表。

显然,这个时候,我们陷入了一个套娃:看起来snprintf要做的事,被vsnprintf拿去了,而vsnprintf要做的事,又被vsnprintf_l拿去了!

为什么呢?因为涉及到数据的写入,我们一定得考虑多线程的情况下是否写入操作是安全的。

我们来看看这个线程安全的vsnprintf_l:

#define MAX_BUFFER_SIZE 1024typedef struct {locate_t locate;char buffer[MAX_BUFFER_SIZE];size_t size;
}vsnprintf_data;static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;// 名字很长长长长的参数名写不动了,换成小名吧 =.=
int vsnprintf_l(char *str, size_t size, const char *format, va_list args) {  vsnprintf_data data;  data.size = size;  data.locale = locale_t();  if (format) {  data.locale = newlocale(LC_ALL_MASK, format, data.locale);  }  int result = vsnprintf(data.buffer, MAX_BUFFER_SIZE, format, args);  if (data.locale) {  freelocale(data.locale);  }  return result;  
}

我们看到,我们有一个用于存储线程安全的数据的结构体vsnprintf_data,它的内容是这样的:

  1. locate_t locate:存储当前线程的locate信息
  2. buffer:存储格式化后的字符串
  3. size:我们的老朋友size,表示缓冲区的大小

我们首先定义了一个互斥锁来守护多线程环境下操作的正确性。接着:

  1. 创建了一个vsnprintf_data的实例data,将它的size初始化为传入的大小参数。如果传递了格式化字符串(format),那么我们使用newlocate函数创造一个新的locate对象,并把它存在data.locate中。这个locate对象是根据传递的格式化字符串创建的,用于支持特定的语言环境。
  2. 接着,函数调用vsnprintf函数,将数据写入data.buffer中。我们看到套娃开始:vsnprintf函数会根据指定的格式化字符串和参数列表将数据格式化为字符串,并将结果写入到缓冲区中。如果格式化后的字符串超过了缓冲区的大小,vsnprintf会自动调整缓冲区大小,动态地分配和释放内存。格式化后的字符串超过了最初分配的内存大小,函数会通过调用realloc来重新分配一块足够大的内存区域,并再次进行格式化操作。如果在第一次分配内存后有足够的空间容纳格式化后的字符串,那么不会发生重新分配内存的情况。完成格式化操作后,可以通过调用free来释放分配的内存。
  3. 如果创建了新的locale对象,函数会使用freelocale函数释放该对象。然后返回vsnprintf函数的返回值,表示成功写入到缓冲区的字符数(不包括结尾的空字符)。

需要注意的是,在snprintf函数中,每次重新分配内存后,新的内存块会被写入到原始内存块的后面,以充分利用已分配的内存空间。此外,如果在第一次分配内存后有足够的空间容纳格式化后的字符串,那么不会发生重新分配内存的情况。

可以看到,由于格式化字符串解析的复杂性、参数的数量和类型、字符串的大小和内容等因素,这个函数的性能会受到一些影响。

stringstream

完了,打不到车了。我先打车回家明天再写55555

相关文章:

[c++]你最喜爱的stringstream和snprintf性能深入剖析

最近写一个程序中两个差不多的模块,一个使用了snprintf输出中间数据,另一个偷懒使用stringstream。结果你猜怎么着?居然压帧了!!到底是谁拖了性能的后退? 来自阿里云的性能分析实验 我上网一搜&#xff0…...

windows 用vs创建cmake工程并编译opencv应用项目生成exe流程简述

目录 前言一、安装opencv(1)下载(2)双击安装(3)环境变量和system文件夹设置 二、打开vs创建项目三、编辑cpp,.h,cmakelist.txt文件(1)h文件(2&…...

QML 仪表盘小示例

本次项目已发布在CSDN->GitCode,下载方便,安全,可在我主页进行下载即可,后面的项目和素材都会发布这个平台。 个人主页:https://gitcode.com/user/m0_45463480怎么下载:在项目中点击克隆,windows:zip linux:tar.gz tar # .pro TEMPLATE = appTARGET = dialcontrol​#…...

力扣206. 反转链表

题目: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1] 示例 2: 输入:head [1,2] 输出:[2,1] 示例 3:…...

深度学习之基于Tensorflow卷积神经网络花卉识别系统

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习是一种机器学习方法,它通过模拟人脑神经网络的结构和功能来实现对数据的自动分析和学习。卷积神…...

leetcode链表

这几天手的骨裂稍微好一点了,但是还是很疼,最近学校的课是真多,我都没时间做自己的事,但是好在今天下午是没有课的,我也终于可以做自己的事情了。 今天分享几道题目 移除链表元素 这道题我们将以两种方法开解决&…...

Kali Linux渗透测试的艺术

Kali Linux(Kali)是专门用于渗透测试的Linux操作系统,它由BackTrack 发展而来。在整合了IWHAX、WHOPPIX 和Auditor 这3 种渗透测试专用Live Linux 之后,BackTrack正式改名为Kali Linux。 BackTrack是相当著名的Linux发行版本。在…...

2023年最新版潮乎盲盒源码含搭建教程

后台开发语言:后端 Laravel 框架开发 前端开发框架:uniappvue 环境配置: php7.4 mysql5.6 nginx1.22 redis(建议宝塔面板或 lnmp) 源码获取请自行百度:一生相随博客 一生相随博客致力于分享全网优质资源&#x…...

[GitLab] 安装Git 指定版本

卸载旧版本 检查是否已经安装 git --version如果已经安装,先卸载 yum -y remove git安装新版本 在GitHub上选择需要下载的版本 Git版本 在/usr/local/目录下新建文件夹:git,并在/usr/local/git/文件夹内下载压缩包 wget https://github…...

vue中ref和$refs

1.作用 利用ref 和 $refs 可以用于 获取 dom 元素 或 组件实例&#xff0c;也可以在父组件获取子组件&#xff0c;从而调用子组件的方法。 2.特点&#xff1a; 查找范围 → 当前组件内(更精确稳定) 3.语法 1.给要获取的盒子添加ref属性 <div ref"chartRef"&…...

CRM怎样帮助您的企业进行营销管理?

​ CRM助力企业营销管理&#xff0c;为企业降本增效提升投入产出比。CRM软件是如何实现的呢&#xff1f; 扩大线索量 想要精准获客的第一步是要扩大线索量&#xff0c;多渠道营销推广是很好的方法。例如&#xff1a; 1.线下展会线上Webinar等市场活动 2.搭建微信、微博、…...

Gerrrit 管理员常用命令

1. replication插件重新加载 当修改replication配置文件&#xff08;replication.config&#xff09;&#xff0c;需要重启gerrit生效&#xff0c;或者重新加载replication。 # 重新加载replication插件 ssh -p 29418 usernamegerrit.company.com gerrit plugin reload repli…...

深入理解强化学习——多臂赌博机:增量式实现

分类目录&#xff1a;《深入理解强化学习》总目录 至今我们讨论的动作—价值方法都把动作价值作为观测到的收益的样本均值来估计。下面我们探讨如何才能以一种高效的方式计算这些均值&#xff0c;尤其是如何保持常数级的内存需求和常数级的单时刻计算量。 为了简化标记&#x…...

视频批量混剪剪辑软件类似剪映设计一个模板后, 视频,图片,文字,转场,音频,特效都可以系统随机

随着自媒体时代的到来&#xff0c;越来越多的人加入到了视频创作行列。然而&#xff0c;视频剪辑是一项繁琐的任务&#xff0c;特别是当你需要批量处理多个视频时。为了提高效率&#xff0c;一款名为“视频闪闪”的批量剪辑软件应运而生。 www.shipinshanshan.com “视频闪闪”…...

优维低代码实践:打包发布

导语 优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。…...

js深度学习(三)

循环 var i0 for(;i<10;){ console.log(i) i } while(i<10){ console.log(i) i } var i100; for(;i--;){ console.log(i) }2、引用值 typeof&#xff1a;number string boolean Object(object/array/null出现是为了指定为空对象/)undefined function typeof a >unde…...

JVM类的声明周期

文章目录 版权声明生命周期概述加载阶段查看内存中的对象 连接阶段连接阶段之验证连接阶段之准备连接阶段之解析 初始化阶段练习题目一练习题目二练习题目三练习题目四 使用阶段卸载阶段总结 版权声明 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明…...

html将复选框变为圆形样例

html将复选框变为圆形样例 说明目录使用对勾图标实现圆形复选框原复选框html代码及默认样式取消复选框未勾选前的样式新增复选框未勾选前的样式新增复选框勾选后的样式获取复选框选中后的value值 使用CSS样式写对勾图标实现圆形复选框 说明 这里记录下用原生html实现将原复选框…...

笔记软件 Keep It mac v2.3.3中文版新增功能

Keep It mac是一款专为 Mac、iPad 和 iPhone 设计的笔记和信息管理应用程序。它允许用户在一个地方组织和管理他们的笔记、网络链接、PDF、图像和其他类型的内容。Keep It 还具有标记、搜索、突出显示、编辑和跨设备同步功能。 Keep It for mac更新日志 修复了更改注释或富文本…...

uni-app 开发的H5 定位功能部署注意事项

一、H5部署的时候&#xff0c;如果设计到定位功能&#xff0c;需要注意以下几点 1、打包部署的时候需要在Web配置-定位和地图里面勾选一个地图&#xff0c;并配置key 2、打包部署需要域名是https协议的&#xff0c;大多数现代浏览器要求在HTTPS协议下才能够访问地理位置信息&a…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

VisualXML全新升级 | 新增数据库编辑功能

VisualXML是一个功能强大的网络总线设计工具&#xff0c;专注于简化汽车电子系统中复杂的网络数据设计操作。它支持多种主流总线网络格式的数据编辑&#xff08;如DBC、LDF、ARXML、HEX等&#xff09;&#xff0c;并能够基于Excel表格的方式生成和转换多种数据库文件。由此&…...

Visual Studio Code 扩展

Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后&#xff0c;命令 changeCase.commands 可预览转换效果 EmmyLua…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 原创笔记&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;《数据结构第4章 数组和广义表》…...

Docker环境下安装 Elasticsearch + IK 分词器 + Pinyin插件 + Kibana(适配7.10.1)

做RAG自己打算使用esmilvus自己开发一个&#xff0c;安装时好像网上没有比较新的安装方法&#xff0c;然后找了个旧的方法对应试试&#xff1a; &#x1f680; 本文将手把手教你在 Docker 环境中部署 Elasticsearch 7.10.1 IK分词器 拼音插件 Kibana&#xff0c;适配中文搜索…...