013 folly的fbstring


作者Lou Xiao, deepseek创建时间2025-04-02 07:51:06更新时间2025-04-02 07:51:06

1. 简介

fbstring 是 Facebook 开发的一个高性能字符串实现,作为 std::string 的替代品,主要用于解决以下问题:

  • 提高字符串操作的性能
  • 减少内存分配次数
  • 优化小字符串存储
  • 提供更好的并发性能

fbstring 是 Folly (Facebook Open-source Library) 的一部分,被广泛应用于 Facebook 的后端服务中。

2. 核心概念

2.1 设计目标

  • 性能优化:比 std::string 更快的常见操作
  • 内存效率:减少内存分配和碎片
  • 小字符串优化(SSO):高效存储短字符串
  • 兼容性:尽可能保持与 std::string 的接口兼容

2.2 主要特性

  1. 三级存储策略
    - 小字符串(<=23字节):存储在栈上(SSO)
    - 中等字符串(24-255字节):存储在堆上,使用 malloc
    - 大字符串(>255字节):存储在堆上,使用 jemalloc(如果可用)

  2. 引用计数:中等和大字符串使用引用计数实现 COW(Copy-On-Write)

  3. 类型擦除:存储策略对用户透明

3. 实现细节

3.1 内存布局

fbstring 使用 24 字节的结构(在 64 位系统上):

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 中等/大字符串存储结构
2 struct MediumLarge {
3 char* data_; // 字符串数据指针
4 size_t size_; // 当前长度
5 size_t capacity_; // 容量(最高位标记COW)
6 };
7
8 // 小字符串布局
9 [字符数据...][长度字节]

对于小字符串:
- 直接使用内部缓冲区存储字符串内容
- 最高字节用于存储字符串长度和标志位

3.2 小字符串优化(SSO)

fbstring 使用最高位作为标志位:
- 最高位为 0:表示小字符串
- 最高位为 1:表示中/大字符串

小字符串的容量固定为 23 字节(在 64 位系统上),存储格式:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 [字符数据][长度|标志]

3.3 引用计数实现

中/大字符串使用共享的引用计数控制块:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 struct RefCounted {
2 std::atomic<size_t> refCount_;
3 size_t capacity_;
4 char data_[1]; // 柔性数组
5 };

4. 示例代码与注释

4.1 基本使用

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 #include <folly/FBString.h>
2
3 using folly::fbstring;
4
5 int main() {
6 // 创建 fbstring
7 fbstring str1 = "Hello";
8 fbstring str2(" World!");
9
10 // 拼接字符串
11 str1 += str2; // 这里可能触发 COW
12
13 // 获取 C 风格字符串
14 const char* cstr = str1.c_str();
15 printf("%s\n", cstr);
16
17 // 修改字符串
18 str1[0] = 'h'; // 如果共享会触发 COW
19
20 // 子字符串
21 fbstring sub = str1.substr(6, 5);
22
23 return 0;
24 }

4.2 性能敏感场景

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 高效拼接
2 fbstring buildString() {
3 fbstring result;
4 result.reserve(estimated_length); // 关键!
5 result.append(part1)
6 .append(part2);
7 return result;
8 }
9
10 // 批量处理
11 void process(const vector<fbstring>& inputs) {
12 for (const auto& s : inputs) {
13 if (s.isSmall()) { // 小字符串特化处理
14 fastProcessSmall(s.data(), s.size());
15 }
16 }
17 }
1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 高效构建大字符串
2 fbstring buildLargeString() {
3 fbstring result;
4 result.reserve(1024); // 预分配内存
5
6 for (int i = 0; i < 100; ++i) {
7 result.append("some data chunk ");
8 }
9
10 return result; // 移动语义避免复制
11 }

4.3 内存管理

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 控制COW行为
2 void modifySharedString(fbstring& s) {
3 char* data = s.mutable_data(); // 显式分离
4 transformData(data, s.size());
5 }
6
7 // 大字符串释放
8 fbstring large = getLargeString();
9 {
10 fbstring tmp; // 使用作用域控制释放
11 swap(large, tmp); // 立即释放大内存
12 }

4.4 异常安全

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 class StringHolder {
2 fbstring value_;
3 public:
4 void setValue(const fbstring& newVal) {
5 fbstring tmp(newVal); // 先构造临时对象
6 swap(value_, tmp); // 无异常操作
7 }
8 };

