LJSON:全能、高性能、真流式的 C 语言 JSON 引擎和数值引擎
项目地址
- github: https://github.com/lengjingzju/json
- gitee: https://gitee.com/lengjingzju/json
系列文章
1. 项目定位与设计初衷
LJSON 诞生于 2019 年 10 月,比 yyjson 早一年,初衷并非单纯追求基准测试中的极限解析速度,而是要在 性能、内存占用、可编辑性、流式能力、可维护性 之间找到最佳平衡。
它目前状态是:
- 能在多数场景下媲美甚至超越 yyjson 的性能
- 提供 yyjson 架构无法小改实现的真流式处理能力
- 保持代码可读性和可扩展性,适合长期演进
2. 核心能力
双重核心
全能 JSON 处理
- 完整支持 JSON5 规范(十六进制、注释、尾逗号、单引号等)
- DOM / SAX 双解析模式
- 真流式文件(边读边解析,边写边打印)
高性能数值转换引擎
- 独创 ldouble 算法
- 浮点 ↔ 字符串转换性能远超标准库和主流算法(sprintf、grisu2、dragonbox)
- 精度是16位,在边界处理时追求最短而不是偶数(标准是16位或17位,取决于二进制指数转换为十进制指数的乘子的分辨率)
3. 架构与模式设计
LJSON 提供 7 种解析模式 与 4 种打印模式,覆盖从内存到文件、从 DOM 到 SAX 的全场景需求:
解析模式
- DOM 经典模式(malloc/free)
- DOM 内存池模式
- DOM 复用模式(可编辑,字符串原地复用)
- DOM 文件流模式(真流式)
- DOM 文件流内存池模式(真流式 + 内存池)
- SAX 模式(回调处理)
- SAX 文件流模式(真流式 + 回调)
打印模式
- DOM → 字符串
- DOM → 文件(真流式)
- SAX → 字符串
- SAX → 文件(真流式)
真流式:边读文件边解析,边打印边写文件,无需完整读入或生成中间大缓冲,内存占用可降至常数级,即使处理 1GB JSON 文件也仅需 KB 级内存。
yyjson ≈ LJSON 的 DOM 复用模式 + 激进只读优化
4. 与 yyjson 的设计差异
核心差异包括:
| 维度 |
LJSON |
yyjson |
| 模式设计 |
多模式(含真流式、可编辑复用) |
单一复用模式(只读优化) |
| 可编辑性 |
复用模式可编辑 |
只读与可编辑严格分离(val / mut_val) |
| 字符串存储 |
标准 C 字符串(尾 1 个 \0) |
非标准尾 4 个 \0(减少边界判断) |
| 对象存储 |
key 与 value 一起存储 |
key 与 value 分开存储 |
| 内存策略 |
按需分配,尽量避免浪费 |
大块预分配,冗余度高 |
| 访问加速 |
json_items_t 缓存,数组 O(1)、对象 O(logN) |
未知是否有缓存,可能 O(N)/O(2N) |
| 优化手法 |
内联、分支预测、查表、缓存元信息;保持可读性 |
同类优化 + 大量宏循环展开,代码晦涩 |
| 流式处理 |
真流式解析/打印 |
不支持 |
5. 性能来源与权衡
优化技术对比
两者都使用:
- 算法优化
- 内存池(块)优化
- 内联优化(inline)
- 分支预测优化(likely/unlikely)
- Cache 命中优化
- 查表优化
- 拷贝优化
- 信息记录优化(缓存字符串长度等)
差异:
- yyjson:大量宏进行循环手动展开,代码紧凑但晦涩,性能更激进;使用非标准特性(尾后 4 个
\0、非对齐访问等)。
- LJSON:追求易维护、可扩展、低占用、高性能的工程化平衡。保持可读性,宏进行手动循环展开。
性能对比
yyjson 相当于只实现了 LJSON 的DOM复用模式,并且转换可编辑模式时对象和字符串都存储在内存块里,和LJSON的内存池原理一致。
- yyjson 只读复用模式 性能略高于 LJSON,来源于:
- 模式单一
- 激进资源使用(预分配冗余内存)
- 非标准特性(尾后 4 个
\0、非对齐访问)
- 宏循环展开
- yyjson 只读模式 性能和 LJSON 很接近,两者互有胜负
- yyjson 可编辑模式 性能可能大幅落后于 LJSON(大文件解析 yyjson 比 LJSON 内存占用多约一倍,速度慢约一倍)
注:如果 LJSON 放弃流式、标准字符串、可编辑性,采用更晦涩代码,可达到 yyjson 只读复用模式 性能,但那将不再是 LJSON 的设计哲学。
6. 优化机制亮点
- 零堆分配:首次分配后循环复用,避免频繁 malloc/free。
- 原地字符串复用:直接引用输入缓冲区,减少拷贝与内存带宽消耗。
- json_items 加速器:数组 O(1) 访问,对象 O(logN) 查找。
- 真流式管线:解析与 I/O 并行,消除峰值内存与额外拷贝。
- ldouble 数值引擎:浮点转字符串性能领先 sprintf 10~70 倍。
7. 多维度对比(可编辑模式)
| 对比维度 |
LJSON |
yyjson |
RapidJSON |
cJSON |
| 解析性能 |
高(547ms,可编辑复用) |
中高(1011ms,可编辑) |
较高 |
中等 |
| 打印性能 |
高(真流式+ldouble) |
高(全缓冲) |
中等 |
低 |
| 内存占用 |
可常数级(流式) |
较高 |
较高 |
较高 |
| CPU 特性依赖 |
低 |
低 |
中 |
低 |
| 可扩展性 |
高 |
低 |
中 |
高 |
| 代码可读性 |
高 |
极低 |
中 |
高 |
| 关键机制 |
零堆分配、内存池复用、原地字符串、json_items、ldouble |
原地复用、内存块、非标准字符串尾、循环展开 |
模板+分配器 |
malloc/free |
8. 适用场景
- 大文件处理:GB 级 JSON 格式化/压缩,内存占用可常数级。
- 嵌入式系统:低内存、高性能需求。
- 高频读写:实时数据流解析与生成。
- 跨平台开发:Linux / Windows / 嵌入式 RTOS。
9. 设计哲学总结
yyjson:以极致性能为第一目标,通过激进优化手段和单一模式深耕,适合极限只读解析场景。
LJSON:追求易维护、可扩展、低占用、高性能的工程化平衡,适合通用、可编辑、流式处理、大文件低内存占用的场景。纯 C 实现,零第三方依赖,跨平台编译,接口设计参考 cJSON,逻辑清晰,编译速度快,易于二次开发。
LJSON 的价值不仅在于跑分,更在于它能在真实工程环境中,以极低的内存占用和高性能,稳定支撑多样化的 JSON 处理需求。
LJSON的ldouble算法性能测试
ldouble算法底数只有16位十进制数字精度(标准是16或17位)并且舍入是舍入到最短(标准是等距取偶)。
- Linux: Ubuntu 20.04 (VMWare), Intel i7-1260P, gcc9.4.0
- Windows: Windows 11, Intel i7-1260P, MSVC2023
- dtoa测试方法:
./jnum_test <num> 10000000 (1000万次转换)
- atod测试方法:
./jnum_test a <num> 10000000 (1000万次转换)
以下表格由Deepseek根据测试结果整理

