最近在写 Flutter 应用,发现 Dart 解析比较大的 json 比较慢,会影响 UI 线程,使用 compute 的话,的确不影响 UI 线程了,但是解析起来更慢,想着可以使用 flutter rust bridge
做个 json 解析库,不过我用 rust 写个测试程序,发现解析 json 比 dart 快不了多少,各位有啥好意见没?
各语言测试:
time node index.js 1.37s user 0.19s system 121% cpu 1.282 total time go build && ./main 1.59s user 0.02s system 80% cpu 2.002 total time python3.9 main.py 2.45s user 0.27s system 98% cpu 2.762 total time cargo run --release 2.46s user 0.42s system 97% cpu 2.972 total time dart compile exe main.dart && bin/main 4.62s user 0.45s system 114% cpu 4.415 total
rust code:
use std::fs::File; use std::io::{Read}; use serde_json::Value; fn parse_json(contents: Vec<u8>) { let now = std::time::Instant::now(); let _: Value = serde_json::from_slice(&contents).unwrap(); let elapsed = now.elapsed(); println!("elapsed: {:?}", elapsed); } fn main() { let cOntents= { let mut vec = Vec::new(); // https://github.com/json-iterator/test-data/blob/master/large-file.json File::open("large-file.json").unwrap().read_to_end(&mut vec).unwrap(); vec }; for _ in 0..10 { parse_json(contents.clone()); } }
测试机器:MacBook Pro (16-inch, 2019) 2.3 GHz 八核 Intel Core i9 32 GB 2667 MHz DDR4
不好意思,上面使用time统计时间是不包含程序编译时间的,只是发主题的时候合到一起的。
for 循环打印的结果如下:
NodeJs: 110 ms NodeJs: 107 ms NodeJs: 103 ms NodeJs: 106 ms NodeJs: 128 ms NodeJs: 94 ms NodeJs: 93 ms NodeJs: 92 ms NodeJs: 98 ms NodeJs: 93 ms
Dart: 384 ms Dart: 390 ms Dart: 377 ms Dart: 395 ms Dart: 356 ms Dart: 381 ms Dart: 378 ms Dart: 370 ms Dart: 388 ms 耗时: 379 ms
Python: 241.057 ms Python: 233.375 ms Python: 249.352 ms Python: 244.968 ms Python: 249.487 ms Python: 288.286 ms Python: 263.347 ms Python: 255.939 ms Python: 256.224 ms Python: 252.851 ms
Golang: 171 ms Golang: 162 ms Golang: 160 ms Golang: 161 ms Golang: 161 ms Golang: 160 ms Golang: 165 ms Golang: 157 ms Golang: 161 ms Golang: 158 ms
Rust: 297.331601ms Rust: 268.03852ms Rust: 280.606788ms Rust: 272.826854ms Rust: 293.53427ms Rust: 269.841906ms Rust: 285.450976ms Rust: 288.862249ms Rust: 275.984671ms Rust: 276.715469ms
![]() | 1 MoYi123 2023-07-05 10:28:46 +08:00 为什么要把编译的时间也算进去? |
![]() | 2 codehz 2023-07-05 10:32:43 +08:00 via iPhone ![]() 你可以考虑一下解析 json 的目的,如果可行的话,不需要先变成一个对象,而是直接从流式解析中提取需要的数据 |
![]() | 4 newmlp 2023-07-05 10:40:36 +08:00 你这都把程序启动时间算上了,肯定不准啊,json 解析才能用多少时间估计 1ms 都不到 |
![]() | 5 tool2d 2023-07-05 11:02:21 +08:00 纯好奇,用自己写的库解析了一下主贴里的 24M json ,竟然要 1 秒。 发现 dart 这些库,已经很强了。 |
6 wxf666 2023-07-05 11:03:48 +08:00 你要存啥呢?用个普通数据库也不错呀。。 比如,SQLite 解析 JSON 也挺快: ``` sqlite> .timer on sqlite> SELECT json_array_length(readFile('large-file.json')); 11351 Run Time: real 0.089 user 0.046875 sys 0.046875 ``` |
7 visper 2023-07-05 11:05:57 +08:00 rust 比 python 还慢.谁说 rust 快的 |
![]() | 8 lisongeee 2023-07-05 11:07:15 +08:00 json 解析函数可以改成异步嘛?拆成多个子任务 let btachTaskNum = 0; while(isParsing){ dobtachTask(); btachTaskNum++; if(btachTaskNum%1000==0){ await nextTick() } } |
![]() | 9 tool2d 2023-07-05 11:07:36 +08:00 二楼说的也对,看具体使用目的,如果预处理一次,全部变成二进制数转节点,变成特定格式,载入和查询起来都是很快的。 你要暴力遍历巨量文本的 json ,速度上不去。要动态查询某些节点,速度还是可以提升一下的。 |
![]() | 10 lisongeee 2023-07-05 11:09:19 +08:00 艹,我刚刚我回答不符合题意,当作没看见吧 |
![]() | 11 tool2d 2023-07-05 11:17:24 +08:00 @lisongeee 其实可以优化成异步或者多线程版本,那个 large-file.json 结构和 csv 一样,一行行很有规律。 但是不是所有的 json 都能这样有规律的拆分,层次结构过于复杂,也没办法拆分。 |
12 iOCZ 2023-07-05 11:18:43 +08:00 1 秒都不到怎么能算慢呢 |
13 iOCZ 2023-07-05 11:19:05 +08:00 我们的人生是需要转圈圈的 |
![]() | 14 debuggerx 2023-07-05 11:21:49 +08:00 via Android 数据量大的时候就应该考虑是不是不应该用 json |
![]() | 15 lisongeee 2023-07-05 11:47:04 +08:00 |
![]() | 16 tool2d 2023-07-05 11:51:20 +08:00 |
17 duke807 2023-07-05 11:52:29 +08:00 via Android 不如弃用 json 改用和 json 类似但支持二进制的 msgpack |
![]() | 19 icyalala 2023-07-05 12:25:34 +08:00 你要用 C/C++ 的那些库比如 simdjson ,帖子里的 24M json 也就十几 ms |
20 x77 2023-07-05 12:45:13 +08:00 - 异步解析 - 缓存(避免重复的解析) - 减少 Json 的体积 - 改进设计,不用 Json 存储巨量数据 |
![]() | 21 flyqie 2023-07-05 12:53:26 +08:00 via Android json 真心不适合储存大量的数据。。 大量数据用 binary 比较好办。。 |
![]() | 22 lysS 2023-07-05 13:44:02 +08:00 数据够大,调 ffi 有提升 |
![]() | 23 honhon 2023-07-05 14:22:51 +08:00 数据量大 json 不适合 |
24 mxT52CRuqR6o5 2023-07-05 14:26:26 +08:00 via Android 如果结论没问题的话,应该就是 dartvm 比较菜了 |
![]() | 25 rb6221 2023-07-05 14:27:06 +08:00 如果是一次性,那么就一次解析完了重复使用 如果不是一次性,该考虑换 protobuf ,大数据用 json 本来就是不合理的选择,或者改需求了,在移动端设备上是否需要执行这种复杂逻辑 |
26 mxT52CRuqR6o5 2023-07-05 14:29:33 +08:00 还有一个问题,你说 dart 慢会「影响 UI 线程」,说明你在开发安卓 但 nodejs 等其他语言的测试应该都是直接跑在 mac 上的 这个性能是不太好直接比较的 |
![]() | 27 Kaiv2 2023-07-05 15:28:43 +08:00 Mac Air M1 2020 Rust: elapsed: 149.987542ms elapsed: 107.580541ms elapsed: 105.902334ms elapsed: 105.940375ms elapsed: 105.132084ms elapsed: 105.841583ms elapsed: 105.093ms elapsed: 105.630708ms elapsed: 105.344959ms elapsed: 105.7735ms Java: elapsed: 263ms elapsed: 189ms elapsed: 44ms elapsed: 50ms elapsed: 41ms elapsed: 56ms elapsed: 39ms elapsed: 53ms elapsed: 39ms elapsed: 39ms |
![]() | 30 serco 2023-07-05 15:51:53 +08:00 @simman serde_json 浪费了不少时间在建 struct 上,不知道具体数据类型的话,还不如 json 这种 crate 快。 json parse 这种常规操作各个语言都有优化,差距不会特别大的,而且还要看具体做到了哪一步,是真的 parse 完建立了对应的类型,还是处在类似 pre-parsing 的状态临查询了才真的实例对应的类型。 |
![]() | 31 Kaiv2 2023-07-05 15:52:21 +08:00 @ljsh093 Java: Fastjson 1.2.83 elapsed: 349ms elapsed: 107ms elapsed: 88ms elapsed: 98ms elapsed: 79ms elapsed: 77ms elapsed: 78ms elapsed: 85ms elapsed: 111ms elapsed: 61ms Java: Fastjson2 2.0.34 elapsed: 214ms elapsed: 65ms elapsed: 71ms elapsed: 62ms elapsed: 53ms elapsed: 46ms elapsed: 46ms elapsed: 46ms elapsed: 46ms elapsed: 49ms |
33 smirkcat 2023-07-05 16:41:45 +08:00 用字节的 ast 解析引擎 |
![]() | 34 icyalala 2023-07-05 16:45:43 +08:00 @Kaiv2 再来个 C++ 的,也是 M1 simdjson: 18.44 ms 16.54 ms 16.05 ms 16.77 ms 16.41 ms yyjson: 14.66 ms 14.37 ms 14.00 ms 13.89 ms 13.68 ms rapidjson: 68.54 ms 67.82 ms 67.06 ms 67.04 ms 66.88 ms |
![]() | 35 GiantHard 2023-07-05 17:15:46 +08:00 @serco #30 说得对,crate json 要比 serde_json 快一倍( AMD Ryzen 7 5800U ) ```rs // build with cargo build -r use std::fs::File; use std::io::{Read}; use serde_json::Value; use json; fn parse_json(contents: &Vec<u8>) { let now = std::time::Instant::now(); let _: Value = serde_json::from_slice(&contents).unwrap(); let elapsed = now.elapsed(); println!("serde_json: {:?}", elapsed); } fn parse_json2(contents: &str) { let now = std::time::Instant::now(); let _ = json::parse(&contents).unwrap(); let elapsed = now.elapsed(); println!("json: {:?}", elapsed); } fn main() { let cOntents= { let mut vec = Vec::new(); // https://github.com/json-iterator/test-data/blob/master/large-file.json File::open("large-file.json").unwrap().read_to_end(&mut vec).unwrap(); vec }; for _ in 0..10 { parse_json(&contents); } for _ in 0..10 { // create json str from contents let cOntents= String::from_utf8(contents.clone()).unwrap(); parse_json2(&contents); } } ``` ``` serde_json: 182.566705ms serde_json: 157.330185ms serde_json: 151.551179ms serde_json: 150.997928ms serde_json: 158.290287ms serde_json: 151.983899ms serde_json: 152.493476ms serde_json: 150.337436ms serde_json: 151.174303ms serde_json: 150.424319ms json: 71.080736ms json: 73.125015ms json: 80.531158ms json: 82.744729ms json: 79.73645ms json: 80.040878ms json: 80.30521ms json: 79.455428ms json: 79.702968ms json: 72.22127ms ________________________________________________________ Executed in 2.42 secs fish external usr time 2.36 secs 0.00 micros 2.36 secs sys time 0.06 secs 464.00 micros 0.06 secs ``` 当然,你换个编程语言也可以比 serde_json 更快 ```fs /// build with dotnet build -c Release open System.IO open System.Text.Json open System.Diagnostics let json = File.ReadAllText("large-file.json") for i in 1..10 do let sw = Stopwatch.StartNew() let jsOnDocument= JsonDocument.Parse(json) sw.Stop() // print in ms printfn "Elapsed: %dms" sw.ElapsedMilliseconds printf "Done" ``` ``` Elapsed: 185ms Elapsed: 74ms Elapsed: 73ms Elapsed: 70ms Elapsed: 75ms Elapsed: 74ms Elapsed: 67ms Elapsed: 69ms Elapsed: 73ms Elapsed: 73ms Done ________________________________________________________ Executed in 963.57 millis fish external usr time 881.76 millis 0.00 micros 881.76 millis sys time 86.87 millis 387.00 micros 86.48 millis ``` |
![]() | 36 nuk 2023-07-05 17:32:12 +08:00 可以分块解析,或者自己做个匹配算法,理论上 json 解析只要不分配内存其实是很快的。那如果又要全部解析,又要快,那只能调用别的了,但是几百毫秒的这么做意义不大。 |
37 zibber 2023-07-06 00:07:04 +08:00 用 c++写个 api, 然后直接掉 c++的接口 |
38 kingzeus 2023-07-06 09:51:28 +08:00 1. 用 C++ 2. dart 的话,可以考虑异步 Future/Isolate 3. 预处理,改成更加高效的数据格式 |