5. 最佳实践

5.1 使用场景

  • 需要高性能字符串处理的场景
  • 频繁的字符串拼接和修改
  • 大量小字符串的使用
  • 需要减少内存分配的场景

5.2 性能建议

  1. 预分配内存:对于已知大小的字符串,使用 reserve() 预分配
  2. 避免不必要的复制:使用移动语义传递大型 fbstring
  3. 批量操作:使用 append() 而非多次 +=
  4. 小字符串优化:尽量保持字符串短于 23 字节

5.3 线程安全

  • 只读操作是线程安全的
  • 写操作需要外部同步
  • COW 机制在多线程环境下需要谨慎使用

6. 错误与缺陷

6.1 常见问题

COW 陷阱

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 fbstring a = "data";
2 fbstring b = a; // 共享数据
3 char* p = a.mutable_data(); // 触发 COW 复制
4 // 此时 a 和 b 不再共享数据

线程安全问题
- 多个线程同时修改同一个 fbstring 的副本可能导致数据竞争
- 解决方案:使用外部锁或避免共享

与 std::string 的差异
- 某些边缘情况行为可能不同
- 性能特征不同(特别是对于小字符串)

6.2 已知限制

  • 最大字符串大小受 size_t 限制
  • 在 32 位系统上小字符串优化空间较小
  • 某些编译器优化可能不如 std::string

7. 高级技术

7.1 自定义分配器

fbstring 支持通过模板参数指定分配器:

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 using CustomAllocString = folly::basic_fbstring<
2 char, std::char_traits<char>, CustomAllocator>;

7.2 与 std::string 互操作

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // fbstring 转 std::string
2 fbstring fbstr = "hello";
3 std::string stdstr(fbstr.data(), fbstr.size());
4
5 // std::string 转 fbstring
6 std::string stdstr = "world";
7 fbstring fbstr(stdstr.data(), stdstr.size());

7.3 多线程模式

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 只读共享(安全)
2 const fbstring global = "readonly";
3 void reader() {
4 auto len = global.size(); // 无锁安全
5 }
6
7 // 写入需同步
8 std::mutex m;
9 fbstring shared;
10 void writer() {
11 std::lock_guard<std::mutex> lock(m);
12 shared += "data";
13 }

7.4 性能调优

  1. jemalloc 集成
    - 大字符串默认使用 jemalloc(如果可用)
    - 可通过环境变量控制

  2. SSO 大小调整
    - 通过修改源码可以调整小字符串的阈值
    - 需要重新编译 Folly

  3. 引用计数策略
    - 可通过编译选项调整 COW 行为
    - 在高度并发场景可能需要禁用 COW

8. 总结

fbstring 是 std::string 的高性能替代品,特别适合以下场景:
- 需要处理大量字符串
- 对性能有严格要求
- 需要减少内存分配
- 大量使用小字符串

在使用时需要注意其与 std::string 的行为差异,特别是在多线程环境下。正确使用时,fbstring 可以显著提高应用程序的字符串处理性能。


fbstring 的主要 API 及其示例代码

构造与析构

A1. 默认构造函数

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 创建一个空的fbstring对象
2 folly::fbstring str1;
3 // 输出: ""

A2. 从C字符串构造

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 从C风格字符串构造
2 folly::fbstring str2("Hello, world!");
3 // 输出: "Hello, world!"

A3. 从std::string构造

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 从std::string构造
2 std::string stdStr("Folly");
3 folly::fbstring str3(stdStr);
4 // 输出: "Folly"

A4. 从子串构造

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 从另一个fbstring的子串构造
2 folly::fbstring str4("Programming", 7); // 前7个字符
3 // 输出: "Program"

A5. 填充构造函数

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 // 创建包含10个'A'的字符串
2 folly::fbstring str5(10, 'A');
3 // 输出: "AAAAAAAAAA"

容量操作

A6. size() / length()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello");
2 size_t len = str.size(); // 或 str.length()
3 // len = 5

A7. empty()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str;
2 bool is_empty = str.empty(); // true

A8. capacity()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Test");
2 size_t cap = str.capacity(); // 返回当前分配的存储容量

A9. reserve()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str;
2 str.reserve(100); // 预分配至少100字节的空间