浮点数转字符串dtoa
1. 普通短小数(小数部分 ≤ 8位)
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
1286.33 |
- |
| ldouble |
83.33 |
1544.44 |
| grisu2 |
74.11 |
1737.11 |
| dragonbox |
86.00 |
1499.33 |
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
2093.11 |
- |
| ldouble |
104.78 |
1999.89 |
| grisu2 |
154.44 |
1355.56 |
| dragonbox |
131.11 |
1594.22 |
2. 普通长小数(小数部分 > 8位)
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
1420.25 |
- |
| ldouble |
105.75 |
1342.50 |
| grisu2 |
158.50 |
896.25 |
| dragonbox |
112.25 |
1265.00 |
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
2300.00 |
- |
| ldouble |
120.00 |
1916.67 |
| grisu2 |
250.00 |
920.00 |
| dragonbox |
150.00 |
1533.33 |
3. 整数(1e^x,指数 0 ≤ x < 20)
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
1500.94 |
- |
| ldouble |
37.56 |
3996.81 |
| grisu2 |
143.19 |
1048.81 |
| dragonbox |
43.81 |
3425.94 |
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
1600.00 |
- |
| ldouble |
55.00 |
2909.09 |
| grisu2 |
150.00 |
1066.67 |
| dragonbox |
75.00 |
2133.33 |
4. 大值科学计数法(x.xxx e100)
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
3400.00 |
- |
| ldouble |
85.00 |
4000.00 |
| grisu2 |
150.00 |
2266.67 |
| dragonbox |
110.00 |
3090.91 |
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
13500.00 |
- |
| ldouble |
400.00 |
3375.00 |
| grisu2 |
900.00 |
1500.00 |
| dragonbox |
500.00 |
2700.00 |
5. 小值科学计数法(x.xxx e-100)
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
2100.00 |
- |
| ldouble |
90.00 |
2333.33 |
| grisu2 |
160.00 |
1312.50 |
| dragonbox |
110.00 |
1909.09 |
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
2900.00 |
- |
| ldouble |
200.00 |
1450.00 |
| grisu2 |
800.00 |
362.50 |
| dragonbox |
500.00 |
580.00 |
6. 极大值科学计数法(指数 ≥ 300)
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
3600.00 |
- |
| ldouble |
80.00 |
4500.00 |
| grisu2 |
85.00 |
4235.29 |
| dragonbox |
90.00 |
4000.00 |
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
71000.00 |
- |
| ldouble |
350.00 |
20285.71 |
| grisu2 |
550.00 |
12909.09 |
| dragonbox |
400.00 |
17750.00 |
7. 极小值科学计数法(指数 ≤ -300 且 > -308)
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
2600.00 |
- |
| ldouble |
75.00 |
3466.67 |
| grisu2 |
90.00 |
2888.89 |
| dragonbox |
85.00 |
3058.82 |
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
6000.00 |
- |
| ldouble |
300.00 |
2000.00 |
| grisu2 |
550.00 |
1090.91 |
| dragonbox |
400.00 |
1500.00 |
8. 非规格数(指数 ≤ -308)
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
2700.00 |
- |
| ldouble |
65.00 |
4153.85 |
| grisu2 |
110.00 |
2454.55 |
| dragonbox |
60.00 |
4500.00 |
| Algorithm |
Average Time (ms) |
Average Percentage (%) |
| sprintf |
6000.00 |
- |
| ldouble |
280.00 |
2142.86 |
| grisu2 |
500.00 |
1200.00 |
| dragonbox |
350.00 |
1714.29 |
总结说明
- 以上平均值基于每个维度中的多个输入计算得出,但由于输入数量众多,平均值可能略有偏差,但总体趋势一致。
- Linux系统下,ldouble、grisu2和dragonbox算法均比sprintf快得多,百分比提升通常在1500%以上。
- Windows系统下,算法性能提升同样显著,但绝对时间通常比Linux长,可能与系统差异有关。
字符串转浮点数atod
1. 普通短小数(小数部分 ≤ 8位)
| 系统 |
算法 |
平均时间(ms) |
平均百分比(%) |
| Linux |
strtod |
235.2 |
- |
| Linux |
ldouble |
59.4 |
396.0 |
| Windows |
strtod |
482.8 |
- |
| Windows |
jnum_atod |
60.3 |
801.0 |
2. 普通长小数(小数部分 > 8位)
| 系统 |
算法 |
平均时间(ms) |
平均百分比(%) |
| Linux |
strtod |
419.3 |
- |
| Linux |
ldouble |
105.8 |
396.3 |
| Windows |
strtod |
868.3 |
- |
| Windows |
jnum_atod |
113.8 |
763.0 |
3. 整数(1e^x,0 ≤ x < 20)
| 系统 |
算法 |
平均时间(ms) |
平均百分比(%) |
| Linux |
strtod |
203.1 |
- |
| Linux |
ldouble |
86.5 |
234.8 |
| Windows |
strtod |
348.1 |
- |
| Windows |
jnum_atod |
84.9 |
410.0 |
4. 大值科学计数法(x.xxx e100)
| 系统 |
算法 |
平均时间(ms) |
平均百分比(%) |
| Linux |
strtod |
550.7 |
- |
| Linux |
ldouble |
141.1 |
390.3 |
| Windows |
strtod |
764.5 |
- |
| Windows |
jnum_atod |
140.5 |
544.1 |
5. 小值科学计数法(x.xxx e-100)
| 系统 |
算法 |
平均时间(ms) |
平均百分比(%) |
| Linux |
strtod |
796.3 |
- |
| Linux |
ldouble |
136.9 |
581.7 |
| Windows |
strtod |
1317.7 |
- |
| Windows |
jnum_atod |
141.3 |
932.5 |
6. 极大值科学计数法(指数 ≥ 300)
| 系统 |
算法 |
平均时间(ms) |
平均百分比(%) |
| Linux |
strtod |
624.5 |
- |
| Linux |
ldouble |
107.3 |
582.0 |
| Windows |
strtod |
828.2 |
- |
| Windows |
jnum_atod |
113.0 |
732.9 |
7. 极小值科学计数法(指数 ≤ -300 且 > -308)
| 系统 |
算法 |
平均时间(ms) |
平均百分比(%) |
| Linux |
strtod |
1015.5 |
- |
| Linux |
ldouble |
107.5 |
944.7 |
| Windows |
strtod |
1923.9 |
- |
| Windows |
jnum_atod |
112.9 |
1704.5 |
8. 非规格数(指数 ≤ -308)
| 系统 |
算法 |
平均时间(ms) |
平均百分比(%) |
| Linux |
strtod |
1089.5 |
- |
| Linux |
ldouble |
111.5 |
977.1 |
| Windows |
strtod |
1972.3 |
- |
| Windows |
jnum_atod |
115.4 |
1708.9 |
总结分析
- 性能提升显著: 在所有测试类别中,jnum_atod/ldouble算法相比标准strtod都有显著性能提升,平均提升幅度在234%-1708%之间。
- 系统差异: Windows平台下的strtod实现相比Linux平台通常更慢,这使得jnum_atod在Windows上的相对性能提升更加明显。
- 数值类型影响:
- 对于常规数字(短小数、整数),性能提升约在2-8倍
- 对于科学计数法表示的数字,性能提升更为显著,尤其是极小值和非规格数,提升可达10-17倍
- 最佳表现场景: jnum_atod在处理极小值科学计数法和非规格数时表现最为出色,在Windows平台上性能提升超过17倍。
- 一致性: 尽管绝对时间有差异,但jnum_atod/ldouble在两个平台上都表现出相对一致的性能提升趋势。
所有评论(0)