漫步Facebook开源C++库Folly之string类设计(散列、字符串、向量、内存分配、位处理等,小部分是对现有标准库和Boost库功能上的补充,大部分都是基于性能的需求而“重新制造轮子”)
就在近日,Facebook宣布开源了内部使用的C++底层库,总称folly,包括散列、字符串、向量、内存分配、位处理等,以满足大规模高性能的需求。
这里是folly的github地址:https://github.com/facebook/folly
在folly项目的Overview.md中,谈到了folly库的初衷:
It complements (as opposed to competing against) offerings such as Boost and of course
std
. In fact, we embark on defining our own component only when something we need is either not available, or does not meet the needed performance profile.
除了小部分是对现有标准库和Boost库功能上的补充,大部分都是基于性能的需求而“重新制造轮子”。
特别是大规模下的性能需求,大规模下的性能追求是Folly统一的主题:
Good performance at large scale is a unifying theme in all of Folly.
为什么先谈string类?
一是因为string几乎是C++程序中最常用的“容器”,性能至关重要;
二是因为之前也曾写过一篇博客《std::string的Copy-on-Write:不如想象中美好》,研究了std::string的copy-on-write实现的优缺点,因此想要看看Facebook究竟需要什么样的string。
folly自定义的string(以下简称为fbstring)的核心实现位于 folly/FBString.h。
还有一些fbstring的辅助函数(如向std::string的转换、各种格式的输出、escape、demangle等),位于 folly/String.h 和 folly/String.cpp ,由于本文主要谈的是fbstring的内部实现,这些内容暂且不提,有兴趣的童鞋可以自己参考源码,folly的代码还是写得相当漂亮的:)
folly对string类的设计和优化,主要体现在两个方面:
1. 内存模型
2. 常用方法的优化
下面将逐一说明。
一. 内存模型
1. 内存布局及策略
fbstring使用了三层的存储策略(three-tiered storage strategy),根据长度将fbstring分为三类:small/medium/large,分别采取不同的优化措施,以达到最佳性能。
fbstring内存模型示意图(使用LucidChart绘制):
简单来说:
短string:直接放在(栈上)对象中,避免了动态内存分配的开销。结构体长度为24字节,减去末尾的1字节(用来表示长度)和为结束符'\0'(data()和c_str()方法的需要)预留的1字节,可以放置22字节的有效长度。
中等string(小于255字节):直接通过malloc分配,并且采用eager-copy的方式,即字符串的复制总是会重新分配并拷贝内容。
至于为什么不用copy-on-write:
1. 我之前的博客也提到,copy-on-write的额外开销(原子操作、容易失效)一定程度上抵消了减少一次内存分配和拷贝带来的好处
2. folly鼓励使用jemalloc来代替glibc下默认的ptmalloc2,并且在代码中迎合jemalloc的使用做了大量优化。在这里,分配一个小片内存区域的开销是极小的,下文还会有说明。
较长string(大于255字节):使用copy-on-write,减少分配和拷贝大内存的开销。在这里,folly使用了C++11中的原子变量:std::atomic<size_t>来管理引用计数,并在引用计数减为0时销毁内存。
PS:使用capacity最高位的4个bits来判断string的种类,folly假定机器的字节序为小端(little endian),适用于x86-64平台上的大部分OS。
2. 内存分配器
与std::string不同,fbstring并没有从模板参数之一的Allocator获取内存,而是直接使用malloc/free管理内存。
fbstring推荐使用jemalloc而不是Linux下glibc默认的ptmalloc2来管理动态内存:
1. 作为FreeBSD上的默认分配器,jemalloc在多线程并发的环境下表现更好(与google开源的tcmalloc性能相近)。
在tcmalloc的论文《TCMalloc : Thread-Caching Malloc》中,提到了ptmalloc2在多线程环境下的一个致命缺陷:
ptmalloc2同样通过为不同的线程分配自己的内存池(Arena)的方式来减少并发分配时的锁冲突,但ptmalloc2中线程拥有的内存池是不能迁移的,在某些情况下能够带来巨大的内存浪费:比如一个线程在开始阶段分配了300MB的内存进行初始化工作,然后释放了,但接下来的线程分配到不同的内存池,那么之前的300MB是无法重复利用的。
2. folly如果检测到使用jemalloc,那么将使用jemalloc的一些非标准扩展接口来提高性能。
PS:folly通过定义弱符号(weak symbol)的方法来运行时判断是否使用了jemalloc:
extern "C" int rallocm(void**, size_t*, size_t, size_t, int) __attribute__((weak)); /**
* Determine if we are using jemalloc or not.
*/
inline bool usingJEMalloc() {
return rallocm != NULL;
}
如果使用了jemalloc,一个典型的优化是使用jemalloc特有的rallocm来代替标准的realloc方法。(下面还会提到realloc的优化)
同时,所有动态内存请求的大小都会经过一个过滤函数:goodMallocSize(在folly/Malloc.h中)处理,以获取一个对jemalloc友好的值
goodMallocSize在不同的请求区间,将请求大小设置为64b / 256b / 4KB / 4MB对齐,以提高分配/回收效率,减少内存碎片。
二. 常见操作的优化
fbstring在实现时做了很多优化(如word-wise copy等),其中的细节不再一一敷述,感兴趣的读者建议去参考源码,这里只列出重要的几点:
1. 末尾'\0'的处理
fbstring的默认行为是“懒惰”添加'\0'(lazy append),即平时预留空间,只在调用data()或者c_str()时,才在结尾添加'\0',避免了每次修改字符串时的额外开销(特别是push_back操作),因为这样做是符合C++标准的。
(当然,fbstring也有相应的宏来关闭该行为)
2. realloc的处理
string很多时候需要realloc,为了优化realloc的效率,fbstring做了这样的设定:
(1)如果使用jemalloc:使用jemalloc的非标准接口——rallocm
(2)没有使用jemalloc:
当前内存的使用率小于50%(size * 2 < capacity),放弃使用realloc(因为realloc可能需要拷贝全部内存,而其中超过一半是无效内容),而是简单采用free+malloc+copy的方式来重新分配内存,减少拷贝开销。
当前内存的使用率大于50%,则使用realloc,寄希望realloc可以合并后面的内存(coalescing)以避免拷贝。
3. 优化string::find()
glibc的string::find()实现中只实现了简单的逐字符查找比较功能,复杂度为O(M*N)。(C++标准并没有规定string::find的复杂度要求)
find使用了简化的Boyer-Moore算法,代码中声称:
Casual tests indicate a 30x speed improvement over
string::find()
for successful searches and a 1.5x speed improvement for failed searches.
如果是简单的短字符查询,string::find()应该足够高效。只有在长字符搜索的情况下,find的BM算法实现才能体现出优势,或许这也是Facebook的常用场景吧。
结语:
顺便提一下,fbstring(FBString.h)的作者为Andrei Alexandrescu(熟悉C++应该都听说过),近距离欣赏大师的代码实在是一种享受。
同时,Alexandre大叔以43岁的“高龄”,依然在Facebook写着如此底层的程序。个中滋味,值得天朝所有浮躁的程序员(包括笔者在内)和“35岁论“者细细体味。
PromisE_谢 (Xielingjun6522@163.com) 原创文章
转载请注明出处:http://www.cnblogs.com/promise6522/archive/2012/06/05/2535530.html
漫步Facebook开源C++库Folly之string类设计(散列、字符串、向量、内存分配、位处理等,小部分是对现有标准库和Boost库功能上的补充,大部分都是基于性能的需求而“重新制造轮子”)的更多相关文章
- c++ string类使用及用string类解决整行字符串输入
下面随笔给出c++ string类使用及用string类解决整行字符串输入. string类 使用字符串类string表示字符串 string实际上是对字符数组操作的封装 string类常用的构造函数 ...
- 从字符串到常量池,一文看懂String类设计
从一道面试题开始 看到这个标题,你肯定以为我又要讲这道面试题了 // 这行代码创建了几个对象? String s3 = new String("1"); 是的,没错,我确实要从这里 ...
- 基于标准库的string类实现简单的字符串替换
感觉基本功还是不扎实,虽然能做些程序但是现在看来我还是个初学者(primer),试着完成习题结果还得修修改改. 废话不多说,实现功能很简单,<C++ Primer>9.5.2节习题. // ...
- String类练习统计一个字符串中大小写字母及数字字符个数
public class StringPractice { public static void main(String[] args) { //创建一个文本扫描器 Scanner sc = new ...
- 109、Java中String类之截取部分子字符串
01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...
- String类自带的字符串处理原生方法
一.取出指定索引的字符 —— 使用charAt()方法 二.字符串与字符数组的转换 三.字符串转大写.先转换成数组,然后再改变ASCII码 四.给定一个字符串,要求判断其是否由数字组成 五.字符串与字 ...
- C++标准模板库Stand Template Library(STL)简介与STL string类
参考<21天学通C++>第15和16章节,在对宏和模板学习之后,开启对C++实现的标准模板类STL进行简介,同时介绍简单的string类.虽然前面对于vector.deque.list等进 ...
- Java进阶01 String类
链接地址:http://www.cnblogs.com/vamei/archive/2013/04/08/3000914.html 作者:Vamei 出处:http://www.cnblogs.com ...
- (转)C++——std::string类的引用计数
1.概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功课,于是你把自己关在房间里 ...
随机推荐
- SecondaryNameNode 的作用
Secondary NameNode:它究竟有什么作用? 尽量不要将 secondarynamede 和 namenode 放在同一台机器上. 1. NameNode NameNode 主要是用来保存 ...
- Mac修改文件权限:You don’t have permission to save the file
1.从互联网上或者其他途径拿过来的工程代码,往往会报下面的提示: (1)打开文件的时候出现窗口提示You don’t have permission to save the file “project ...
- css中背景 字体 文体属性练习
@charset "utf-8"; body{ background-color:#332244;/*//背景颜色*/ background-image:("../kk. ...
- javascript数据结构与算法 零(前记+前言)
前记 这本书Data Structure and Algorithm with Javascript 我将其翻译成<< javascript 数据结构和算法>> 为什么这么翻译 ...
- htmlunit 模拟登录 数字验证码
使用htmlunit的好处有两点,相比httpclient,htmlunit是对浏览器的模拟,比如你定位一个按钮,就可以执行click()方法,此外不需要象在httpclient中一样编写复杂的代码, ...
- linux下jar包的生存与开机自动启动
前言 作为一名Android开发,有的时候也是要做做其他工作的,前段时间写了一个很简单的java消息转发程序,因为和三方厂商合作,对方只提供了java的sdk,然而我们这边都是清一色的C#后台开发人员 ...
- Word 2010/2013 菜单栏添加 MathType 菜单
一般对于 office 的高级版本,比如 word 2010/2013,在手动安装 mathtype 之后,并不会再 word 的菜单栏,创建 mathtype 按钮,此时需要按照如下步骤,手动导入 ...
- Cocos2d-x 3.2 Lua演示样本 ActionTest(操作测试)
Cocos2d-x 3.2 Lua演示样本 ActionTest(操作测试) 2014年博文大赛,请投上您宝贵的一票:http://vote.blog.csdn.net/Article/Details ...
- Ubuntu不输入密码执行sudo命令方法介绍
作为ubuntu等桌面系统,默认登录的帐号是没有root权限的,为了提升权限来执行任务,我们一般用到sudo+命令来执行,但是不难发现我们一般都要输入密码.那么有没有什么方法可以让我们执行sudo的时 ...
- C# WPF 左侧菜单右侧内容布局效果实现
原文:C# WPF 左侧菜单右侧内容布局效果实现 我们要做的效果是这样的,左侧是可折叠的菜单栏,右侧是内容区域,点击左侧的菜单项右侧内容区域则相应地切换. wpf实现的话,我的办法是用一个tabcon ...