就在近日,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库功能上的补充,大部分都是基于性能的需求而“重新制造轮子”)的更多相关文章

  1. c++ string类使用及用string类解决整行字符串输入

    下面随笔给出c++ string类使用及用string类解决整行字符串输入. string类 使用字符串类string表示字符串 string实际上是对字符数组操作的封装 string类常用的构造函数 ...

  2. 从字符串到常量池,一文看懂String类设计

    从一道面试题开始 看到这个标题,你肯定以为我又要讲这道面试题了 // 这行代码创建了几个对象? String s3 = new String("1"); 是的,没错,我确实要从这里 ...

  3. 基于标准库的string类实现简单的字符串替换

    感觉基本功还是不扎实,虽然能做些程序但是现在看来我还是个初学者(primer),试着完成习题结果还得修修改改. 废话不多说,实现功能很简单,<C++ Primer>9.5.2节习题. // ...

  4. String类练习统计一个字符串中大小写字母及数字字符个数

    public class StringPractice { public static void main(String[] args) { //创建一个文本扫描器 Scanner sc = new ...

  5. 109、Java中String类之截取部分子字符串

    01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...

  6. String类自带的字符串处理原生方法

    一.取出指定索引的字符 —— 使用charAt()方法 二.字符串与字符数组的转换 三.字符串转大写.先转换成数组,然后再改变ASCII码 四.给定一个字符串,要求判断其是否由数字组成 五.字符串与字 ...

  7. C++标准模板库Stand Template Library(STL)简介与STL string类

    参考<21天学通C++>第15和16章节,在对宏和模板学习之后,开启对C++实现的标准模板类STL进行简介,同时介绍简单的string类.虽然前面对于vector.deque.list等进 ...

  8. Java进阶01 String类

    链接地址:http://www.cnblogs.com/vamei/archive/2013/04/08/3000914.html 作者:Vamei 出处:http://www.cnblogs.com ...

  9. (转)C++——std::string类的引用计数

    1.概念 Scott Meyers在<More Effective C++>中举了个例子,不知你是否还记得?在你还在上学的时候,你的父母要你不要看电视,而去复习功课,于是你把自己关在房间里 ...

随机推荐

  1. SecondaryNameNode 的作用

    Secondary NameNode:它究竟有什么作用? 尽量不要将 secondarynamede 和 namenode 放在同一台机器上. 1. NameNode NameNode 主要是用来保存 ...

  2. Mac修改文件权限:You don’t have permission to save the file

    1.从互联网上或者其他途径拿过来的工程代码,往往会报下面的提示: (1)打开文件的时候出现窗口提示You don’t have permission to save the file “project ...

  3. css中背景 字体 文体属性练习

    @charset "utf-8"; body{ background-color:#332244;/*//背景颜色*/ background-image:("../kk. ...

  4. javascript数据结构与算法 零(前记+前言)

    前记 这本书Data Structure and Algorithm with Javascript 我将其翻译成<< javascript 数据结构和算法>> 为什么这么翻译 ...

  5. htmlunit 模拟登录 数字验证码

    使用htmlunit的好处有两点,相比httpclient,htmlunit是对浏览器的模拟,比如你定位一个按钮,就可以执行click()方法,此外不需要象在httpclient中一样编写复杂的代码, ...

  6. linux下jar包的生存与开机自动启动

    前言 作为一名Android开发,有的时候也是要做做其他工作的,前段时间写了一个很简单的java消息转发程序,因为和三方厂商合作,对方只提供了java的sdk,然而我们这边都是清一色的C#后台开发人员 ...

  7. Word 2010/2013 菜单栏添加 MathType 菜单

    一般对于 office 的高级版本,比如 word 2010/2013,在手动安装 mathtype 之后,并不会再 word 的菜单栏,创建 mathtype 按钮,此时需要按照如下步骤,手动导入 ...

  8. Cocos2d-x 3.2 Lua演示样本 ActionTest(操作测试)

    Cocos2d-x 3.2 Lua演示样本 ActionTest(操作测试) 2014年博文大赛,请投上您宝贵的一票:http://vote.blog.csdn.net/Article/Details ...

  9. Ubuntu不输入密码执行sudo命令方法介绍

    作为ubuntu等桌面系统,默认登录的帐号是没有root权限的,为了提升权限来执行任务,我们一般用到sudo+命令来执行,但是不难发现我们一般都要输入密码.那么有没有什么方法可以让我们执行sudo的时 ...

  10. C# WPF 左侧菜单右侧内容布局效果实现

    原文:C# WPF 左侧菜单右侧内容布局效果实现 我们要做的效果是这样的,左侧是可折叠的菜单栏,右侧是内容区域,点击左侧的菜单项右侧内容区域则相应地切换. wpf实现的话,我的办法是用一个tabcon ...