A10. shrink_to_fit()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str(100, 'x');
2 str.resize(10);
3 str.shrink_to_fit(); // 减少容量以匹配大小

元素访问

A11. operator[]

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello");
2 char c = str[1]; // 'e'

A12. at()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("World");
2 try {
3 char c = str.at(10); // 抛出std::out_of_range异常
4 } catch (const std::out_of_range& e) {
5 // 处理异常
6 }

A13. front() / back()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Folly");
2 char first = str.front(); // 'F'
3 char last = str.back(); // 'y'

修改操作

A14. operator+=

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello");
2 str += ", world!"; // "Hello, world!"

A15. append()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello");
2 str.append(" there"); // "Hello there"

A16. push_back()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hell");
2 str.push_back('o'); // "Hello"

A17. insert()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Helloworld");
2 str.insert(5, ", "); // "Hello, world"

A18. erase()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello, world!");
2 str.erase(5, 2); // 从位置5删除2个字符 -> "Hello world!"

A19. replace()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello, world!");
2 str.replace(7, 5, "folly"); // "Hello, folly!"

A20. clear()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Content");
2 str.clear(); // 清空字符串

A21. resize()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello");
2 str.resize(3); // "Hel"
3 str.resize(5, '!'); // "Hel!!"

字符串操作

A22. c_str() / data()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello");
2 const char* cstr = str.c_str(); // 获取C风格字符串
3 const char* data = str.data(); // 同上

A23. substr()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello, world!");
2 folly::fbstring sub = str.substr(7, 5); // "world"

A24. copy()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello");
2 char buffer[10];
3 size_t copied = str.copy(buffer, 3, 1); // 从位置1复制3个字符到buffer
4 buffer[copied] = '\0'; // "ell"

A25. find()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello, world!");
2 size_t pos = str.find("world"); // 7

A26. rfind()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello, world, hello!");
2 size_t pos = str.rfind("hello"); // 14

A27. find_first_of()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello");
2 size_t pos = str.find_first_of("aeiou"); // 1 ('e')

A28. find_first_not_of()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str(" Hello");
2 size_t pos = str.find_first_not_of(" "); // 3 ('H')

A29. find_last_of()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello");
2 size_t pos = str.find_last_of("aeiou"); // 4 ('o')

A30. find_last_not_of()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello ");
2 size_t pos = str.find_last_not_of(" "); // 4 ('o')

比较操作

A31. compare()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str1("apple");
2 folly::fbstring str2("banana");
3 int result = str1.compare(str2); // 负数,因为"apple" < "banana"

A32. operator==, !=, <, <=, >, >=

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str1("hello");
2 folly::fbstring str2("world");
3 if (str1 < str2) { /* "hello" < "world" */ }

fbstring特有功能

A33. isSmall()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("short");
2 bool small = str.isSmall(); // 检查是否使用小字符串优化

A34. mallocSize()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("a longer string that probably isn't small");
2 size_t mem = str.mallocSize(); // 返回分配的内存大小

A35. swap()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str1("hello");
2 folly::fbstring str2("world");
3 str1.swap(str2); // str1 = "world", str2 = "hello"

A36. operator=

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str1;
2 folly::fbstring str2("hello");
3 str1 = str2; // 赋值操作

A37. move构造函数

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str1("hello");
2 folly::fbstring str2(std::move(str1)); // 移动构造,str1现在为空

IO操作

A38. operator<<

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello, folly!");
2 std::cout << str << std::endl;

A39. operator>>

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str;
2 std::cin >> str; // 从标准输入读取到fbstring

数值转换

A40. toStdString()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring fbstr("Hello");
2 std::string stdstr = fbstr.toStdString(); // 转换为std::string

A41. stoi(), stol(), stoul(), etc.

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring numStr("1234");
2 int num = folly::to<int>(numStr); // 1234

其他操作

A42. starts_with() (C++20风格)

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("Hello, world!");
2 bool starts = str.starts_with("Hello"); // true

A43. ends_with() (C++20风格)

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("file.txt");
2 bool ends = str.ends_with(".txt"); // true

A44. replaceAll()

1.双击鼠标左键复制此行;2.单击复制所有代码。
                                
                                    
1 folly::fbstring str("aabbcc");
2 folly::replaceAll(str, "bb", "xx"); // "aaxxcc"
文章目录