C++不是号称不限制你的开发方式么,每个库想怎么搞就怎么搞,这明明就是 C++的优势,不知道你们抱怨个啥?哈哈
接着说 std::string 的性能问题,举个具体例子吧,之前接手过一个项目,别的部门同事自己撸的一套 DirectUI 系统,用 tinyxml 解析界面节点,项目简单的时候没啥,随着ui越来越复杂,数千个节点,每个xml节点若干属性,每个属性就是一个字符串,我记得好像有500+ KB的 xml要解析,而且这部分界面还没法延迟初始化,必须启动加载时做完,启动十分慢。profile下来,很多时间卡在 tinyxml上,整个过程接近 3秒,费时最前的操作卡在处理各种字符串的操作上。
把 tinyxml 换成其他 xml库 ?没那么容易,项目各处模块都在依赖 tinyxml的各种接口和类。一开始觉得内部的 TiXmlString 实现有问题,换成 std::string,vc 2012下时间从3秒增加到4秒,更不靠谱(vs2012应该已经有所谓SSO了),所以人家 tinyxml 这里用自己的 TiXmlString 肯定也是比较过的,不然干嘛不用 std::string 。
但问题总得解决,所以还得优化字符串实现:
1. 自己重新给 TiXmlString 实现了一套新的 SSO ,因为 xml里面很多小字符串,10个字节以内的占比很多,这部分用 TiXmlString 里面一块静态空间存储,随着capacity变化,超过限制长度的字符串才会开辟新的空间存储,这样避免了大量的内存分配,和碎片,总解析时间从3秒下降到 2秒。
2. 还是嫌慢,又把 tinyxml 继续改写,增加把文本 xml编译成二进制格式的功能,平时开发用xml,实际发布用二进制版本的 xml,免去整个文本解析过程,时间进一步从2秒缩短到 0.8 秒。
3. 还嫌不够快,接着改进二进制 xml文件结构,扫描整个 xml里面用到的所有字符串,统一做一个字符串常量表放在文件最前面,这样,二进制 xml文件里涉及到字符串的地方从缘来一段内存变成字符串表的一个索引 int,整个 TiXmlString 也变成对字符串常量表里某个索引的引用,这样彻底避免了字符串分配和维护操作,而且总内存变小了,比如 "type", "button", "label" "text" 等高频字符串只存储一遍,以前 1000个 "text" 字符串要解析1000遍,还要创建分配 1000次内存,有了常量表以后,所有的 "text" 都是一个引用,不需要1000便解析,更不需要1000次构造,时间从 0.8秒继续下降到 0.2秒。
运营常识,客户端项目,启动时间直接和用户流失率成正比,tinyxml字符串优化,前后把优化前的 3秒下降到优化后的 0.2秒,基本xml解析不再是一个瓶颈。
为什么不同的库要实现不同的字符串呢?从这个小例子可见一斑。
后来呢?嗯,后来有一天我不能忍了,我把整个项目的 gui系统弄成 Qt 的了,ui描述文件直接编译成代码,再也不用烦这些事情。
在结合看楼上高票说的 QString,可以感受下。
所以大家才会说:人在做,天在看,信 Qt,保平安。。。。。
补充一点:ABI的需要。
其实ABI中最麻烦的就是传递sized-buffer,其他的都还好说。
如果你用STL中的容器来传,那么就要面临不同runtime的兼容性问题。
std::string并没有实现字符串应该做的事,而仅仅只是用STL风格的容器类接口封装了一下char[]罢了,其他std::xxstring同理。
与其说它是个字符串,还不如说它是个处理二进制数据的buffer,基本等同于std::vector<char>。
std::string连内部统一编码都做不到,根本没法拿来做文本处理,一直到c++11,在algorithm、regex、localization、chrono这么一大堆类库的支撑下,才勉强可堪一用。
所以,凡是有文本处理需求的framework,都必须自己搞一个string类。
相反,题主所说的高性能场景下,std::string借助STL的支持,反而是很强力的,也许在极端需求下需要自己写,但大部分场景下足够使用,比起文本处理方面实在强太多了。所以我觉得std::string更像是一个buffer。
这里,我就用我见过的最强大的字符串类,Qt的QString做下对比吧:
至于说到处理二进制数据么……Qt有个比QString更底层的QByteArray类,和std::string效果相同:
对了,顺便说下辅助类QTextCodec和QLocale吧
以上各个类(QString、QByteArray、QLocale等),每个类的源文件也就三五个,全都包含在QtCore模块中。该模块的relase版dll只有4M,并且可以通过编译选项裁剪到1M级别。
而QtCore中的其他类,也都是如斯强大的,比如处理URI的QUrl,比如c++17才有的std::any(QVariant),比如IPC所用的QSharedMemory,比如说逐线程数据存储QThreadStorage,比如c++17才有的filesystem,比如支持全反射的元对象系统,比如比异步时间循环还高级的信号槽机制,比如xml流式处理,比如json处理(有人和rapidJson做过对比,大约为rapidjson耗时的1.4倍),比如定时器,比如DateTime……
QtCore.dll,4M大小,涵盖了c++11到17,除了network之外的所有东西,并且全都比标准库中的更加强大,就问你是不是物超所值?
好吧我偏题了。
总之,相比起来,std::string是什么渣渣?我还从没见过有比QString更强力的字符串类,尤其是加上QByteArray、QLocale这些相关类之后。
(动态语言,利用更高级的语言特性,可以比QString更顺手,但不会有太大差距,主要是类似linq的语法上的优势,在功能性上并没有差距,而QString的性能则远高于前者。只有正则上QString是弱项——或者说整个c++在正则上都是弱项,因为动态语言可以把正则JIT到机器码……)
附:
QJson vs RapidJson vs JSON.parse 该文章里处理的json文件只有5kb。如果是大文件,则还需要考虑双方使用的容器类的性能差异。在不专门特化allocator的情况下,Qt和STL的容器类基本不存在性能差距——论Qt容器与STL
其实这原因主要有这些:
1,C++诞生于1979年,STL在1994年才进入C++标准库,两者相隔15年。早期的C++库都必须实现一个自己的string类,既然实现了,也就一直沿用下来了。这应该是最重要的原因。
2,C++标准库的string类的部分功能依赖STL自身的算法。而STL算法早期用起来也是非常不方便的,那时候也没有lambda。——所以开源库希望有一个比较纯粹的字符串类,能够不依赖任何其他模块就完整完成字符串功能的。
3,觉得C++标准库的string缺少自己需要的功能又难以扩展。必须直接改stl代码才好做,但这样就不如干脆自己重新做一个。又由于「破窗效应」的原理,既然已经有了那么多第三方字符串实现,自己额外重新实现一个也是一件平常的事情了。
本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度,google,bing,sogou 等
© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有