013 folly的fbstring
1. 简介
fbstring 是 Facebook 开发的一个高性能字符串实现,作为 std::string 的替代品,主要用于解决以下问题:
- 提高字符串操作的性能
- 减少内存分配次数
- 优化小字符串存储
- 提供更好的并发性能
fbstring 是 Folly (Facebook Open-source Library) 的一部分,被广泛应用于 Facebook 的后端服务中。
2. 核心概念
2.1 设计目标
- 性能优化:比 std::string 更快的常见操作
- 内存效率:减少内存分配和碎片
- 小字符串优化(SSO):高效存储短字符串
- 兼容性:尽可能保持与 std::string 的接口兼容
2.2 主要特性
三级存储策略:
- 小字符串(<=23字节):存储在栈上(SSO)
- 中等字符串(24-255字节):存储在堆上,使用 malloc
- 大字符串(>255字节):存储在堆上,使用 jemalloc(如果可用)引用计数:中等和大字符串使用引用计数实现 COW(Copy-On-Write)
类型擦除:存储策略对用户透明
3. 实现细节
3.1 内存布局
fbstring 使用 24 字节的结构(在 64 位系统上):
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
[字符数据][长度|标志]
3.3 引用计数实现
中/大字符串使用共享的引用计数控制块:
1
struct RefCounted {
2
std::atomic<size_t> refCount_;
3
size_t capacity_;
4
char data_[1]; // 柔性数组
5
};
4. 示例代码与注释
4.1 基本使用
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
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
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
// 控制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
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 性能建议
- 预分配内存:对于已知大小的字符串,使用
reserve()
预分配 - 避免不必要的复制:使用移动语义传递大型 fbstring
- 批量操作:使用
append()
而非多次+=
- 小字符串优化:尽量保持字符串短于 23 字节
5.3 线程安全
- 只读操作是线程安全的
- 写操作需要外部同步
- COW 机制在多线程环境下需要谨慎使用
6. 错误与缺陷
6.1 常见问题
COW 陷阱:
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
using CustomAllocString = folly::basic_fbstring<
2
char, std::char_traits<char>, CustomAllocator>;
7.2 与 std::string 互操作
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
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 性能调优
jemalloc 集成:
- 大字符串默认使用 jemalloc(如果可用)
- 可通过环境变量控制SSO 大小调整:
- 通过修改源码可以调整小字符串的阈值
- 需要重新编译 Folly引用计数策略:
- 可通过编译选项调整 COW 行为
- 在高度并发场景可能需要禁用 COW
8. 总结
fbstring 是 std::string 的高性能替代品,特别适合以下场景:
- 需要处理大量字符串
- 对性能有严格要求
- 需要减少内存分配
- 大量使用小字符串
在使用时需要注意其与 std::string 的行为差异,特别是在多线程环境下。正确使用时,fbstring 可以显著提高应用程序的字符串处理性能。
fbstring
的主要 API
及其示例代码
构造与析构
A1. 默认构造函数
1
// 创建一个空的fbstring对象
2
folly::fbstring str1;
3
// 输出: ""
A2. 从C字符串构造
1
// 从C风格字符串构造
2
folly::fbstring str2("Hello, world!");
3
// 输出: "Hello, world!"
A3. 从std::string构造
1
// 从std::string构造
2
std::string stdStr("Folly");
3
folly::fbstring str3(stdStr);
4
// 输出: "Folly"
A4. 从子串构造
1
// 从另一个fbstring的子串构造
2
folly::fbstring str4("Programming", 7); // 前7个字符
3
// 输出: "Program"
A5. 填充构造函数
1
// 创建包含10个'A'的字符串
2
folly::fbstring str5(10, 'A');
3
// 输出: "AAAAAAAAAA"
容量操作
A6. size() / length()
1
folly::fbstring str("Hello");
2
size_t len = str.size(); // 或 str.length()
3
// len = 5
A7. empty()
1
folly::fbstring str;
2
bool is_empty = str.empty(); // true
A8. capacity()
1
folly::fbstring str("Test");
2
size_t cap = str.capacity(); // 返回当前分配的存储容量
A9. reserve()
1
folly::fbstring str;
2
str.reserve(100); // 预分配至少100字节的空间
A10. shrink_to_fit()
1
folly::fbstring str(100, 'x');
2
str.resize(10);
3
str.shrink_to_fit(); // 减少容量以匹配大小
元素访问
A11. operator[]
1
folly::fbstring str("Hello");
2
char c = str[1]; // 'e'
A12. at()
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
folly::fbstring str("Folly");
2
char first = str.front(); // 'F'
3
char last = str.back(); // 'y'
修改操作
A14. operator+=
1
folly::fbstring str("Hello");
2
str += ", world!"; // "Hello, world!"
A15. append()
1
folly::fbstring str("Hello");
2
str.append(" there"); // "Hello there"
A16. push_back()
1
folly::fbstring str("Hell");
2
str.push_back('o'); // "Hello"
A17. insert()
1
folly::fbstring str("Helloworld");
2
str.insert(5, ", "); // "Hello, world"
A18. erase()
1
folly::fbstring str("Hello, world!");
2
str.erase(5, 2); // 从位置5删除2个字符 -> "Hello world!"
A19. replace()
1
folly::fbstring str("Hello, world!");
2
str.replace(7, 5, "folly"); // "Hello, folly!"
A20. clear()
1
folly::fbstring str("Content");
2
str.clear(); // 清空字符串
A21. resize()
1
folly::fbstring str("Hello");
2
str.resize(3); // "Hel"
3
str.resize(5, '!'); // "Hel!!"
字符串操作
A22. c_str() / data()
1
folly::fbstring str("Hello");
2
const char* cstr = str.c_str(); // 获取C风格字符串
3
const char* data = str.data(); // 同上
A23. substr()
1
folly::fbstring str("Hello, world!");
2
folly::fbstring sub = str.substr(7, 5); // "world"
A24. copy()
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
folly::fbstring str("Hello, world!");
2
size_t pos = str.find("world"); // 7
A26. rfind()
1
folly::fbstring str("Hello, world, hello!");
2
size_t pos = str.rfind("hello"); // 14
A27. find_first_of()
1
folly::fbstring str("Hello");
2
size_t pos = str.find_first_of("aeiou"); // 1 ('e')
A28. find_first_not_of()
1
folly::fbstring str(" Hello");
2
size_t pos = str.find_first_not_of(" "); // 3 ('H')
A29. find_last_of()
1
folly::fbstring str("Hello");
2
size_t pos = str.find_last_of("aeiou"); // 4 ('o')
A30. find_last_not_of()
1
folly::fbstring str("Hello ");
2
size_t pos = str.find_last_not_of(" "); // 4 ('o')
比较操作
A31. compare()
1
folly::fbstring str1("apple");
2
folly::fbstring str2("banana");
3
int result = str1.compare(str2); // 负数,因为"apple" < "banana"
A32. operator==, !=, <, <=, >, >=
1
folly::fbstring str1("hello");
2
folly::fbstring str2("world");
3
if (str1 < str2) { /* "hello" < "world" */ }
fbstring特有功能
A33. isSmall()
1
folly::fbstring str("short");
2
bool small = str.isSmall(); // 检查是否使用小字符串优化
A34. mallocSize()
1
folly::fbstring str("a longer string that probably isn't small");
2
size_t mem = str.mallocSize(); // 返回分配的内存大小
A35. swap()
1
folly::fbstring str1("hello");
2
folly::fbstring str2("world");
3
str1.swap(str2); // str1 = "world", str2 = "hello"
A36. operator=
1
folly::fbstring str1;
2
folly::fbstring str2("hello");
3
str1 = str2; // 赋值操作
A37. move构造函数
1
folly::fbstring str1("hello");
2
folly::fbstring str2(std::move(str1)); // 移动构造,str1现在为空
IO操作
A38. operator<<
1
folly::fbstring str("Hello, folly!");
2
std::cout << str << std::endl;
A39. operator>>
1
folly::fbstring str;
2
std::cin >> str; // 从标准输入读取到fbstring
数值转换
A40. toStdString()
1
folly::fbstring fbstr("Hello");
2
std::string stdstr = fbstr.toStdString(); // 转换为std::string
A41. stoi(), stol(), stoul(), etc.
1
folly::fbstring numStr("1234");
2
int num = folly::to<int>(numStr); // 1234
其他操作
A42. starts_with() (C++20风格)
1
folly::fbstring str("Hello, world!");
2
bool starts = str.starts_with("Hello"); // true
A43. ends_with() (C++20风格)
1
folly::fbstring str("file.txt");
2
bool ends = str.ends_with(".txt"); // true
A44. replaceAll()
1
folly::fbstring str("aabbcc");
2
folly::replaceAll(str, "bb", "xx"); // "aaxxcc"